mirror of
https://github.com/pomerium/pomerium.git
synced 2025-04-29 18:36:30 +02:00
142 lines
3.6 KiB
Go
142 lines
3.6 KiB
Go
package cryptutil
|
|
|
|
import (
|
|
"crypto"
|
|
"crypto/ecdsa"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"encoding/hex"
|
|
"encoding/pem"
|
|
"fmt"
|
|
|
|
"github.com/go-jose/go-jose/v3"
|
|
"github.com/hashicorp/go-multierror"
|
|
)
|
|
|
|
// PrivateJWKFromBytes returns a jose JSON Web _Private_ Key from bytes.
|
|
func PrivateJWKFromBytes(data []byte) (*jose.JSONWebKey, error) {
|
|
jwks, err := loadKeys(data, loadPrivateKey)
|
|
if err != nil {
|
|
return nil, err
|
|
} else if len(jwks) == 0 {
|
|
return nil, fmt.Errorf("invalid pem data")
|
|
}
|
|
return jwks[0], nil
|
|
}
|
|
|
|
// PrivateJWKsFromBytes returns jose JSON Web _Private_ Keys from bytes.
|
|
func PrivateJWKsFromBytes(data []byte) ([]*jose.JSONWebKey, error) {
|
|
return loadKeys(data, loadPrivateKey)
|
|
}
|
|
|
|
// PublicJWKFromBytes returns a jose JSON Web _Public_ Key from bytes.
|
|
func PublicJWKFromBytes(data []byte) (*jose.JSONWebKey, error) {
|
|
jwks, err := loadKeys(data, loadPublicKey)
|
|
if err != nil {
|
|
return nil, err
|
|
} else if len(jwks) == 0 {
|
|
return nil, fmt.Errorf("invalid pem data")
|
|
}
|
|
return jwks[0], nil
|
|
}
|
|
|
|
// PublicJWKsFromBytes returns jose JSON Web _Public_ Keys from bytes.
|
|
func PublicJWKsFromBytes(data []byte) ([]*jose.JSONWebKey, error) {
|
|
return loadKeys(data, loadPublicKey)
|
|
}
|
|
|
|
func loadKeys(data []byte, unmarshal func([]byte) (any, error)) ([]*jose.JSONWebKey, error) {
|
|
var jwks []*jose.JSONWebKey
|
|
for {
|
|
var block *pem.Block
|
|
block, data = pem.Decode(data)
|
|
if block == nil {
|
|
break
|
|
}
|
|
|
|
key, err := unmarshal(block.Bytes)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unmarshal key: %w", err)
|
|
}
|
|
|
|
alg, err := SignatureAlgorithmForKey(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
jwk := &jose.JSONWebKey{Key: key, Use: "sig", Algorithm: string(alg)}
|
|
thumbprint, err := jwk.Thumbprint(crypto.SHA256)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("computing thumbprint: %w", err)
|
|
}
|
|
jwk.KeyID = hex.EncodeToString(thumbprint)
|
|
jwks = append(jwks, jwk)
|
|
}
|
|
return jwks, nil
|
|
}
|
|
|
|
func loadPrivateKey(b []byte) (interface{}, error) {
|
|
var wrappedErr error
|
|
var err error
|
|
var key any
|
|
|
|
if key, err = x509.ParseECPrivateKey(b); err == nil {
|
|
return key, nil
|
|
}
|
|
wrappedErr = multierror.Append(wrappedErr, err)
|
|
|
|
if key, err = x509.ParsePKCS1PrivateKey(b); err == nil {
|
|
return key, nil
|
|
}
|
|
wrappedErr = multierror.Append(wrappedErr, err)
|
|
|
|
if key, err = x509.ParsePKCS8PrivateKey(b); err == nil {
|
|
return key, nil
|
|
}
|
|
wrappedErr = multierror.Append(wrappedErr, err)
|
|
|
|
return nil, fmt.Errorf("couldn't load private key: %w", wrappedErr)
|
|
}
|
|
|
|
// https://github.com/square/go-jose/tree/v2.5.1#supported-key-types
|
|
func loadPublicKey(b []byte) (interface{}, error) {
|
|
var wrappedErr error
|
|
var err error
|
|
var key any
|
|
|
|
if key, err = loadPrivateKey(b); err == nil {
|
|
switch k := key.(type) {
|
|
case *rsa.PrivateKey:
|
|
return k.Public(), nil
|
|
case *ecdsa.PrivateKey:
|
|
return k.Public(), nil
|
|
default:
|
|
return nil, fmt.Errorf("private key is unsupported type")
|
|
}
|
|
}
|
|
wrappedErr = multierror.Append(wrappedErr, err)
|
|
|
|
if key, err = x509.ParsePKIXPublicKey(b); err == nil {
|
|
return key, nil
|
|
}
|
|
wrappedErr = multierror.Append(wrappedErr, err)
|
|
|
|
if key, err = x509.ParseCertificate(b); err == nil {
|
|
return key, nil
|
|
}
|
|
wrappedErr = multierror.Append(wrappedErr, err)
|
|
|
|
return nil, fmt.Errorf("couldn't load public key: %w", wrappedErr)
|
|
}
|
|
|
|
// SignatureAlgorithmForKey returns the signature algorithm for the given key.
|
|
func SignatureAlgorithmForKey(key interface{}) (jose.SignatureAlgorithm, error) {
|
|
switch key.(type) {
|
|
case *ecdsa.PrivateKey, *ecdsa.PublicKey:
|
|
return jose.ES256, nil
|
|
case *rsa.PrivateKey, *rsa.PublicKey:
|
|
return jose.RS256, nil
|
|
default:
|
|
return "", fmt.Errorf("crypto: unsupported key type for signing: %T", key)
|
|
}
|
|
}
|