Skip to content

Commit

Permalink
feat: generate a subject key identifier when creating a certificate (#…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
kruskall authored Dec 24, 2024
1 parent b7b4628 commit 318addd
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 0 deletions.
19 changes: 19 additions & 0 deletions testing/certutil/certutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
Expand Down Expand Up @@ -349,6 +350,7 @@ func newRootCert(priv crypto.PrivateKey, pub crypto.PublicKey, opts ...Option) (
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
BasicConstraintsValid: true,
IsCA: true,
SubjectKeyId: generateSubjectKeyID(pub),
}

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

func generateSubjectKeyID(pub crypto.PublicKey) []byte {
// SubjectKeyId generated using method 1 in RFC 7093, Section 2:
// 1) The keyIdentifier is composed of the leftmost 160-bits of the
// SHA-256 hash of the value of the BIT STRING subjectPublicKey
// (excluding the tag, length, and number of unused bits).
var publicKeyBytes []byte
switch publicKey := pub.(type) {
case *rsa.PublicKey:
publicKeyBytes = x509.MarshalPKCS1PublicKey(publicKey)
case *ecdsa.PublicKey:
//nolint:staticcheck // no alternative
publicKeyBytes = elliptic.Marshal(publicKey.Curve, publicKey.X, publicKey.Y)
}
h := sha256.Sum256(publicKeyBytes)
return h[:20]
}

// defaultChildCert generates a child certificate for localhost and 127.0.0.1.
// It returns the certificate and its key as a Pair and an error if any happens.
func defaultChildCert(
Expand Down
17 changes: 17 additions & 0 deletions transport/httpcommon/diag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
Expand Down Expand Up @@ -313,6 +314,8 @@ func genCA(t *testing.T) tls.Certificate {
caKey, err := rsa.GenerateKey(rand.Reader, 2048) // less secure key for quicker testing.
require.NoError(t, err)

ca.SubjectKeyId = generateSubjectKeyID(&caKey.PublicKey)

caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &caKey.PublicKey, caKey)
require.NoError(t, err)

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

if isCA {
cert.SubjectKeyId = generateSubjectKeyID(&certKey.PublicKey)
}

certBytes, err := x509.CreateCertificate(
rand.Reader,
cert,
Expand All @@ -401,3 +408,13 @@ func serial() *big.Int {
ser = ser + 1
return big.NewInt(ser)
}

func generateSubjectKeyID(publicKey *rsa.PublicKey) []byte {
// SubjectKeyId generated using method 1 in RFC 7093, Section 2:
// 1) The keyIdentifier is composed of the leftmost 160-bits of the
// SHA-256 hash of the value of the BIT STRING subjectPublicKey
// (excluding the tag, length, and number of unused bits).
publicKeyBytes := x509.MarshalPKCS1PublicKey(publicKey)
h := sha256.Sum256(publicKeyBytes)
return h[:20]
}
17 changes: 17 additions & 0 deletions transport/tlscommon/ca_pinning_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
Expand Down Expand Up @@ -335,6 +336,8 @@ func genCA() (tls.Certificate, error) {
return tls.Certificate{}, fmt.Errorf("fail to generate RSA key: %w", err)
}

ca.SubjectKeyId = generateSubjectKeyID(&caKey.PublicKey)

caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &caKey.PublicKey, caKey)
if err != nil {
return tls.Certificate{}, fmt.Errorf("fail to create certificate: %w", err)
Expand Down Expand Up @@ -404,6 +407,10 @@ func genSignedCert(
return tls.Certificate{}, fmt.Errorf("fail to generate RSA key: %w", err)
}

if isCA {
cert.SubjectKeyId = generateSubjectKeyID(&certKey.PublicKey)
}

certBytes, err := x509.CreateCertificate(
rand.Reader,
cert,
Expand Down Expand Up @@ -432,3 +439,13 @@ func serial() *big.Int {
ser = ser + 1
return big.NewInt(ser)
}

func generateSubjectKeyID(publicKey *rsa.PublicKey) []byte {
// SubjectKeyId generated using method 1 in RFC 7093, Section 2:
// 1) The keyIdentifier is composed of the leftmost 160-bits of the
// SHA-256 hash of the value of the BIT STRING subjectPublicKey
// (excluding the tag, length, and number of unused bits).
publicKeyBytes := x509.MarshalPKCS1PublicKey(publicKey)
h := sha256.Sum256(publicKeyBytes)
return h[:20]
}

0 comments on commit 318addd

Please sign in to comment.