Skip to content

Commit a367981

Browse files
cipherboygopherbot
authored andcommitted
crypto/x509: create CRLs with Issuer.RawSubject
Per discussion with Roland Shoemaker, this updates x509.CreateRevocationList to mirror the behavior of x509.CreateCertificate, creating an internal struct for the ASN.1 encoding of the CRL. This allows us to switch the Issuer field type to asn1.RawValue, bypassing the round-tripping issues of pkix.Name in most scenarios. Per linked ticket, this resolves issues where a non-Go created certificate can be used to create CRLs which aren't correctly attested to that certificate. In rare cases where the CRL issuer is validated against the certificate's issuer (such as the linked JDK example), this can result in failing to check this CRL for the candidate certificate. Fixes #53754 Change-Id: If0adc053c081d6fb0b1ce47324c877eb2429a51f GitHub-Last-Rev: 033115d GitHub-Pull-Request: #53985 Reviewed-on: https://go-review.googlesource.com/c/go/+/418834 Reviewed-by: Roland Shoemaker <[email protected]> Reviewed-by: Bryan Mills <[email protected]> Run-TryBot: Roland Shoemaker <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Auto-Submit: Roland Shoemaker <[email protected]>
1 parent 1bfb51f commit a367981

File tree

2 files changed

+85
-3
lines changed

2 files changed

+85
-3
lines changed

src/crypto/x509/x509.go

+36-3
Original file line numberDiff line numberDiff line change
@@ -2149,6 +2149,29 @@ type RevocationList struct {
21492149
ExtraExtensions []pkix.Extension
21502150
}
21512151

2152+
// These structures reflect the ASN.1 structure of X.509 CRLs better than
2153+
// the existing crypto/x509/pkix variants do. These mirror the existing
2154+
// certificate structs in this file.
2155+
//
2156+
// Notably, we include issuer as an asn1.RawValue, mirroring the behavior of
2157+
// tbsCertificate and allowing raw (unparsed) subjects to be passed cleanly.
2158+
type certificateList struct {
2159+
TBSCertList tbsCertificateList
2160+
SignatureAlgorithm pkix.AlgorithmIdentifier
2161+
SignatureValue asn1.BitString
2162+
}
2163+
2164+
type tbsCertificateList struct {
2165+
Raw asn1.RawContent
2166+
Version int `asn1:"optional,default:0"`
2167+
Signature pkix.AlgorithmIdentifier
2168+
Issuer asn1.RawValue
2169+
ThisUpdate time.Time
2170+
NextUpdate time.Time `asn1:"optional"`
2171+
RevokedCertificates []pkix.RevokedCertificate `asn1:"optional"`
2172+
Extensions []pkix.Extension `asn1:"tag:0,optional,explicit"`
2173+
}
2174+
21522175
// CreateRevocationList creates a new X.509 v2 Certificate Revocation List,
21532176
// according to RFC 5280, based on template.
21542177
//
@@ -2206,10 +2229,16 @@ func CreateRevocationList(rand io.Reader, template *RevocationList, issuer *Cert
22062229
return nil, err
22072230
}
22082231

