Skip to content

Commit 318addd

Browse files
authored
feat: generate a subject key identifier when creating a certificate (#263)
* feat: generate a subject key identifier when creating a certificate If a subject key id is omitted, go will generate one using sha1. This is described as method 1 in RFC 5280 Section 4.2.1.2. When sha1 is not available (e.g. fips only mode) this method will panic. Update the code to explicitly pass a subject key id to avoid calling sha1 functions. The new SubjectKeyId is generated using method 1 in RFC 7093 Section 2 which takes 160-bits of the SHA-256 hash. * lint: fix linting errors
1 parent b7b4628 commit 318addd

File tree

3 files changed

+53
-0
lines changed

3 files changed

+53
-0
lines changed

testing/certutil/certutil.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"crypto/elliptic"
2525
"crypto/rand"
2626
"crypto/rsa"
27+
"crypto/sha256"
2728
"crypto/tls"
2829
"crypto/x509"
2930
"crypto/x509/pkix"
@@ -349,6 +350,7 @@ func newRootCert(priv crypto.PrivateKey, pub crypto.PublicKey, opts ...Option) (
349350
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
350351
BasicConstraintsValid: true,
351352
IsCA: true,
353+
SubjectKeyId: generateSubjectKeyID(pub),
352354
}
353355

354356
rootCertRawBytes, err := x509.CreateCertificate(
@@ -406,6 +408,23 @@ func getCgf(opts []Option) configs {
406408
return cfg
407409
}
408410

411+
func generateSubjectKeyID(pub crypto.PublicKey) []byte {
412+
// SubjectKeyId generated using method 1 in RFC 7093, Section 2:
413+
// 1) The keyIdentifier is composed of the leftmost 160-bits of the
414+
// SHA-256 hash of the value of the BIT STRING subjectPublicKey
415+
// (excluding the tag, length, and number of unused bits).
416+
var publicKeyBytes []byte
417+
switch publicKey := pub.(type) {
418+
case *rsa.PublicKey:
419+
publicKeyBytes = x509.MarshalPKCS1PublicKey(publicKey)
420+
case *ecdsa.PublicKey:
421+
//nolint:staticcheck // no alternative
422+
publicKeyBytes = elliptic.Marshal(publicKey.Curve, publicKey.X, publicKey.Y)
423+
}
424+
h := sha256.Sum256(publicKeyBytes)
425+
return h[:20]
426+
}
427+
409428
// defaultChildCert generates a child certificate for localhost and 127.0.0.1.
410429
// It returns the certificate and its key as a Pair and an error if any happens.
411430
func defaultChildCert(

transport/httpcommon/diag_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"context"
2222
"crypto/rand"
2323
"crypto/rsa"
24+
"crypto/sha256"
2425
"crypto/tls"
2526
"crypto/x509"
2627
"crypto/x509/pkix"
@@ -313,6 +314,8 @@ func genCA(t *testing.T) tls.Certificate {
313314
caKey, err := rsa.GenerateKey(rand.Reader, 2048) // less secure key for quicker testing.
314315
require.NoError(t, err)
315316

317+
ca.SubjectKeyId = generateSubjectKeyID(&caKey.PublicKey)
318+
316319
caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &caKey.PublicKey, caKey)
317320
require.NoError(t, err)
318321

@@ -378,6 +381,10 @@ func genSignedCert(
378381
certKey, err := rsa.GenerateKey(rand.Reader, 2048)
379382
require.NoError(t, err)
380383

384+
if isCA {
385+
cert.SubjectKeyId = generateSubjectKeyID(&certKey.PublicKey)
386+
}
387+
381388
certBytes, err := x509.CreateCertificate(
382389
rand.Reader,
383390
cert,
@@ -401,3 +408,13 @@ func serial() *big.Int {
401408
ser = ser + 1
402409
return big.NewInt(ser)
403410
}
411+
412+
func generateSubjectKeyID(publicKey *rsa.PublicKey) []byte {
413+
// SubjectKeyId generated using method 1 in RFC 7093, Section 2:
414+
// 1) The keyIdentifier is composed of the leftmost 160-bits of the
415+
// SHA-256 hash of the value of the BIT STRING subjectPublicKey
416+
// (excluding the tag, length, and number of unused bits).
417+
publicKeyBytes := x509.MarshalPKCS1PublicKey(publicKey)
418+
h := sha256.Sum256(publicKeyBytes)
419+
return h[:20]
420+
}

transport/tlscommon/ca_pinning_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"context"
2323
"crypto/rand"
2424
"crypto/rsa"
25+
"crypto/sha256"
2526
"crypto/tls"
2627
"crypto/x509"
2728
"crypto/x509/pkix"
@@ -335,6 +336,8 @@ func genCA() (tls.Certificate, error) {
335336
return tls.Certificate{}, fmt.Errorf("fail to generate RSA key: %w", err)
336337
}
337338

339+
ca.SubjectKeyId = generateSubjectKeyID(&caKey.PublicKey)
340+
338341
caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &caKey.PublicKey, caKey)
339342
if err != nil {
340343
return tls.Certificate{}, fmt.Errorf("fail to create certificate: %w", err)
@@ -404,6 +407,10 @@ func genSignedCert(
404407
return tls.Certificate{}, fmt.Errorf("fail to generate RSA key: %w", err)
405408
}
406409

410+
if isCA {
411+
cert.SubjectKeyId = generateSubjectKeyID(&certKey.PublicKey)
412+
}
413+
407414
certBytes, err := x509.CreateCertificate(
408415
rand.Reader,
409416
cert,
@@ -432,3 +439,13 @@ func serial() *big.Int {
432439
ser = ser + 1
433440
return big.NewInt(ser)
434441
}
442+
443+
func generateSubjectKeyID(publicKey *rsa.PublicKey) []byte {
444+
// SubjectKeyId generated using method 1 in RFC 7093, Section 2:
445+
// 1) The keyIdentifier is composed of the leftmost 160-bits of the
446+
// SHA-256 hash of the value of the BIT STRING subjectPublicKey
447+
// (excluding the tag, length, and number of unused bits).
448+
publicKeyBytes := x509.MarshalPKCS1PublicKey(publicKey)
449+
h := sha256.Sum256(publicKeyBytes)
450+
return h[:20]
451+
}

0 commit comments

Comments
 (0)