Skip to content

Commit 07b4266

Browse files
seankhliaorolandshoemaker
authored andcommitted
crypto/x509: generate serial number for nil template SerialNumber
Fixes #67675 Change-Id: I976935d20eb6b9adcd19d47bcaeb7abcf78ec5bb Reviewed-on: https://go-review.googlesource.com/c/go/+/630995 Reviewed-by: Roland Shoemaker <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]>
1 parent 9aaef91 commit 07b4266

File tree

2 files changed

+58
-4
lines changed

2 files changed

+58
-4
lines changed

src/crypto/x509/x509.go

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"crypto/ecdsa"
2828
"crypto/ed25519"
2929
"crypto/elliptic"
30+
cryptorand "crypto/rand"
3031
"crypto/rsa"
3132
"crypto/sha1"
3233
"crypto/x509/pkix"
@@ -1655,6 +1656,9 @@ var emptyASN1Subject = []byte{0x30, 0}
16551656
// If SubjectKeyId from template is empty and the template is a CA, SubjectKeyId
16561657
// will be generated from the hash of the public key.
16571658
//
1659+
// If template.SerialNumber is nil, a serial number will be generated which
1660+
// conforms to RFC 5280, Section 4.1.2.2 using entropy from rand.
1661+
//
16581662
// The PolicyIdentifier and Policies fields can both be used to marshal certificate
16591663
// policy OIDs. By default, only the Policies is marshaled, but if the
16601664
// GODEBUG setting "x509usepolicies" has the value "0", the PolicyIdentifiers field will
@@ -1667,16 +1671,35 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub, priv
16671671
return nil, errors.New("x509: certificate private key does not implement crypto.Signer")
16681672
}
16691673

1670-
if template.SerialNumber == nil {
1671-
return nil, errors.New("x509: no SerialNumber given")
1674+
serialNumber := template.SerialNumber
1675+
if serialNumber == nil {
1676+
// Generate a serial number following RFC 5280 Section 4.1.2.2 if one is not provided.
1677+
// Requirements:
1678+
// - serial number must be positive
1679+
// - at most 20 octets when encoded
1680+
maxSerial := big.NewInt(1).Lsh(big.NewInt(1), 20*8)
1681+
for {
1682+
var err error
1683+
serialNumber, err = cryptorand.Int(rand, maxSerial)
1684+
if err != nil {
1685+
return nil, err
1686+
}
1687+
// If the serial is exactly 20 octets, check if the high bit of the first byte is set.
1688+
// If so, generate a new serial, since it will be padded with a leading 0 byte during
1689+
// encoding so that the serial is not interpreted as a negative integer, making it
1690+
// 21 octets.
1691+
if serialBytes := serialNumber.Bytes(); len(serialBytes) > 0 && (len(serialBytes) < 20 || serialBytes[0]&0x80 == 0) {
1692+
break
1693+
}
1694+
}
16721695
}
16731696

16741697
// RFC 5280 Section 4.1.2.2: serial number must positive
16751698
//
16761699
// We _should_ also restrict serials to <= 20 octets, but it turns out a lot of people
16771700
// get this wrong, in part because the encoding can itself alter the length of the
16781701
// serial. For now we accept these non-conformant serials.
1679-
if template.SerialNumber.Sign() == -1 {
1702+
if serialNumber.Sign() == -1 {
16801703
return nil, errors.New("x509: serial number must be positive")
16811704
}
16821705

@@ -1740,7 +1763,7 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub, priv
17401763
encodedPublicKey := asn1.BitString{BitLength: len(publicKeyBytes) * 8, Bytes: publicKeyBytes}
17411764
c := tbsCertificate{
17421765
Version: 2,
1743-
SerialNumber: template.SerialNumber,
1766+
SerialNumber: serialNumber,
17441767
SignatureAlgorithm: algorithmIdentifier,
17451768
Issuer: asn1.RawValue{FullBytes: asn1Issuer},
17461769
Validity: validity{template.NotBefore.UTC(), template.NotAfter.UTC()},

src/crypto/x509/x509_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2323,6 +2323,37 @@ func TestAdditionFieldsInGeneralSubtree(t *testing.T) {
23232323
}
23242324
}
23252325

2326+
func TestEmptySerialNumber(t *testing.T) {
2327+
template := Certificate{
2328+
DNSNames: []string{"example.com"},
2329+
}
2330+
2331+
for range 100 {
2332+
derBytes, err := CreateCertificate(rand.Reader, &template, &template, &testPrivateKey.PublicKey, testPrivateKey)
2333+
if err != nil {
2334+
t.Fatalf("failed to create certificate: %s", err)
2335+
}
2336+
2337+
cert, err := ParseCertificate(derBytes)
2338+
if err != nil {
2339+
t.Fatalf("failed to parse certificate: %s", err)
2340+
}
2341+
2342+
if sign := cert.SerialNumber.Sign(); sign != 1 {
2343+
t.Fatalf("generated a non positive serial, sign: %d", sign)
2344+
}
2345+
2346+
b, err := asn1.Marshal(cert.SerialNumber)
2347+
if err != nil {
2348+
t.Fatalf("failed to marshal generated serial number: %s", err)
2349+
}
2350+
// subtract 2 for tag and length
2351+
if l := len(b) - 2; l > 20 {
2352+
t.Fatalf("generated serial number larger than 20 octets when encoded: %d", l)
2353+
}
2354+
}
2355+
}
2356+
23262357
func TestEmptySubject(t *testing.T) {
23272358
template := Certificate{
23282359
SerialNumber: big.NewInt(1),

0 commit comments

Comments
 (0)