From 8236765e9c11dc7daf2df2beea8aa998ef4afd10 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 15 Jul 2020 14:48:24 -0700 Subject: [PATCH] Use only key part to generate the SubjectKeyId. This change generates the certificate subject key identifier using the recommended method in the RFC 5280 section 4.2.1.2. --- x509util/testdata/ed25519.crt | 10 ++++++ x509util/testdata/google.crt | 28 +++++++++++++++ x509util/testdata/smallstep.crt | 31 +++++++++++++++++ x509util/utils.go | 20 ++++++++++- x509util/utils_test.go | 60 +++++++++++++++++++++++++++++++++ 5 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 x509util/testdata/ed25519.crt create mode 100644 x509util/testdata/google.crt create mode 100644 x509util/testdata/smallstep.crt create mode 100644 x509util/utils_test.go diff --git a/x509util/testdata/ed25519.crt b/x509util/testdata/ed25519.crt new file mode 100644 index 00000000..ef83f0dd --- /dev/null +++ b/x509util/testdata/ed25519.crt @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBWzCCAQ2gAwIBAgIUDIPYISuCyyOYI2Pi95eKQ1vzvZIwBQYDK2VwMCMxITAf +BgNVBAMMGEVkMjU1MTkgdGVzdCBjZXJ0aWZpY2F0ZTAeFw0xOTA1MDYxNzI3MTZa +Fw0xOTA2MDUxNzI3MTZaMCMxITAfBgNVBAMMGEVkMjU1MTkgdGVzdCBjZXJ0aWZp +Y2F0ZTAqMAUGAytlcAMhADYpxWwNTxRsgdD/ddNqcF9pzQ9NZtXamH6CSYmjijz6 +o1MwUTAdBgNVHQ4EFgQUCTs6nUop2JX/aL57Q1Ry4K2i464wHwYDVR0jBBgwFoAU +CTs6nUop2JX/aL57Q1Ry4K2i464wDwYDVR0TAQH/BAUwAwEB/zAFBgMrZXADQQBT +pVgcLDsqnqydTqUdX11tprUI3hKC85cgrvrYmPQagzJrkfUkHcQgfyziTdoTO21U +GtKoKNxgudT0eEs8HJEA +-----END CERTIFICATE----- \ No newline at end of file diff --git a/x509util/testdata/google.crt b/x509util/testdata/google.crt new file mode 100644 index 00000000..c7465328 --- /dev/null +++ b/x509util/testdata/google.crt @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIExjCCA66gAwIBAgIRAP1vPiSYwlsdCAAAAABH8DMwDQYJKoZIhvcNAQELBQAw +QjELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFUdvb2dsZSBUcnVzdCBTZXJ2aWNlczET +MBEGA1UEAxMKR1RTIENBIDFPMTAeFw0yMDA2MTcxNDMxMjJaFw0yMDA5MDkxNDMx +MjJaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH +Ew1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgTExDMRcwFQYDVQQDEw53 +d3cuZ29vZ2xlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOFYzxk3Ghpj +cPvTNffIAqyY4p62NFaISj/X66RMGO7rs0RyM2I4ch1hiEP/alWb/+81BzA+R1nK +w0ZKwy86Kh6jggJaMIICVjAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYB +BQUHAwEwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUjqNsRxKnCgdblFHWKj9y+TUG +RSwwHwYDVR0jBBgwFoAUmNH4bhDrz5vsYJ8YkBug630J/SswaAYIKwYBBQUHAQEE +XDBaMCsGCCsGAQUFBzABhh9odHRwOi8vb2NzcC5wa2kuZ29vZy9ndHMxbzFjb3Jl +MCsGCCsGAQUFBzAChh9odHRwOi8vcGtpLmdvb2cvZ3NyMi9HVFMxTzEuY3J0MBkG +A1UdEQQSMBCCDnd3dy5nb29nbGUuY29tMCEGA1UdIAQaMBgwCAYGZ4EMAQICMAwG +CisGAQQB1nkCBQMwMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5wa2kuZ29v +Zy9HVFMxTzFjb3JlLmNybDCCAQIGCisGAQQB1nkCBAIEgfMEgfAA7gB1ALIeBcyL +os2KIE6HZvkruYolIGdr2vpw57JJUy3vi5BeAAABcsLn/z4AAAQDAEYwRAIgTB4w +h0IyoPg5FFwxva6DkCumsXIIhKmHO1xk6HrOpDECIA0c5Y7Yq2mgGu03QPdbPRLb +dBF5ISGltoW1ni2LkLIsAHUAXqdz+d9WwOe1Nkh90EngMnqRmgyEoRIShBh1loFx +RVgAAAFywuf/bgAABAMARjBEAiBUcGdSwAP/13zLGH4xcJ5l7rYoif6lb7Ymv/4u +intIHQIga1qNo582mt6FkPUYcKNq77MR9MPQYjJj+SQg266bbacwDQYJKoZIhvcN +AQELBQADggEBAFKf5klhZEz9FwJlsu6/vnAn9lUKM2hKVKxO4B8T4f6PuY6dlZ3T +g6mvdtsiLjAZ8v6WL6Bn0v15kTh9RhrKwEwVbLLfoXZbQBygV9B/k+HOCgMEb8U3 +Nn7chIP1kTJcyy3BW7TSU0WODmNuEusX0MQcs5TNl/omwdKkzpB6NjXWhnwgzCeD +1nT33vvq1pwgg28ncIBJkqUpDca7hBDQjx1HYEq00euF5/zDWLV98mH8ORPSC2i2 +N7e0OMNT3q2HJRtHor5USuzehR86u3Aie90Z6jBvG/kNsGSgjUAdj/PAlVBj9GcF +KtLwFt/ee1wps0/VGR+gqkoJps0BW8Tv+1k= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/x509util/testdata/smallstep.crt b/x509util/testdata/smallstep.crt new file mode 100644 index 00000000..7cc4b49f --- /dev/null +++ b/x509util/testdata/smallstep.crt @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFZTCCBE2gAwIBAgISBPRFhEdtcBsNSxHgkoSkHpU/MA0GCSqGSIb3DQEBCwUA +MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD +ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0yMDA2MTYwNzE2NTZaFw0y +MDA5MTQwNzE2NTZaMBoxGDAWBgNVBAMMDyouc21hbGxzdGVwLmNvbTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMgKbXAbD84RScmR3QGVlT27U09ihM6X +XkVp4Ht4fbfm2SvpmOm7kLt5EB3f+0/I3enVsupCoULisUOQpEaoY9wXTjSHRSKl +X3QPzyb8eJrwltxeo0qC5SidaVl2lXcSpKrKMvbp5qfd4hLhAJudEldmTFMQjzou +/FouhzDpP4UDzf8Kc9b8Px27Qw7hg/888lJwCIcTAIVgmHUOxZKmjsHe0rHTNTYi +mE+76udBPyG/Kis+ld2nuufqI/gPrD6gkm5pqSekt057zbk9oJTdBZTuAtaPGlER +bH4G4fbkoT6j0tD7qafeebKATGBtzsK8gqwTZ2Uh4jzd5Ppukg2x4gkCAwEAAaOC +AnMwggJvMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB +BQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUFlxnRkxa25R8P1zsLqtVMHBC +MlUwHwYDVR0jBBgwFoAUqEpqYwR93brm0Tm3pkVl7/Oo7KEwbwYIKwYBBQUHAQEE +YzBhMC4GCCsGAQUFBzABhiJodHRwOi8vb2NzcC5pbnQteDMubGV0c2VuY3J5cHQu +b3JnMC8GCCsGAQUFBzAChiNodHRwOi8vY2VydC5pbnQteDMubGV0c2VuY3J5cHQu +b3JnLzApBgNVHREEIjAggg8qLnNtYWxsc3RlcC5jb22CDXNtYWxsc3RlcC5jb20w +TAYDVR0gBEUwQzAIBgZngQwBAgEwNwYLKwYBBAGC3xMBAQEwKDAmBggrBgEFBQcC +ARYaaHR0cDovL2Nwcy5sZXRzZW5jcnlwdC5vcmcwggEEBgorBgEEAdZ5AgQCBIH1 +BIHyAPAAdgDwlaRZ8gDRgkAQLS+TiI6tS/4dR+OZ4dA0prCoqo6ycwAAAXK8M+PT +AAAEAwBHMEUCIFjO361bM1BiXp9Nexw1KxJX34bI98JkBsHkSz3S+nSZAiEAuBy6 +KBUpYNLT71aPoVWtprZnxThUmKq2gm2Jltp5gMgAdgCyHgXMi6LNiiBOh2b5K7mK +JSBna9r6cOeySVMt74uQXgAAAXK8M+PAAAAEAwBHMEUCIQCIqfGGfeGQHtYnVO4J +UPTqIbNia+Lrbfw9HARElQw4iAIgSspUzNwYHY4cU/BdpfHRaTGzddrTFXhEoCQt +0pbaG0wwDQYJKoZIhvcNAQELBQADggEBAJJgEB70PUChWsbLUyFk1udDPePanU5O +zzSViB5+fesWppG9IDnYnL6KSI+l1jP9jl5Zym79SQ2gSC3xwkswpT8X+Drzup/7 +UV7T5NbHpzkZfg2itsx407yuxoW2L7zihZ4CWm1bodUbPKWNYJAZFrcxqcydwmFn +pvZMoJDP3PW0hejz+gsLe9ZrtXb8q1BLFupHfx+bTx476qhRyL+e2fDi5wQz1Qs2 +jVZGsvv/cljvlRSLZ0NVPeKcRN50f7UQ7OYw+JLR2K5+K1H7oHiu+iUX4F2/PGGm +Tb4HBKgzkJSsS5b3pEp+2U77m9KOBhsiZOVvQ2ECgJ9/eDiy0QCqEyI= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/x509util/utils.go b/x509util/utils.go index 110d2644..ee1caf23 100644 --- a/x509util/utils.go +++ b/x509util/utils.go @@ -5,6 +5,8 @@ import ( "crypto/rand" "crypto/sha1" "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" "math/big" "net" "net/url" @@ -48,11 +50,27 @@ func generateSerialNumber() (*big.Int, error) { return sn, nil } +// subjectPublicKeyInfo is a PKIX public key structure defined in RFC 5280. +type subjectPublicKeyInfo struct { + Algorithm pkix.AlgorithmIdentifier + SubjectPublicKey asn1.BitString +} + +// generateSubjectKeyID generates the key identifier according the the RFC 5280 +// section 4.2.1.2. +// +// The keyIdentifier is composed of the 160-bit SHA-1 hash of the value of the +// BIT STRING subjectPublicKey (excluding the tag, length, and number of unused +// bits). func generateSubjectKeyID(pub crypto.PublicKey) ([]byte, error) { b, err := x509.MarshalPKIXPublicKey(pub) if err != nil { return nil, errors.Wrap(err, "error marshaling public key") } - hash := sha1.Sum(b) + var info subjectPublicKeyInfo + if _, err = asn1.Unmarshal(b, &info); err != nil { + return nil, errors.Wrap(err, "error unmarshaling public key") + } + hash := sha1.Sum(info.SubjectPublicKey.Bytes) return hash[:], nil } diff --git a/x509util/utils_test.go b/x509util/utils_test.go new file mode 100644 index 00000000..7965fe59 --- /dev/null +++ b/x509util/utils_test.go @@ -0,0 +1,60 @@ +package x509util + +import ( + "crypto" + "crypto/x509" + "encoding/pem" + "io/ioutil" + "reflect" + "testing" +) + +func decodeCertificateFile(t *testing.T, filename string) *x509.Certificate { + t.Helper() + b, err := ioutil.ReadFile(filename) + if err != nil { + t.Fatal(err) + } + block, _ := pem.Decode(b) + if block == nil { + t.Fatal("error decoding pem") + } + crt, err := x509.ParseCertificate(block.Bytes) + if err != nil { + t.Fatal(err) + } + return crt +} + +func Test_generateSubjectKeyID(t *testing.T) { + ecdsaCrt := decodeCertificateFile(t, "testdata/google.crt") + rsaCrt := decodeCertificateFile(t, "testdata/smallstep.crt") + ed25519Crt := decodeCertificateFile(t, "testdata/ed25519.crt") + + type args struct { + pub crypto.PublicKey + } + tests := []struct { + name string + args args + want []byte + wantErr bool + }{ + {"ecdsa", args{ecdsaCrt.PublicKey}, ecdsaCrt.SubjectKeyId, false}, + {"rsa", args{rsaCrt.PublicKey}, rsaCrt.SubjectKeyId, false}, + {"ed25519", args{ed25519Crt.PublicKey}, ed25519Crt.SubjectKeyId, false}, + {"fail", args{[]byte("fail")}, nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := generateSubjectKeyID(tt.args.pub) + if (err != nil) != tt.wantErr { + t.Errorf("generateSubjectKeyID() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("generateSubjectKeyID() = %v, want %v", got, tt.want) + } + }) + } +}