77 "crypto/elliptic"
88 "crypto/rand"
99 "crypto/rsa"
10- "crypto/sha1"
10+ "crypto/sha1" //nolint:gosec // Required for legacy RFC 5280 SKI compatibility.
11+ "crypto/sha256"
1112 "crypto/x509"
1213 "crypto/x509/pkix"
1314 "encoding/asn1"
@@ -32,10 +33,37 @@ const (
3233 defaultValidityPeriod = 7776000
3334)
3435
36+ // SubjectKeyIdentifierHash identifies the hash used to generate certificate
37+ // subject key identifiers.
38+ type SubjectKeyIdentifierHash string
39+
40+ const (
41+ SubjectKeyIdentifierHashSHA1 SubjectKeyIdentifierHash = "sha1"
42+ SubjectKeyIdentifierHashSHA256 SubjectKeyIdentifierHash = "sha256"
43+ )
44+
45+ // Option configures a CA.
46+ type Option struct {
47+ apply func (* options )
48+ }
49+
50+ // WithSubjectKeyIdentifierHash configures the hash used to generate
51+ // certificate subject key identifiers.
52+ func WithSubjectKeyIdentifierHash (hash SubjectKeyIdentifierHash ) Option {
53+ return Option {apply : func (options * options ) {
54+ options .subjectKeyIdentifierHash = hash
55+ }}
56+ }
57+
58+ type options struct {
59+ subjectKeyIdentifierHash SubjectKeyIdentifierHash
60+ }
61+
3562type CAImpl struct {
36- log * log.Logger
37- db * db.MemoryStore
38- ocspResponderURL string
63+ log * log.Logger
64+ db * db.MemoryStore
65+ ocspResponderURL string
66+ subjectKeyIdentifierHash SubjectKeyIdentifierHash
3967
4068 chains []* chain
4169 profiles map [string ]* Profile
@@ -77,7 +105,7 @@ func makeSerial() *big.Int {
77105}
78106
79107// Taken from https://github.com/cloudflare/cfssl/blob/b94e044bb51ec8f5a7232c71b1ed05dbe4da96ce/signer/signer.go#L221-L244
80- func makeSubjectKeyID (key crypto.PublicKey ) ([]byte , error ) {
108+ func makeSubjectKeyID (key crypto.PublicKey , hash SubjectKeyIdentifierHash ) ([]byte , error ) {
81109 // Marshal the public key as ASN.1
82110 pubAsDER , err := x509 .MarshalPKIXPublicKey (key )
83111 if err != nil {
@@ -94,17 +122,28 @@ func makeSubjectKeyID(key crypto.PublicKey) ([]byte, error) {
94122 return nil , err
95123 }
96124
97- // Hash it according to https://tools.ietf.org/html/rfc5280#section-4.2.1.2 Method #1:
98- ski := sha1 .Sum (pubInfo .SubjectPublicKey .Bytes )
99- return ski [:], nil
125+ switch hash {
126+ case SubjectKeyIdentifierHashSHA256 :
127+ // RFC 7093, section 2, method 1 uses the leftmost 160 bits of the
128+ // SHA-256 hash of the subjectPublicKey.
129+ ski := sha256 .Sum256 (pubInfo .SubjectPublicKey .Bytes )
130+ return ski [:20 ], nil
131+ case SubjectKeyIdentifierHashSHA1 :
132+ // RFC 5280, section 4.2.1.2, method 1. SHA-1 is retained for legacy
133+ // compatibility when explicitly selected.
134+ ski := sha1 .Sum (pubInfo .SubjectPublicKey .Bytes )
135+ return ski [:], nil
136+ default :
137+ return nil , fmt .Errorf ("unsupported subject key identifier hash %q" , hash )
138+ }
100139}
101140
102141// makeKey and makeRootCert are adapted from MiniCA:
103142// https://github.com/jsha/minica/blob/3a621c05b61fa1c24bcb42fbde4b261db504a74f/main.go
104143
105144// makeKey creates a new private key of the requested key algorithm, and
106145// returns it and its corresponding Subject Key Identifier.
107- func makeKey (keyAlg string ) (crypto.Signer , []byte , error ) {
146+ func ( ca * CAImpl ) makeKey (keyAlg string ) (crypto.Signer , []byte , error ) {
108147 var key crypto.Signer
109148 var err error
110149 switch keyAlg {
@@ -116,7 +155,7 @@ func makeKey(keyAlg string) (crypto.Signer, []byte, error) {
116155 if err != nil {
117156 return nil , nil , err
118157 }
119- ski , err := makeSubjectKeyID (key .Public ())
158+ ski , err := makeSubjectKeyID (key .Public (), ca . subjectKeyIdentifierHash )
120159 if err != nil {
121160 return nil , nil , err
122161 }
@@ -182,7 +221,7 @@ func (ca *CAImpl) makeCACert(
182221
183222func (ca * CAImpl ) newRootIssuer (name string , keyAlg string ) (* issuer , error ) {
184223 // Make a root private key
185- rk , subjectKeyID , err := makeKey (keyAlg )
224+ rk , subjectKeyID , err := ca . makeKey (keyAlg )
186225 if err != nil {
187226 return nil , err
188227 }
@@ -238,7 +277,7 @@ func (ca *CAImpl) newChain(intermediateKey crypto.Signer, intermediateSubject pk
238277 prev := root
239278 intermediates := make ([]* issuer , numIntermediates )
240279 for i := numIntermediates - 1 ; i > 0 ; i -- {
241- k , ski , err := makeKey (keyAlg )
280+ k , ski , err := ca . makeKey (keyAlg )
242281 if err != nil {
243282 panic (fmt .Sprintf ("Error creating new intermediate issuer: %v" , err ))
244283 }
@@ -373,11 +412,19 @@ func (ca *CAImpl) newCertificate(domains []string, ips []net.IP, key crypto.Publ
373412 return newCert , nil
374413}
375414
376- func New (log * log.Logger , db * db.MemoryStore , ocspResponderURL string , keyAlg string , alternateRoots int , chainLength int , profiles map [string ]Profile ) * CAImpl {
415+ func New (log * log.Logger , db * db.MemoryStore , ocspResponderURL string , keyAlg string , alternateRoots int , chainLength int , profiles map [string ]Profile , opts ... Option ) * CAImpl {
416+ options := options {subjectKeyIdentifierHash : SubjectKeyIdentifierHashSHA1 }
417+ for _ , option := range opts {
418+ if option .apply != nil {
419+ option .apply (& options )
420+ }
421+ }
422+
377423 ca := & CAImpl {
378- log : log ,
379- db : db ,
380- profiles : make (map [string ]* Profile , len (profiles )),
424+ log : log ,
425+ db : db ,
426+ subjectKeyIdentifierHash : options .subjectKeyIdentifierHash ,
427+ profiles : make (map [string ]* Profile , len (profiles )),
381428 }
382429
383430 if ocspResponderURL != "" {
@@ -388,7 +435,7 @@ func New(log *log.Logger, db *db.MemoryStore, ocspResponderURL string, keyAlg st
388435 intermediateSubject := pkix.Name {
389436 CommonName : intermediateCAPrefix + hex .EncodeToString (makeSerial ().Bytes ()[:3 ]),
390437 }
391- intermediateKey , subjectKeyID , err := makeKey (keyAlg )
438+ intermediateKey , subjectKeyID , err := ca . makeKey (keyAlg )
392439 if err != nil {
393440 panic (fmt .Sprintf ("Error creating new intermediate private key: %s" , err .Error ()))
394441 }
0 commit comments