// Copied from crypto/x509/pkcs8.go and crypto/x509/x509.go // Parses X25519 whereas built-in only parses Ed25519 (which are mostly identical!) package kit25519 import ( "crypto/ed25519" "crypto/x509/pkix" "encoding/asn1" "errors" "fmt" "github.com/lestrrat-go/jwx/x25519" "golang.org/x/crypto/cryptobyte" ) var ( oidPublicKeyX25519 = asn1.ObjectIdentifier{1, 3, 101, 110} oidPublicKeyEd25519 = asn1.ObjectIdentifier{1, 3, 101, 112} ) // pkixPublicKey reflects a PKIX public key structure. See SubjectPublicKeyInfo // in RFC 3280. type pkixPublicKey struct { Algo pkix.AlgorithmIdentifier BitString asn1.BitString } type publicKeyInfo struct { Raw asn1.RawContent Algorithm pkix.AlgorithmIdentifier PublicKey asn1.BitString } // pkcs8 reflects an ASN.1, PKCS #8 PrivateKey. See // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-8/pkcs-8v1_2.asn // and RFC 5208. type pkcs8 struct { Version int Algo pkix.AlgorithmIdentifier PrivateKey []byte // optional attributes omitted. } // ParsePKIXPublicKey parses a public key in PKIX, ASN.1 DER form. // The encoded public key is a SubjectPublicKeyInfo structure // (see RFC 5280, Section 4.1). // // It returns a *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey, or // ed25519.PublicKey. More types might be supported in the future. // // This kind of key is commonly encoded in PEM blocks of type "PUBLIC KEY". func ParsePKIXPublicKey(derBytes []byte) (pub any, err error) { var pki publicKeyInfo if rest, err := asn1.Unmarshal(derBytes, &pki); err != nil { return nil, err } else if len(rest) != 0 { return nil, errors.New("x509: trailing data after ASN.1 of public-key") } if oidPublicKeyEd25519.Equal(pki.Algorithm.Algorithm) { // RFC 8410, Section 3 // > For all of the OIDs, the parameters MUST be absent. if len(pki.Algorithm.Parameters.FullBytes) != 0 { return nil, errors.New("x509: Ed25519 key encoded with illegal parameters") } der := cryptobyte.String(pki.PublicKey.RightAlign()) if len(der) != ed25519.PublicKeySize { return nil, errors.New("x509: wrong Ed25519 public key size") } return ed25519.PublicKey(der), nil } else if oidPublicKeyX25519.Equal(pki.Algorithm.Algorithm) { // RFC 8410, Section 3 // > For all of the OIDs, the parameters MUST be absent. if len(pki.Algorithm.Parameters.FullBytes) != 0 { return nil, errors.New("x509: X25519 key encoded with illegal parameters") } der := cryptobyte.String(pki.PublicKey.RightAlign()) if len(der) != x25519.PublicKeySize { return nil, errors.New("x509: wrong X25519 public key size") } return x25519.PublicKey(der), nil } return nil, errors.New("unsupported format") } // ParsePKCS8PrivateKey parses an unencrypted private key in PKCS #8, ASN.1 DER form. // // It returns a *rsa.PrivateKey, a *ecdsa.PrivateKey, or a ed25519.PrivateKey. // More types might be supported in the future. // // This kind of key is commonly encoded in PEM blocks of type "PRIVATE KEY". func ParsePKCS8PrivateKey(der []byte) (key any, err error) { var privKey pkcs8 _, err = asn1.Unmarshal(der, &privKey) if err != nil { return nil, err } switch { // New implementation from lestrrat-go/jwx/x25519 case privKey.Algo.Algorithm.Equal(oidPublicKeyX25519): var curvePrivateKey []byte if _, err := asn1.Unmarshal(privKey.PrivateKey, &curvePrivateKey); err != nil { return nil, fmt.Errorf("x509: invalid X25519 private key: %v", err) } return x25519.NewKeyFromSeed(curvePrivateKey) // Existing implementation from Golang case privKey.Algo.Algorithm.Equal(oidPublicKeyEd25519): if l := len(privKey.Algo.Parameters.FullBytes); l != 0 { return nil, errors.New("x509: invalid Ed25519 private key parameters") } var curvePrivateKey []byte if _, err := asn1.Unmarshal(privKey.PrivateKey, &curvePrivateKey); err != nil { return nil, fmt.Errorf("x509: invalid Ed25519 private key: %v", err) } if l := len(curvePrivateKey); l != ed25519.SeedSize { return nil, fmt.Errorf("x509: invalid Ed25519 private key length: %d", l) } return ed25519.NewKeyFromSeed(curvePrivateKey), nil default: return nil, fmt.Errorf("x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm) } } // MarshalPKCS8PrivateKey converts a private key to PKCS #8, ASN.1 DER form. // // The following key types are currently supported: *rsa.PrivateKey, *ecdsa.PrivateKey // and ed25519.PrivateKey. Unsupported key types result in an error. // // This kind of key is commonly encoded in PEM blocks of type "PRIVATE KEY". func MarshalPKCS8PrivateKey(key any) ([]byte, error) { var privKey pkcs8 switch k := key.(type) { // New implementation from lestrrat-go/jwx/x25519 case x25519.PrivateKey: privKey.Algo = pkix.AlgorithmIdentifier{ Algorithm: oidPublicKeyX25519, } curvePrivateKey, err := asn1.Marshal(k.Seed()) if err != nil { return nil, fmt.Errorf("x509: failed to marshal private key: %v", err) } privKey.PrivateKey = curvePrivateKey // Existing implementation from Golang case ed25519.PrivateKey: privKey.Algo = pkix.AlgorithmIdentifier{ Algorithm: oidPublicKeyEd25519, } curvePrivateKey, err := asn1.Marshal(k.Seed()) if err != nil { return nil, fmt.Errorf("x509: failed to marshal private key: %v", err) } privKey.PrivateKey = curvePrivateKey default: return nil, fmt.Errorf("x509: unknown key type while marshaling PKCS#8: %T", key) } return asn1.Marshal(privKey) } // MarshalPKIXPublicKey converts a public key to PKIX, ASN.1 DER form. // The encoded public key is a SubjectPublicKeyInfo structure // (see RFC 5280, Section 4.1). // // The following key types are currently supported: *rsa.PublicKey, *ecdsa.PublicKey // and ed25519.PublicKey. Unsupported key types result in an error. // // This kind of key is commonly encoded in PEM blocks of type "PUBLIC KEY". func MarshalPKIXPublicKey(pub any) ([]byte, error) { var publicKeyBytes []byte var publicKeyAlgorithm pkix.AlgorithmIdentifier var err error if publicKeyBytes, publicKeyAlgorithm, err = marshalPublicKey(pub); err != nil { return nil, err } pkix := pkixPublicKey{ Algo: publicKeyAlgorithm, BitString: asn1.BitString{ Bytes: publicKeyBytes, BitLength: 8 * len(publicKeyBytes), }, } ret, _ := asn1.Marshal(pkix) return ret, nil } func marshalPublicKey(pub any) (publicKeyBytes []byte, publicKeyAlgorithm pkix.AlgorithmIdentifier, err error) { switch pub := pub.(type) { case ed25519.PublicKey: publicKeyBytes = pub publicKeyAlgorithm.Algorithm = oidPublicKeyEd25519 case x25519.PublicKey: publicKeyBytes = pub publicKeyAlgorithm.Algorithm = oidPublicKeyX25519 default: return nil, pkix.AlgorithmIdentifier{}, fmt.Errorf("x509: unsupported public key type: %T", pub) } return publicKeyBytes, publicKeyAlgorithm, nil }