This specifically doesn't add support for X25519 certificates.
Refactored parsePublicKey not to depend on the public PublicKeyAlgorithm
values, and ParseCertificate/ParseCertificateRequest to ignore keys that
don't have a PublicKeyAlgorithm even if parsePublicKey supports them.
Updates #56088
Change-Id: I2274deadfe9bb592e3547c0d4d48166de1006df0
Reviewed-on: https://go-review.googlesource.com/c/go/+/450815
Reviewed-by: Roland Shoemaker <roland@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Joedian Reid <joedian@golang.org>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
import (
"bytes"
"crypto/dsa"
+ "crypto/ecdh"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
return ext, nil
}
-func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (any, error) {
+func parsePublicKey(keyData *publicKeyInfo) (any, error) {
+ oid := keyData.Algorithm.Algorithm
+ params := keyData.Algorithm.Parameters
der := cryptobyte.String(keyData.PublicKey.RightAlign())
- switch algo {
- case RSA:
+ switch {
+ case oid.Equal(oidPublicKeyRSA):
// RSA public keys must have a NULL in the parameters.
// See RFC 3279, Section 2.3.1.
- if !bytes.Equal(keyData.Algorithm.Parameters.FullBytes, asn1.NullBytes) {
+ if !bytes.Equal(params.FullBytes, asn1.NullBytes) {
return nil, errors.New("x509: RSA key missing NULL parameters")
}
N: p.N,
}
return pub, nil
- case ECDSA:
- paramsDer := cryptobyte.String(keyData.Algorithm.Parameters.FullBytes)
+ case oid.Equal(oidPublicKeyECDSA):
+ paramsDer := cryptobyte.String(params.FullBytes)
namedCurveOID := new(asn1.ObjectIdentifier)
if !paramsDer.ReadASN1ObjectIdentifier(namedCurveOID) {
return nil, errors.New("x509: invalid ECDSA parameters")
Y: y,
}
return pub, nil
- case Ed25519:
+ case oid.Equal(oidPublicKeyEd25519):
// RFC 8410, Section 3
// > For all of the OIDs, the parameters MUST be absent.
- if len(keyData.Algorithm.Parameters.FullBytes) != 0 {
+ if len(params.FullBytes) != 0 {
return nil, errors.New("x509: Ed25519 key encoded with illegal parameters")
}
if len(der) != ed25519.PublicKeySize {
return nil, errors.New("x509: wrong Ed25519 public key size")
}
return ed25519.PublicKey(der), nil
- case DSA:
+ case oid.Equal(oidPublicKeyX25519):
+ // RFC 8410, Section 3
+ // > For all of the OIDs, the parameters MUST be absent.
+ if len(params.FullBytes) != 0 {
+ return nil, errors.New("x509: X25519 key encoded with illegal parameters")
+ }
+ return ecdh.X25519().NewPublicKey(der)
+ case oid.Equal(oidPublicKeyDSA):
y := new(big.Int)
if !der.ReadASN1Integer(y) {
return nil, errors.New("x509: invalid DSA public key")
G: new(big.Int),
},
}
- paramsDer := cryptobyte.String(keyData.Algorithm.Parameters.FullBytes)
+ paramsDer := cryptobyte.String(params.FullBytes)
if !paramsDer.ReadASN1(¶msDer, cryptobyte_asn1.SEQUENCE) ||
!paramsDer.ReadASN1Integer(pub.Parameters.P) ||
!paramsDer.ReadASN1Integer(pub.Parameters.Q) ||
}
return pub, nil
default:
- return nil, nil
+ return nil, errors.New("x509: unknown public key algorithm")
}
}
if !spki.ReadASN1BitString(&spk) {
return nil, errors.New("x509: malformed subjectPublicKey")
}
- cert.PublicKey, err = parsePublicKey(cert.PublicKeyAlgorithm, &publicKeyInfo{
- Algorithm: pkAI,
- PublicKey: spk,
- })
- if err != nil {
- return nil, err
+ if cert.PublicKeyAlgorithm != UnknownPublicKeyAlgorithm {
+ cert.PublicKey, err = parsePublicKey(&publicKeyInfo{
+ Algorithm: pkAI,
+ PublicKey: spk,
+ })
+ if err != nil {
+ return nil, err
+ }
}
if cert.Version > 1 {
package x509
import (
+ "crypto/ecdh"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
// 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.
+// It returns a *rsa.PrivateKey, a *ecdsa.PrivateKey, a ed25519.PrivateKey (not
+// a pointer), or a *ecdh.PublicKey (for X25519). 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) {
}
return ed25519.NewKeyFromSeed(curvePrivateKey), nil
+ case privKey.Algo.Algorithm.Equal(oidPublicKeyX25519):
+ if l := len(privKey.Algo.Parameters.FullBytes); l != 0 {
+ return nil, errors.New("x509: invalid X25519 private key parameters")
+ }
+ var curvePrivateKey []byte
+ if _, err := asn1.Unmarshal(privKey.PrivateKey, &curvePrivateKey); err != nil {
+ return nil, fmt.Errorf("x509: invalid X25519 private key: %v", err)
+ }
+ return ecdh.X25519().NewPrivateKey(curvePrivateKey)
+
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.
+// The following key types are currently supported: *rsa.PrivateKey,
+// *ecdsa.PrivateKey, ed25519.PrivateKey (not a pointer), and *ecdh.PrivateKey
+// (X25519 only). 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) {
}
privKey.PrivateKey = curvePrivateKey
+ case *ecdh.PrivateKey:
+ if k.Curve() != ecdh.X25519() {
+ return nil, errors.New("x509: unknown curve while marshaling to PKCS#8")
+ }
+ privKey.Algo = pkix.AlgorithmIdentifier{
+ Algorithm: oidPublicKeyX25519,
+ }
+ curvePrivateKey, err := asn1.Marshal(k.Bytes())
+ 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)
}
import (
"bytes"
+ "crypto/ecdh"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
// From RFC 8410, Section 7.
var pkcs8Ed25519PrivateKeyHex = `302e020100300506032b657004220420d4ee72dbf913584ad5b6d8f1f769f8ad3afe7c28cbf1d4fbe097a88f44755842`
+// Generated using:
+//
+// openssl genpkey -algorithm x25519
+var pkcs8X25519PrivateKeyHex = `302e020100300506032b656e0422042068ff93a73c5adefd6d498b24e588fd4daa10924d992afed01b43ca5725025a6b`
+
func TestPKCS8(t *testing.T) {
tests := []struct {
name string
keyHex: pkcs8Ed25519PrivateKeyHex,
keyType: reflect.TypeOf(ed25519.PrivateKey{}),
},
+ {
+ name: "X25519 private key",
+ keyHex: pkcs8X25519PrivateKeyHex,
+ keyType: reflect.TypeOf(&ecdh.PrivateKey{}),
+ },
}
for _, test := range tests {
import (
"bytes"
"crypto"
+ "crypto/ecdh"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
BitString asn1.BitString
}
-// 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).
+// 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.
+// It returns a *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey,
+// ed25519.PublicKey (not a pointer), or *ecdh.PublicKey (for X25519).
+// 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) {
} else if len(rest) != 0 {
return nil, errors.New("x509: trailing data after ASN.1 of public-key")
}
- algo := getPublicKeyAlgorithmFromOID(pki.Algorithm.Algorithm)
- if algo == UnknownPublicKeyAlgorithm {
- return nil, errors.New("x509: unknown public key algorithm")
- }
- return parsePublicKey(algo, &pki)
+ return parsePublicKey(&pki)
}
func marshalPublicKey(pub any) (publicKeyBytes []byte, publicKeyAlgorithm pkix.AlgorithmIdentifier, err error) {
case ed25519.PublicKey:
publicKeyBytes = pub
publicKeyAlgorithm.Algorithm = oidPublicKeyEd25519
+ case *ecdh.PublicKey:
+ if pub.Curve() != ecdh.X25519() {
+ return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: unsupported ECDH curve")
+ }
+ publicKeyBytes = pub.Bytes()
+ publicKeyAlgorithm.Algorithm = oidPublicKeyX25519
default:
return nil, pkix.AlgorithmIdentifier{}, fmt.Errorf("x509: unsupported public key type: %T", pub)
}
// 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.
+// The following key types are currently supported: *rsa.PublicKey,
+// *ecdsa.PublicKey, ed25519.PublicKey (not a pointer), and *ecdh.PublicKey
+// (X25519 only). 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) {
const (
UnknownPublicKeyAlgorithm PublicKeyAlgorithm = iota
RSA
- DSA // Unsupported.
+ DSA // Only supported for parsing.
ECDSA
Ed25519
)
return UnknownSignatureAlgorithm
}
-// RFC 3279, 2.3 Public Key Algorithms
-//
-// pkcs-1 OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840)
-// rsadsi(113549) pkcs(1) 1 }
-//
-// rsaEncryption OBJECT IDENTIFIER ::== { pkcs1-1 1 }
-//
-// id-dsa OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840)
-// x9-57(10040) x9cm(4) 1 }
-//
-// RFC 5480, 2.1.1 Unrestricted Algorithm Identifier and Parameters
-//
-// id-ecPublicKey OBJECT IDENTIFIER ::= {
-// iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 }
var (
- oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
- oidPublicKeyDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}
- oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
- oidPublicKeyEd25519 = oidSignatureEd25519
+ // RFC 3279, 2.3 Public Key Algorithms
+ //
+ // pkcs-1 OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840)
+ // rsadsi(113549) pkcs(1) 1 }
+ //
+ // rsaEncryption OBJECT IDENTIFIER ::== { pkcs1-1 1 }
+ //
+ // id-dsa OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840)
+ // x9-57(10040) x9cm(4) 1 }
+ oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
+ oidPublicKeyDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}
+ // RFC 5480, 2.1.1 Unrestricted Algorithm Identifier and Parameters
+ //
+ // id-ecPublicKey OBJECT IDENTIFIER ::= {
+ // iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 }
+ oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
+ // RFC 8410, Section 3
+ //
+ // id-X25519 OBJECT IDENTIFIER ::= { 1 3 101 110 }
+ // id-Ed25519 OBJECT IDENTIFIER ::= { 1 3 101 112 }
+ oidPublicKeyX25519 = asn1.ObjectIdentifier{1, 3, 101, 110}
+ oidPublicKeyEd25519 = asn1.ObjectIdentifier{1, 3, 101, 112}
)
+// getPublicKeyAlgorithmFromOID returns the exposed PublicKeyAlgorithm
+// identifier for public key types supported in certificates and CSRs. Marshal
+// and Parse functions may support a different set of public key types.
func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm {
switch {
case oid.Equal(oidPublicKeyRSA):
if err != nil {
return nil, err
}
+ if getPublicKeyAlgorithmFromOID(publicKeyAlgorithm.Algorithm) == UnknownPublicKeyAlgorithm {
+ return nil, fmt.Errorf("x509: unsupported public key type: %T", pub)
+ }
asn1Issuer, err := subjectBytes(parent)
if err != nil {
}
var err error
- out.PublicKey, err = parsePublicKey(out.PublicKeyAlgorithm, &in.TBSCSR.PublicKey)
- if err != nil {
- return nil, err
+ if out.PublicKeyAlgorithm != UnknownPublicKeyAlgorithm {
+ out.PublicKey, err = parsePublicKey(&in.TBSCSR.PublicKey)
+ if err != nil {
+ return nil, err
+ }
}
var subject pkix.RDNSequence
"bytes"
"crypto"
"crypto/dsa"
+ "crypto/ecdh"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
t.Errorf("Value returned from ParsePKIXPublicKey was not an Ed25519 public key")
}
})
+ t.Run("X25519", func(t *testing.T) {
+ pub := testParsePKIXPublicKey(t, pemX25519Key)
+ k, ok := pub.(*ecdh.PublicKey)
+ if !ok || k.Curve() != ecdh.X25519() {
+ t.Errorf("Value returned from ParsePKIXPublicKey was not an X25519 public key")
+ }
+ })
}
var pemPublicKey = `-----BEGIN PUBLIC KEY-----
-----END PUBLIC KEY-----
`
+// pemX25519Key was generated from pemX25519Key with "openssl pkey -pubout".
+var pemX25519Key = `
+-----BEGIN PUBLIC KEY-----
+MCowBQYDK2VuAyEA5yGXrH/6OzxuWEhEWS01/f4OP+Of3Yrddy6/J1kDTVM=
+-----END PUBLIC KEY-----
+`
+
func TestPKIXMismatchPublicKeyFormat(t *testing.T) {
const pkcs1PublicKey = "308201080282010100817cfed98bcaa2e2a57087451c7674e0c675686dc33ff1268b0c2a6ee0202dec710858ee1c31bdf5e7783582e8ca800be45f3275c6576adc35d98e26e95bb88ca5beb186f853b8745d88bc9102c5f38753bcda519fb05948d5c77ac429255ff8aaf27d9f45d1586e95e2e9ba8a7cb771b8a09dd8c8fed3f933fd9b439bc9f30c475953418ef25f71a2b6496f53d94d39ce850aa0cc75d445b5f5b4f4ee4db78ab197a9a8d8a852f44529a007ac0ac23d895928d60ba538b16b0b087a7f903ed29770e215019b77eaecc360f35f7ab11b6d735978795b2c4a74e5bdea4dc6594cd67ed752a108e666729a753ab36d6c4f606f8760f507e1765be8cd744007e629020103"