Use RFC 7093 truncated SHA256 hash for Subject Key Identifier (#7179)
- Adds a feature flag to gate rollout for SHA256 Subject Key Identifiers
for end-entity certificates.
- The ceremony tool will now use the RFC 7093 section 2 option 1 method
for generating Subject Key Identifiers for future root CA, intermediate
CA, and cross-sign ceremonies.
- - - -
[RFC 7093 section 2 option
1](https://datatracker.ietf.org/doc/html/rfc7093#section-2) provides a
method for generating a truncated SHA256 hash for the Subject Key
Identifier field in accordance with Baseline Requirement [section
7.1.2.11.4 Subject Key
Identifier](90a98dc7c1/docs/BR.md (712114-subject-key-identifier)).
> [RFC5280] specifies two examples for generating key identifiers from
> public keys. Four additional mechanisms are as follows:
>
> 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).
The related [RFC 5280 section
4.2.1.2](https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.2)
states:
> For CA certificates, subject key identifiers SHOULD be derived from
> the public key or a method that generates unique values. Two common
> methods for generating key identifiers from the public key are:
> ...
> Other methods of generating unique numbers are also acceptable.
This commit is contained in:
parent
c45bfb8aed
commit
3366be50f1
|
|
@ -207,8 +207,13 @@ func generateSKID(pk []byte) ([]byte, error) {
|
|||
if _, err := asn1.Unmarshal(pk, &pkixPublicKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// RFC 7093 Section 2 Additional Methods for Generating Key Identifiers: The
|
||||
// keyIdentifier [may be] 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).
|
||||
skid := sha256.Sum256(pkixPublicKey.BitString.Bytes)
|
||||
return skid[:], nil
|
||||
return skid[0:20:20], nil
|
||||
}
|
||||
|
||||
// makeTemplate generates the certificate template for use in x509.CreateCertificate
|
||||
|
|
|
|||
|
|
@ -579,3 +579,10 @@ func TestLoadCert(t *testing.T) {
|
|||
_, err = loadCert("../../test/test-root.pubkey.pem")
|
||||
test.AssertError(t, err, "should have failed when trying to parse a public key")
|
||||
}
|
||||
|
||||
func TestGenerateSKID(t *testing.T) {
|
||||
sha256skid, err := generateSKID(samplePubkey())
|
||||
test.AssertNotError(t, err, "Error generating SKID")
|
||||
test.AssertEquals(t, len(sha256skid), 20)
|
||||
test.AssertEquals(t, cap(sha256skid), 20)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,11 +28,12 @@ func _() {
|
|||
_ = x[AsyncFinalize-17]
|
||||
_ = x[AllowNoCommonName-18]
|
||||
_ = x[CAAAfterValidation-19]
|
||||
_ = x[SHA256SubjectKeyIdentifier-20]
|
||||
}
|
||||
|
||||
const _FeatureFlag_name = "unusedStoreRevokerInfoROCSPStage6ROCSPStage7StoreLintingCertificateInsteadOfPrecertificateCAAValidationMethodsCAAAccountURILeaseCRLShardsEnforceMultiVAMultiVAFullResultsECDSAForAllServeRenewalInfoAllowUnrecognizedFeaturesExpirationMailerUsesJoinCertCheckerChecksValidationsCertCheckerRequiresValidationsCertCheckerRequiresCorrespondenceAsyncFinalizeAllowNoCommonNameCAAAfterValidation"
|
||||
const _FeatureFlag_name = "unusedStoreRevokerInfoROCSPStage6ROCSPStage7StoreLintingCertificateInsteadOfPrecertificateCAAValidationMethodsCAAAccountURILeaseCRLShardsEnforceMultiVAMultiVAFullResultsECDSAForAllServeRenewalInfoAllowUnrecognizedFeaturesExpirationMailerUsesJoinCertCheckerChecksValidationsCertCheckerRequiresValidationsCertCheckerRequiresCorrespondenceAsyncFinalizeAllowNoCommonNameCAAAfterValidationSHA256SubjectKeyIdentifier"
|
||||
|
||||
var _FeatureFlag_index = [...]uint16{0, 6, 22, 33, 44, 90, 110, 123, 137, 151, 169, 180, 196, 221, 245, 273, 303, 336, 349, 366, 384}
|
||||
var _FeatureFlag_index = [...]uint16{0, 6, 22, 33, 44, 90, 110, 123, 137, 151, 169, 180, 196, 221, 245, 273, 303, 336, 349, 366, 384, 410}
|
||||
|
||||
func (i FeatureFlag) String() string {
|
||||
if i < 0 || i >= FeatureFlag(len(_FeatureFlag_index)-1) {
|
||||
|
|
|
|||
|
|
@ -81,6 +81,11 @@ const (
|
|||
// successful validations slower by serializing the DCV and CAA work, but
|
||||
// makes unsuccessful validations easier by not doing CAA work at all.
|
||||
CAAAfterValidation
|
||||
|
||||
// SHA256SubjectKeyIdentifier enables the generation and use of an RFC 7093
|
||||
// compliant truncated SHA256 Subject Key Identifier in end-entity
|
||||
// certificates.
|
||||
SHA256SubjectKeyIdentifier
|
||||
)
|
||||
|
||||
// List of features and their default value, protected by fMu
|
||||
|
|
@ -104,6 +109,7 @@ var features = map[FeatureFlag]bool{
|
|||
AllowNoCommonName: false,
|
||||
LeaseCRLShards: false,
|
||||
CAAAfterValidation: false,
|
||||
SHA256SubjectKeyIdentifier: false,
|
||||
|
||||
StoreLintingCertificateInsteadOfPrecertificate: false,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
|
|
@ -21,6 +22,7 @@ import (
|
|||
ctx509 "github.com/google/certificate-transparency-go/x509"
|
||||
"github.com/jmhodges/clock"
|
||||
|
||||
"github.com/letsencrypt/boulder/features"
|
||||
"github.com/letsencrypt/boulder/precert"
|
||||
)
|
||||
|
||||
|
|
@ -211,8 +213,18 @@ func generateSKID(pk crypto.PublicKey) ([]byte, error) {
|
|||
if _, err := asn1.Unmarshal(pkBytes, &pkixPublicKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
skid := sha1.Sum(pkixPublicKey.BitString.Bytes)
|
||||
return skid[:], nil
|
||||
|
||||
if features.Enabled(features.SHA256SubjectKeyIdentifier) {
|
||||
// RFC 7093 Section 2 Additional Methods for Generating Key Identifiers:
|
||||
// The keyIdentifier [may be] 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).
|
||||
skid := sha256.Sum256(pkixPublicKey.BitString.Bytes)
|
||||
return skid[0:20:20], nil
|
||||
} else {
|
||||
skid := sha1.Sum(pkixPublicKey.BitString.Bytes)
|
||||
return skid[:], nil
|
||||
}
|
||||
}
|
||||
|
||||
// IssuanceRequest describes a certificate issuance request
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/jmhodges/clock"
|
||||
|
||||
"github.com/letsencrypt/boulder/ctpolicy/loglist"
|
||||
"github.com/letsencrypt/boulder/features"
|
||||
"github.com/letsencrypt/boulder/linter"
|
||||
"github.com/letsencrypt/boulder/test"
|
||||
)
|
||||
|
|
@ -764,3 +765,25 @@ func TestMismatchedProfiles(t *testing.T) {
|
|||
test.AssertError(t, err, "preparing final cert issuance")
|
||||
test.AssertContains(t, err.Error(), "precert does not correspond to linted final cert")
|
||||
}
|
||||
|
||||
func TestGenerateSKID(t *testing.T) {
|
||||
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
test.AssertNotError(t, err, "Error generating key")
|
||||
|
||||
_ = features.Set(map[string]bool{"SHA256SubjectKeyIdentifier": true})
|
||||
defer features.Reset()
|
||||
// RFC 7093 section 2 method 1 allows us to use 160 of the leftmost bits for
|
||||
// the Subject Key Identifier. This is the same amount of bits as the
|
||||
// related SHA1 hash.
|
||||
sha256skid, err := generateSKID(key.Public())
|
||||
test.AssertNotError(t, err, "Error generating SKID")
|
||||
test.AssertEquals(t, len(sha256skid), 20)
|
||||
test.AssertEquals(t, cap(sha256skid), 20)
|
||||
features.Reset()
|
||||
|
||||
_ = features.Set(map[string]bool{"SHA256SubjectKeyIdentifier": false})
|
||||
sha1skid, err := generateSKID(key.Public())
|
||||
test.AssertNotError(t, err, "Error generating SKID")
|
||||
test.AssertEquals(t, len(sha1skid), 20)
|
||||
test.AssertEquals(t, cap(sha1skid), 20)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,7 +113,8 @@
|
|||
"ctLogListFile": "test/ct-test-srv/log_list.json",
|
||||
"features": {
|
||||
"ECDSAForAll": true,
|
||||
"AllowNoCommonName": true
|
||||
"AllowNoCommonName": true,
|
||||
"SHA256SubjectKeyIdentifier": true
|
||||
}
|
||||
},
|
||||
"pa": {
|
||||
|
|
|
|||
|
|
@ -113,7 +113,8 @@
|
|||
"ctLogListFile": "test/ct-test-srv/log_list.json",
|
||||
"features": {
|
||||
"ECDSAForAll": true,
|
||||
"AllowNoCommonName": true
|
||||
"AllowNoCommonName": true,
|
||||
"SHA256SubjectKeyIdentifier": true
|
||||
}
|
||||
},
|
||||
"pa": {
|
||||
|
|
|
|||
Loading…
Reference in New Issue