Skip to content

Commit 80c5bbc

Browse files
FiloSottilegopherbot
authored andcommitted
crypto/x509: add support for PKCS8/PKIX X25519 key encodings
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 <[email protected]> Run-TryBot: Filippo Valsorda <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Joedian Reid <[email protected]> Auto-Submit: Filippo Valsorda <[email protected]>
1 parent dafc915 commit 80c5bbc

File tree

5 files changed

+135
-55
lines changed

5 files changed

+135
-55
lines changed

src/crypto/x509/parser.go

+29-17
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package x509
77
import (
88
"bytes"
99
"crypto/dsa"
10+
"crypto/ecdh"
1011
"crypto/ecdsa"
1112
"crypto/ed25519"
1213
"crypto/elliptic"
@@ -213,13 +214,15 @@ func parseExtension(der cryptobyte.String) (pkix.Extension, error) {
213214
return ext, nil
214215
}
215216

216-
func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (any, error) {
217+
func parsePublicKey(keyData *publicKeyInfo) (any, error) {
218+
oid := keyData.Algorithm.Algorithm
219+
params := keyData.Algorithm.Parameters
217220
der := cryptobyte.String(keyData.PublicKey.RightAlign())
218-
switch algo {
219-
case RSA:
221+
switch {
222+
case oid.Equal(oidPublicKeyRSA):
220223
// RSA public keys must have a NULL in the parameters.
221224
// See RFC 3279, Section 2.3.1.
222-
if !bytes.Equal(keyData.Algorithm.Parameters.FullBytes, asn1.NullBytes) {
225+
if !bytes.Equal(params.FullBytes, asn1.NullBytes) {
223226
return nil, errors.New("x509: RSA key missing NULL parameters")
224227
}
225228

@@ -246,8 +249,8 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (any, error
246249
N: p.N,
247250
}
248251
return pub, nil
249-
case ECDSA:
250-
paramsDer := cryptobyte.String(keyData.Algorithm.Parameters.FullBytes)
252+
case oid.Equal(oidPublicKeyECDSA):
253+
paramsDer := cryptobyte.String(params.FullBytes)
251254
namedCurveOID := new(asn1.ObjectIdentifier)
252255
if !paramsDer.ReadASN1ObjectIdentifier(namedCurveOID) {
253256
return nil, errors.New("x509: invalid ECDSA parameters")
@@ -266,17 +269,24 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (any, error
266269
Y: y,
267270
}
268271
return pub, nil
269-
case Ed25519:
272+
case oid.Equal(oidPublicKeyEd25519):
270273
// RFC 8410, Section 3
271274
// > For all of the OIDs, the parameters MUST be absent.
272-
if len(keyData.Algorithm.Parameters.FullBytes) != 0 {
275+
if len(params.FullBytes) != 0 {
273276
return nil, errors.New("x509: Ed25519 key encoded with illegal parameters")
274277
}
275278
if len(der) != ed25519.PublicKeySize {
276279
return nil, errors.New("x509: wrong Ed25519 public key size")
277280
}
278281
return ed25519.PublicKey(der), nil
279-
case DSA:
282+
case oid.Equal(oidPublicKeyX25519):
283+
// RFC 8410, Section 3
284+
// > For all of the OIDs, the parameters MUST be absent.
285+
if len(params.FullBytes) != 0 {
286+
return nil, errors.New("x509: X25519 key encoded with illegal parameters")
287+
}
288+
return ecdh.X25519().NewPublicKey(der)
289+
case oid.Equal(oidPublicKeyDSA):
280290
y := new(big.Int)
281291
if !der.ReadASN1Integer(y) {
282292
return nil, errors.New("x509: invalid DSA public key")
@@ -289,7 +299,7 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (any, error
289299
G: new(big.Int),
290300
},
291301
}
292-
paramsDer := cryptobyte.String(keyData.Algorithm.Parameters.FullBytes)
302+
paramsDer := cryptobyte.String(params.FullBytes)
293303
if !paramsDer.ReadASN1(&paramsDer, cryptobyte_asn1.SEQUENCE) ||
294304
!paramsDer.ReadASN1Integer(pub.Parameters.P) ||
295305
!paramsDer.ReadASN1Integer(pub.Parameters.Q) ||
@@ -302,7 +312,7 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (any, error
302312
}
303313
return pub, nil
304314
default:
305-
return nil, nil
315+
return nil, errors.New("x509: unknown public key algorithm")
306316
}
307317
}
308318

@@ -909,12 +919,14 @@ func parseCertificate(der []byte) (*Certificate, error) {
909919
if !spki.ReadASN1BitString(&spk) {
910920
return nil, errors.New("x509: malformed subjectPublicKey")
911921
}
912-
cert.PublicKey, err = parsePublicKey(cert.PublicKeyAlgorithm, &publicKeyInfo{
913-
Algorithm: pkAI,
914-
PublicKey: spk,
915-
})
916-
if err != nil {
917-
return nil, err
922+
if cert.PublicKeyAlgorithm != UnknownPublicKeyAlgorithm {
923+
cert.PublicKey, err = parsePublicKey(&publicKeyInfo{
924+
Algorithm: pkAI,
925+
PublicKey: spk,
926+
})
927+
if err != nil {
928+
return nil, err
929+
}
918930
}
919931

920932
if cert.Version > 1 {

src/crypto/x509/pkcs8.go

+30-4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package x509
66

77
import (
8+
"crypto/ecdh"
89
"crypto/ecdsa"
910
"crypto/ed25519"
1011
"crypto/rsa"
@@ -26,8 +27,9 @@ type pkcs8 struct {
2627

2728
// ParsePKCS8PrivateKey parses an unencrypted private key in PKCS #8, ASN.1 DER form.
2829
//
29-
// It returns a *rsa.PrivateKey, a *ecdsa.PrivateKey, or a ed25519.PrivateKey.
30-
// More types might be supported in the future.
30+
// It returns a *rsa.PrivateKey, a *ecdsa.PrivateKey, a ed25519.PrivateKey (not
31+
// a pointer), or a *ecdh.PublicKey (for X25519). More types might be supported
32+
// in the future.
3133
//
3234
// This kind of key is commonly encoded in PEM blocks of type "PRIVATE KEY".
3335
func ParsePKCS8PrivateKey(der []byte) (key any, err error) {
@@ -74,15 +76,26 @@ func ParsePKCS8PrivateKey(der []byte) (key any, err error) {
7476
}
7577
return ed25519.NewKeyFromSeed(curvePrivateKey), nil
7678

79+
case privKey.Algo.Algorithm.Equal(oidPublicKeyX25519):
80+
if l := len(privKey.Algo.Parameters.FullBytes); l != 0 {
81+
return nil, errors.New("x509: invalid X25519 private key parameters")
82+
}
83+
var curvePrivateKey []byte
84+
if _, err := asn1.Unmarshal(privKey.PrivateKey, &curvePrivateKey); err != nil {
85+
return nil, fmt.Errorf("x509: invalid X25519 private key: %v", err)
86+
}
87+
return ecdh.X25519().NewPrivateKey(curvePrivateKey)
88+
7789
default:
7890
return nil, fmt.Errorf("x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm)
7991
}
8092
}
8193

8294
// MarshalPKCS8PrivateKey converts a private key to PKCS #8, ASN.1 DER form.
8395
//
84-
// The following key types are currently supported: *rsa.PrivateKey, *ecdsa.PrivateKey
85-
// and ed25519.PrivateKey. Unsupported key types result in an error.
96+
// The following key types are currently supported: *rsa.PrivateKey,
97+
// *ecdsa.PrivateKey, ed25519.PrivateKey (not a pointer), and *ecdh.PrivateKey
98+
// (X25519 only). Unsupported key types result in an error.
8699
//
87100
// This kind of key is commonly encoded in PEM blocks of type "PRIVATE KEY".
88101
func MarshalPKCS8PrivateKey(key any) ([]byte, error) {
@@ -128,6 +141,19 @@ func MarshalPKCS8PrivateKey(key any) ([]byte, error) {
128141
}
129142
privKey.PrivateKey = curvePrivateKey
130143

144+
case *ecdh.PrivateKey:
145+
if k.Curve() != ecdh.X25519() {
146+
return nil, errors.New("x509: unknown curve while marshaling to PKCS#8")
147+
}
148+
privKey.Algo = pkix.AlgorithmIdentifier{
149+
Algorithm: oidPublicKeyX25519,
150+
}
151+
curvePrivateKey, err := asn1.Marshal(k.Bytes())
152+
if err != nil {
153+
return nil, fmt.Errorf("x509: failed to marshal private key: %v", err)
154+
}
155+
privKey.PrivateKey = curvePrivateKey
156+
131157
default:
132158
return nil, fmt.Errorf("x509: unknown key type while marshaling PKCS#8: %T", key)
133159
}

src/crypto/x509/pkcs8_test.go

+11
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package x509
66

77
import (
88
"bytes"
9+
"crypto/ecdh"
910
"crypto/ecdsa"
1011
"crypto/ed25519"
1112
"crypto/elliptic"
@@ -49,6 +50,11 @@ var pkcs8P521PrivateKeyHex = `3081ee020100301006072a8648ce3d020106052b8104002304
4950
// From RFC 8410, Section 7.
5051
var pkcs8Ed25519PrivateKeyHex = `302e020100300506032b657004220420d4ee72dbf913584ad5b6d8f1f769f8ad3afe7c28cbf1d4fbe097a88f44755842`
5152

53+
// Generated using:
54+
//
55+
// openssl genpkey -algorithm x25519
56+
var pkcs8X25519PrivateKeyHex = `302e020100300506032b656e0422042068ff93a73c5adefd6d498b24e588fd4daa10924d992afed01b43ca5725025a6b`
57+
5258
func TestPKCS8(t *testing.T) {
5359
tests := []struct {
5460
name string
@@ -90,6 +96,11 @@ func TestPKCS8(t *testing.T) {
9096
keyHex: pkcs8Ed25519PrivateKeyHex,
9197
keyType: reflect.TypeOf(ed25519.PrivateKey{}),
9298
},
99+
{
100+
name: "X25519 private key",
101+
keyHex: pkcs8X25519PrivateKeyHex,
102+
keyType: reflect.TypeOf(&ecdh.PrivateKey{}),
103+
},
93104
}
94105

95106
for _, test := range tests {

src/crypto/x509/x509.go

+50-34
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ package x509
2323
import (
2424
"bytes"
2525
"crypto"
26+
"crypto/ecdh"
2627
"crypto/ecdsa"
2728
"crypto/ed25519"
2829
"crypto/elliptic"
@@ -59,12 +60,12 @@ type pkixPublicKey struct {
5960
BitString asn1.BitString
6061
}
6162

62-
// ParsePKIXPublicKey parses a public key in PKIX, ASN.1 DER form.
63-
// The encoded public key is a SubjectPublicKeyInfo structure
64-
// (see RFC 5280, Section 4.1).
63+
// ParsePKIXPublicKey parses a public key in PKIX, ASN.1 DER form. The encoded
64+
// public key is a SubjectPublicKeyInfo structure (see RFC 5280, Section 4.1).
6565
//
66-
// It returns a *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey, or
67-
// ed25519.PublicKey. More types might be supported in the future.
66+
// It returns a *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey,
67+
// ed25519.PublicKey (not a pointer), or *ecdh.PublicKey (for X25519).
68+
// More types might be supported in the future.
6869
//
6970
// This kind of key is commonly encoded in PEM blocks of type "PUBLIC KEY".
7071
func ParsePKIXPublicKey(derBytes []byte) (pub any, err error) {
@@ -77,11 +78,7 @@ func ParsePKIXPublicKey(derBytes []byte) (pub any, err error) {
7778
} else if len(rest) != 0 {
7879
return nil, errors.New("x509: trailing data after ASN.1 of public-key")
7980
}
80-
algo := getPublicKeyAlgorithmFromOID(pki.Algorithm.Algorithm)
81-
if algo == UnknownPublicKeyAlgorithm {
82-
return nil, errors.New("x509: unknown public key algorithm")
83-
}
84-
return parsePublicKey(algo, &pki)
81+
return parsePublicKey(&pki)
8582
}
8683

8784
func marshalPublicKey(pub any) (publicKeyBytes []byte, publicKeyAlgorithm pkix.AlgorithmIdentifier, err error) {
@@ -117,6 +114,12 @@ func marshalPublicKey(pub any) (publicKeyBytes []byte, publicKeyAlgorithm pkix.A
117114
case ed25519.PublicKey:
118115
publicKeyBytes = pub
119116
publicKeyAlgorithm.Algorithm = oidPublicKeyEd25519
117+
case *ecdh.PublicKey:
118+
if pub.Curve() != ecdh.X25519() {
119+
return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: unsupported ECDH curve")
120+
}
121+
publicKeyBytes = pub.Bytes()
122+
publicKeyAlgorithm.Algorithm = oidPublicKeyX25519
120123
default:
121124
return nil, pkix.AlgorithmIdentifier{}, fmt.Errorf("x509: unsupported public key type: %T", pub)
122125
}
@@ -128,8 +131,9 @@ func marshalPublicKey(pub any) (publicKeyBytes []byte, publicKeyAlgorithm pkix.A
128131
// The encoded public key is a SubjectPublicKeyInfo structure
129132
// (see RFC 5280, Section 4.1).
130133
//
131-
// The following key types are currently supported: *rsa.PublicKey, *ecdsa.PublicKey
132-
// and ed25519.PublicKey. Unsupported key types result in an error.
134+
// The following key types are currently supported: *rsa.PublicKey,
135+
// *ecdsa.PublicKey, ed25519.PublicKey (not a pointer), and *ecdh.PublicKey
136+
// (X25519 only). Unsupported key types result in an error.
133137
//
134138
// This kind of key is commonly encoded in PEM blocks of type "PUBLIC KEY".
135139
func MarshalPKIXPublicKey(pub any) ([]byte, error) {
@@ -240,7 +244,7 @@ type PublicKeyAlgorithm int
240244
const (
241245
UnknownPublicKeyAlgorithm PublicKeyAlgorithm = iota
242246
RSA
243-
DSA // Unsupported.
247+
DSA // Only supported for parsing.
244248
ECDSA
245249
Ed25519
246250
)
@@ -444,27 +448,34 @@ func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) SignatureAlgorithm
444448
return UnknownSignatureAlgorithm
445449
}
446450

447-
// RFC 3279, 2.3 Public Key Algorithms
448-
//
449-
// pkcs-1 OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840)
450-
// rsadsi(113549) pkcs(1) 1 }
451-
//
452-
// rsaEncryption OBJECT IDENTIFIER ::== { pkcs1-1 1 }
453-
//
454-
// id-dsa OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840)
455-
// x9-57(10040) x9cm(4) 1 }
456-
//
457-
// RFC 5480, 2.1.1 Unrestricted Algorithm Identifier and Parameters
458-
//
459-
// id-ecPublicKey OBJECT IDENTIFIER ::= {
460-
// iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 }
461451
var (
462-
oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
463-
oidPublicKeyDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}
464-
oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
465-
oidPublicKeyEd25519 = oidSignatureEd25519
452+
// RFC 3279, 2.3 Public Key Algorithms
453+
//
454+
// pkcs-1 OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840)
455+
// rsadsi(113549) pkcs(1) 1 }
456+
//
457+
// rsaEncryption OBJECT IDENTIFIER ::== { pkcs1-1 1 }
458+
//
459+
// id-dsa OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840)
460+
// x9-57(10040) x9cm(4) 1 }
461+
oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
462+
oidPublicKeyDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}
463+
// RFC 5480, 2.1.1 Unrestricted Algorithm Identifier and Parameters
464+
//
465+
// id-ecPublicKey OBJECT IDENTIFIER ::= {
466+
// iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 }
467+
oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
468+
// RFC 8410, Section 3
469+
//
470+
// id-X25519 OBJECT IDENTIFIER ::= { 1 3 101 110 }
471+
// id-Ed25519 OBJECT IDENTIFIER ::= { 1 3 101 112 }
472+
oidPublicKeyX25519 = asn1.ObjectIdentifier{1, 3, 101, 110}
473+
oidPublicKeyEd25519 = asn1.ObjectIdentifier{1, 3, 101, 112}
466474
)
467475

476+
// getPublicKeyAlgorithmFromOID returns the exposed PublicKeyAlgorithm
477+
// identifier for public key types supported in certificates and CSRs. Marshal
478+
// and Parse functions may support a different set of public key types.
468479
func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm {
469480
switch {
470481
case oid.Equal(oidPublicKeyRSA):
@@ -1521,6 +1532,9 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub, priv
15211532
if err != nil {
15221533
return nil, err
15231534
}
1535+
if getPublicKeyAlgorithmFromOID(publicKeyAlgorithm.Algorithm) == UnknownPublicKeyAlgorithm {
1536+
return nil, fmt.Errorf("x509: unsupported public key type: %T", pub)
1537+
}
15241538

15251539
asn1Issuer, err := subjectBytes(parent)
15261540
if err != nil {
@@ -2068,9 +2082,11 @@ func parseCertificateRequest(in *certificateRequest) (*CertificateRequest, error
20682082
}
20692083

20702084
var err error
2071-
out.PublicKey, err = parsePublicKey(out.PublicKeyAlgorithm, &in.TBSCSR.PublicKey)
2072-
if err != nil {
2073-
return nil, err
2085+
if out.PublicKeyAlgorithm != UnknownPublicKeyAlgorithm {
2086+
out.PublicKey, err = parsePublicKey(&in.TBSCSR.PublicKey)
2087+
if err != nil {
2088+
return nil, err
2089+
}
20742090
}
20752091

20762092
var subject pkix.RDNSequence

src/crypto/x509/x509_test.go

+15
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"bytes"
99
"crypto"
1010
"crypto/dsa"
11+
"crypto/ecdh"
1112
"crypto/ecdsa"
1213
"crypto/ed25519"
1314
"crypto/elliptic"
@@ -115,6 +116,13 @@ func TestParsePKIXPublicKey(t *testing.T) {
115116
t.Errorf("Value returned from ParsePKIXPublicKey was not an Ed25519 public key")
116117
}
117118
})
119+
t.Run("X25519", func(t *testing.T) {
120+
pub := testParsePKIXPublicKey(t, pemX25519Key)
121+
k, ok := pub.(*ecdh.PublicKey)
122+
if !ok || k.Curve() != ecdh.X25519() {
123+
t.Errorf("Value returned from ParsePKIXPublicKey was not an X25519 public key")
124+
}
125+
})
118126
}
119127

120128
var pemPublicKey = `-----BEGIN PUBLIC KEY-----
@@ -153,6 +161,13 @@ MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE=
153161
-----END PUBLIC KEY-----
154162
`
155163

164+
// pemX25519Key was generated from pemX25519Key with "openssl pkey -pubout".
165+
var pemX25519Key = `
166+
-----BEGIN PUBLIC KEY-----
167+
MCowBQYDK2VuAyEA5yGXrH/6OzxuWEhEWS01/f4OP+Of3Yrddy6/J1kDTVM=
168+
-----END PUBLIC KEY-----
169+
`
170+
156171
func TestPKIXMismatchPublicKeyFormat(t *testing.T) {
157172

158173
const pkcs1PublicKey = "308201080282010100817cfed98bcaa2e2a57087451c7674e0c675686dc33ff1268b0c2a6ee0202dec710858ee1c31bdf5e7783582e8ca800be45f3275c6576adc35d98e26e95bb88ca5beb186f853b8745d88bc9102c5f38753bcda519fb05948d5c77ac429255ff8aaf27d9f45d1586e95e2e9ba8a7cb771b8a09dd8c8fed3f933fd9b439bc9f30c475953418ef25f71a2b6496f53d94d39ce850aa0cc75d445b5f5b4f4ee4db78ab197a9a8d8a852f44529a007ac0ac23d895928d60ba538b16b0b087a7f903ed29770e215019b77eaecc360f35f7ab11b6d735978795b2c4a74e5bdea4dc6594cd67ed752a108e666729a753ab36d6c4f606f8760f507e1765be8cd744007e629020103"

0 commit comments

Comments
 (0)