2209-
tbsCertList := pkix.TBSCertificateList{
2232+
// Correctly use the issuer's subject sequence if one is specified.
2233+
issuerSubject, err := subjectBytes(issuer)
2234+
if err != nil {
2235+
return nil, err
2236+
}
2237+
2238+
tbsCertList := tbsCertificateList{
22102239
Version: 1, // v2
22112240
Signature: signatureAlgorithm,
2212-
Issuer: issuer.Subject.ToRDNSequence(),
2241+
Issuer: asn1.RawValue{FullBytes: issuerSubject},
22132242
ThisUpdate: template.ThisUpdate.UTC(),
22142243
NextUpdate: template.NextUpdate.UTC(),
22152244
Extensions: []pkix.Extension{
@@ -2236,6 +2265,10 @@ func CreateRevocationList(rand io.Reader, template *RevocationList, issuer *Cert
22362265
return nil, err
22372266
}
22382267

2268+
// Optimization to only marshal this struct once, when signing and
2269+
// then embedding in certificateList below.
2270+
tbsCertList.Raw = tbsCertListContents
2271+
22392272
input := tbsCertListContents
22402273
if hashFunc != 0 {
22412274
h := hashFunc.New()
@@ -2255,7 +2288,7 @@ func CreateRevocationList(rand io.Reader, template *RevocationList, issuer *Cert
22552288
return nil, err
22562289
}
22572290

2258-
return asn1.Marshal(pkix.CertificateList{
2291+
return asn1.Marshal(certificateList{
22592292
TBSCertList: tbsCertList,
22602293
SignatureAlgorithm: signatureAlgorithm,
22612294
SignatureValue: asn1.BitString{Bytes: signature, BitLength: len(signature) * 8},

src/crypto/x509/x509_test.go

+49
Original file line numberDiff line numberDiff line change
@@ -2419,6 +2419,18 @@ func TestCreateRevocationList(t *testing.T) {
24192419
if err != nil {
24202420
t.Fatalf("Failed to generate Ed25519 key: %s", err)
24212421
}
2422+
2423+
// Generation command:
2424+
// openssl req -x509 -newkey rsa -keyout key.pem -out cert.pem -days 365 -nodes -subj '/C=US/ST=California/L=San Francisco/O=Internet Widgets, Inc./OU=WWW/CN=Root/[email protected]' -sha256 -addext basicConstraints=CA:TRUE -addext "keyUsage = digitalSignature, keyEncipherment, dataEncipherment, cRLSign, keyCertSign" -utf8
2425+
utf8CAStr := "MIIEITCCAwmgAwIBAgIUXHXy7NdtDv+ClaHvIvlwCYiI4a4wDQYJKoZIhvcNAQELBQAwgZoxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMR8wHQYDVQQKDBZJbnRlcm5ldCBXaWRnZXRzLCBJbmMuMQwwCgYDVQQLDANXV1cxDTALBgNVBAMMBFJvb3QxIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUuY29tMB4XDTIyMDcwODE1MzgyMFoXDTIzMDcwODE1MzgyMFowgZoxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMR8wHQYDVQQKDBZJbnRlcm5ldCBXaWRnZXRzLCBJbmMuMQwwCgYDVQQLDANXV1cxDTALBgNVBAMMBFJvb3QxIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmXvp0WNjsZzySWT7Ce5zewQNKq8ujeZGphJ44Vdrwut/b6TcC4iYENds5+7/3PYwBllp3K5TRpCcafSxdhJsvA7/zWlHHNRcJhJLNt9qsKWP6ukI2Iw6OmFMg6kJQ8f67RXkT8HR3v0UqE+lWrA0g+oRuj4erLtfOtSpnl4nsE/Rs2qxbELFWAf7F5qMqH4dUyveWKrNT8eI6YQN+wBg0MAjoKRvDJnBhuo+IvvXX8Aq1QWUcBGPK3or/Ehxy5f/gEmSUXyEU1Ht/vATt2op+eRaEEpBdGRvO+DrKjlcQV2XMN18A9LAX6hCzH43sGye87dj7RZ9yj+waOYNaM7kFQIDAQABo10wWzAdBgNVHQ4EFgQUtbSlrW4hGL2kNjviM6wcCRwvOEEwHwYDVR0jBBgwFoAUtbSlrW4hGL2kNjviM6wcCRwvOEEwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAbYwDQYJKoZIhvcNAQELBQADggEBAAko82YNNI2n/45L3ya21vufP6nZihIOIxgcRPUMX+IDJZk16qsFdcLgH3KAP8uiVLn8sULuCj35HpViR4IcAk2d+DqfG11l8kY+e5P7nYsViRfy0AatF59/sYlWf+3RdmPXfL70x4mE9OqlMdDm0kR2obps8rng83VLDNvj3R5sBnQwdw6LKLGzaE+RiCTmkH0+P6vnbOJ33su9+9al1+HvJUg3UM1Xq5Bw7TE8DQTetMV3c2Q35RQaJB9pQ4blJOnW9hfnt8yQzU6TU1bU4mRctTm1o1f8btPqUpi+/blhi5MUJK0/myj1XD00pmyfp8QAFl1EfqmTMIBMLg633A0="
2426+
utf8CABytes, _ := base64.StdEncoding.DecodeString(utf8CAStr)
2427+
utf8CA, _ := ParseCertificate(utf8CABytes)
2428+
2429+
utf8KeyStr := "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCZe+nRY2OxnPJJZPsJ7nN7BA0qry6N5kamEnjhV2vC639vpNwLiJgQ12zn7v/c9jAGWWncrlNGkJxp9LF2Emy8Dv/NaUcc1FwmEks232qwpY/q6QjYjDo6YUyDqQlDx/rtFeRPwdHe/RSoT6VasDSD6hG6Ph6su1861KmeXiewT9GzarFsQsVYB/sXmoyofh1TK95Yqs1Px4jphA37AGDQwCOgpG8MmcGG6j4i+9dfwCrVBZRwEY8reiv8SHHLl/+ASZJRfIRTUe3+8BO3ain55FoQSkF0ZG874OsqOVxBXZcw3XwD0sBfqELMfjewbJ7zt2PtFn3KP7Bo5g1ozuQVAgMBAAECggEAIscjKiD9PAe2Fs9c2tk/LYazfRKI1/pv072nylfGwToffCq8+ZgP7PEDamKLc4QNScME685MbFbkOlYJyBlQriQv7lmGlY/A+Zd3l410XWaGf9IiAP91Sjk13zd0M/micApf23qtlXt/LMwvSadXnvRw4+SjirxCTdBWRt5K2/ZAN550v7bHFk1EZc3UBF6sOoNsjQWh9Ek79UmQYJBPiZDBHO7O2fh2GSIbUutTma+Tb2i1QUZzg+AG3cseF3p1i3uhNrCh+p+01bJSzGTQsRod2xpD1tpWwR3kIftCOmD1XnhpaBQi7PXjEuNbfucaftnoYj2ShDdmgD5RkkbTAQKBgQC8Ghu5MQ/yIeqXg9IpcSxuWtUEAEfK33/cC/IvuntgbNEnWQm5Lif4D6a9zjkxiCS+9HhrUu5U2EV8NxOyaqmtub3Np1Z5mPuI9oiZ119bjUJd4X+jKOTaePWvOv/rL/pTHYqzXohVMrXy+DaTIq4lOcv3n72SuhuTcKU95rhKtQKBgQDQ4t+HsRZd5fJzoCgRQhlNK3EbXQDv2zXqMW3GfpF7GaDP18I530inRURSJa++rvi7/MCFg/TXVS3QC4HXtbzTYTqhE+VHzSr+/OcsqpLE8b0jKBDv/SBkz811PUJDs3LsX31DT3K0zUpMpNSd/5SYTyJKef9L6mxmwlC1S2Yv4QKBgQC57SiYDdnQIRwrtZ2nXvlm/xttAAX2jqJoU9qIuNA4yHaYaRcGVowlUvsiw9OelQ6VPTpGA0wWy0src5lhkrKzSFRHEe+U89U1VVJCljLoYKFIAJvUH5jOJh/am/vYca0COMIfeAJUDHLyfcwb9XyiyRVGZzvP62tUelSq8gIZvQKBgCAHeaDzzWsudCO4ngwvZ3PGwnwgoaElqrmzRJLYG3SVtGvKOJTpINnNLDGwZ6dEaw1gLyEJ38QY4oJxEULDMiXzVasXQuPkmMAqhUP7D7A1JPw8C4TQ+mOa3XUppHx/CpMl/S4SA5OnmsnvyE5Fv0IveCGVXUkFtAN5rihuXEfhAoGANUkuGU3A0Upk2mzv0JTGP4H95JFG93cqnyPNrYs30M6RkZNgTW27yyr+Nhs4/cMdrg1AYTB0+6ItQWSDmYLs7JEbBE/8L8fdD1irIcygjIHE9nJh96TgZCt61kVGLE8758lOdmoB2rZOpGwi16QIhdQb+IyozYqfX+lQUojL/W0="
2430+
utf8KeyBytes, _ := base64.StdEncoding.DecodeString(utf8KeyStr)
2431+
utf8KeyRaw, _ := ParsePKCS8PrivateKey(utf8KeyBytes)
2432+
utf8Key := utf8KeyRaw.(crypto.Signer)
2433+
24222434
tests := []struct {
24232435
name string
24242436
key crypto.Signer
@@ -2687,6 +2699,16 @@ func TestCreateRevocationList(t *testing.T) {
26872699
NextUpdate: time.Time{}.Add(time.Hour * 48),
26882700
},
26892701
},
2702+
{
2703+
name: "valid CA with utf8 Subject fields including Email, empty list",
2704+
key: utf8Key,
2705+
issuer: utf8CA,
2706+
template: &RevocationList{
2707+
Number: big.NewInt(5),
2708+
ThisUpdate: time.Time{}.Add(time.Hour * 24),
2709+
NextUpdate: time.Time{}.Add(time.Hour * 48),
2710+
},
2711+
},
26902712
}
26912713

26922714
for _, tc := range tests {
@@ -2746,6 +2768,33 @@ func TestCreateRevocationList(t *testing.T) {
27462768
t.Fatalf("Unexpected second extension: got %v, want %v",
27472769
parsedCRL.Extensions[1], crlExt)
27482770
}
2771+
2772+
// With Go 1.19's updated RevocationList, we can now directly compare
2773+
// the RawSubject of the certificate to RawIssuer on the parsed CRL.
2774+
// However, this doesn't work with our hacked issuers above (that
2775+
// aren't parsed from a proper DER bundle but are instead manually
2776+
// constructed). Prefer RawSubject when it is set.
2777+
if len(tc.issuer.RawSubject) > 0 {
2778+
issuerSubj, err := subjectBytes(tc.issuer)
2779+
if err != nil {
2780+
t.Fatalf("failed to get issuer subject: %s", err)
2781+
}
2782+
if !bytes.Equal(issuerSubj, parsedCRL.RawIssuer) {
2783+
t.Fatalf("Unexpected issuer subject; wanted: %v, got: %v", hex.EncodeToString(issuerSubj), hex.EncodeToString(parsedCRL.RawIssuer))
2784+
}
2785+
} else {
2786+
// When we hack our custom Subject in the test cases above,
2787+
// we don't set the additional fields (such as Names) in the
2788+
// hacked issuer. Round-trip a parsing of pkix.Name so that
2789+
// we add these missing fields for the comparison.
2790+
issuerRDN := tc.issuer.Subject.ToRDNSequence()
2791+
var caIssuer pkix.Name
2792+
caIssuer.FillFromRDNSequence(&issuerRDN)
2793+
if !reflect.DeepEqual(caIssuer, parsedCRL.Issuer) {
2794+
t.Fatalf("Expected issuer.Subject, parsedCRL.Issuer to be the same; wanted: %#v, got: %#v", caIssuer, parsedCRL.Issuer)
2795+
}
2796+
}
2797+
27492798
if len(parsedCRL.Extensions[2:]) == 0 && len(tc.template.ExtraExtensions) == 0 {
27502799
// If we don't have anything to check return early so we don't
27512800
// hit a [] != nil false positive below.

0 commit comments

Comments
 (0)