1/* Copyright (c) 2020, Google Inc. 2 * 3 * Permission to use, copy, modify, and/or distribute this software for any 4 * purpose with or without fee is hereby granted, provided that the above 5 * copyright notice and this permission notice appear in all copies. 6 * 7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ 14 15// make_invalid_extensions.go generates a number of certificate chains with 16// invalid extension encodings. 17package main 18 19import ( 20 "crypto/ecdsa" 21 "crypto/rand" 22 "crypto/x509" 23 "crypto/x509/pkix" 24 "encoding/pem" 25 "fmt" 26 "math/big" 27 "os" 28 "time" 29) 30 31type extension struct { 32 // The name of the extension, in a form suitable for including in a 33 // filename. 34 name string 35 // The extension's OID. 36 oid []int 37} 38 39var extensions = []extension{ 40 {name: "authority_key_identifier", oid: []int{2, 5, 29, 35}}, 41 {name: "basic_constraints", oid: []int{2, 5, 29, 19}}, 42 {name: "ext_key_usage", oid: []int{2, 5, 29, 37}}, 43 {name: "key_usage", oid: []int{2, 5, 29, 15}}, 44 {name: "name_constraints", oid: []int{2, 5, 29, 30}}, 45 {name: "subject_alt_name", oid: []int{2, 5, 29, 17}}, 46 {name: "subject_key_identifier", oid: []int{2, 5, 29, 14}}, 47} 48 49var leafKey, intermediateKey, rootKey *ecdsa.PrivateKey 50 51func init() { 52 leafKey = ecdsaKeyFromPEMOrPanic(leafKeyPEM) 53 intermediateKey = ecdsaKeyFromPEMOrPanic(intermediateKeyPEM) 54 rootKey = ecdsaKeyFromPEMOrPanic(rootKeyPEM) 55} 56 57type templateAndKey struct { 58 template x509.Certificate 59 key *ecdsa.PrivateKey 60} 61 62func generateCertificateOrPanic(path string, subject, issuer *templateAndKey) []byte { 63 cert, err := x509.CreateCertificate(rand.Reader, &subject.template, &issuer.template, &subject.key.PublicKey, issuer.key) 64 if err != nil { 65 panic(err) 66 } 67 file, err := os.Create(path) 68 if err != nil { 69 panic(err) 70 } 71 defer file.Close() 72 err = pem.Encode(file, &pem.Block{Type: "CERTIFICATE", Bytes: cert}) 73 if err != nil { 74 panic(err) 75 } 76 return cert 77} 78 79func main() { 80 notBefore, err := time.Parse(time.RFC3339, "2000-01-01T00:00:00Z") 81 if err != nil { 82 panic(err) 83 } 84 notAfter, err := time.Parse(time.RFC3339, "2100-01-01T00:00:00Z") 85 if err != nil { 86 panic(err) 87 } 88 89 root := templateAndKey{ 90 template: x509.Certificate{ 91 SerialNumber: new(big.Int).SetInt64(1), 92 Subject: pkix.Name{CommonName: "Invalid Extensions Root"}, 93 NotBefore: notBefore, 94 NotAfter: notAfter, 95 BasicConstraintsValid: true, 96 IsCA: true, 97 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, 98 KeyUsage: x509.KeyUsageCertSign, 99 SignatureAlgorithm: x509.ECDSAWithSHA256, 100 SubjectKeyId: []byte("root"), 101 }, 102 key: rootKey, 103 } 104 intermediate := templateAndKey{ 105 template: x509.Certificate{ 106 SerialNumber: new(big.Int).SetInt64(2), 107 Subject: pkix.Name{CommonName: "Invalid Extensions Intermediate"}, 108 NotBefore: notBefore, 109 NotAfter: notAfter, 110 BasicConstraintsValid: true, 111 IsCA: true, 112 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, 113 KeyUsage: x509.KeyUsageCertSign, 114 SignatureAlgorithm: x509.ECDSAWithSHA256, 115 SubjectKeyId: []byte("intermediate"), 116 }, 117 key: intermediateKey, 118 } 119 leaf := templateAndKey{ 120 template: x509.Certificate{ 121 SerialNumber: new(big.Int).SetInt64(3), 122 Subject: pkix.Name{CommonName: "www.example.com"}, 123 NotBefore: notBefore, 124 NotAfter: notAfter, 125 BasicConstraintsValid: true, 126 IsCA: false, 127 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, 128 KeyUsage: x509.KeyUsageCertSign, 129 SignatureAlgorithm: x509.ECDSAWithSHA256, 130 DNSNames: []string{"www.example.com"}, 131 SubjectKeyId: []byte("leaf"), 132 PermittedDNSDomains: []string{"www.example.com"}, 133 }, 134 key: leafKey, 135 } 136 137 // Generate a valid certificate chain from the templates. 138 generateCertificateOrPanic("invalid_extension_root.pem", &root, &root) 139 generateCertificateOrPanic("invalid_extension_intermediate.pem", &intermediate, &root) 140 leafDER := generateCertificateOrPanic("invalid_extension_leaf.pem", &leaf, &intermediate) 141 142 leafCert, err := x509.ParseCertificate(leafDER) 143 if err != nil { 144 panic(err) 145 } 146 147 // Make copies of the certificates with invalid extensions. These copies may 148 // be substituted into the valid chain. 149 for _, ext := range extensions { 150 invalidExtension := []pkix.Extension{{Id: ext.oid, Value: []byte("INVALID")}} 151 152 rootInvalid := root 153 rootInvalid.template.ExtraExtensions = invalidExtension 154 generateCertificateOrPanic(fmt.Sprintf("invalid_extension_root_%s.pem", ext.name), &rootInvalid, &rootInvalid) 155 156 intermediateInvalid := intermediate 157 intermediateInvalid.template.ExtraExtensions = invalidExtension 158 generateCertificateOrPanic(fmt.Sprintf("invalid_extension_intermediate_%s.pem", ext.name), &intermediateInvalid, &root) 159 160 leafInvalid := leaf 161 leafInvalid.template.ExtraExtensions = invalidExtension 162 generateCertificateOrPanic(fmt.Sprintf("invalid_extension_leaf_%s.pem", ext.name), &leafInvalid, &intermediate) 163 164 // Additionally generate a copy of the leaf certificate with extra data in 165 // the extension. 166 var trailingDataExtension []pkix.Extension 167 for _, leafExt := range leafCert.Extensions { 168 if leafExt.Id.Equal(ext.oid) { 169 newValue := make([]byte, len(leafExt.Value)+1) 170 copy(newValue, leafExt.Value) 171 trailingDataExtension = append(trailingDataExtension, pkix.Extension{Id: ext.oid, Critical: leafExt.Critical, Value: newValue}) 172 } 173 } 174 if len(trailingDataExtension) != 1 { 175 panic(fmt.Sprintf("could not find sample extension %s", ext.name)) 176 } 177 178 leafTrailingData := leaf 179 leafTrailingData.template.ExtraExtensions = trailingDataExtension 180 generateCertificateOrPanic(fmt.Sprintf("trailing_data_leaf_%s.pem", ext.name), &leafTrailingData, &intermediate) 181 } 182} 183 184const leafKeyPEM = `-----BEGIN PRIVATE KEY----- 185MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgoPUXNXuH9mgiS/nk 186024SYxryxMa3CyGJldiHymLxSquhRANCAASRKti8VW2Rkma+Kt9jQkMNitlCs0l5 187w8u3SSwm7HZREvmcBCJBjVIREacRqI0umhzR2V5NLzBBP9yPD/A+Ch5X 188-----END PRIVATE KEY-----` 189 190const intermediateKeyPEM = `-----BEGIN PRIVATE KEY----- 191MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWHKCKgY058ahE3t6 192vpxVQgzlycgCVMogwjK0y3XMNfWhRANCAATiOnyojN4xS5C8gJ/PHL5cOEsMbsoE 193Y6KT9xRQSh8lEL4d1Vb36kqUgkpqedEImo0Og4Owk6VWVVR/m4Lk+yUw 194-----END PRIVATE KEY-----` 195 196const rootKeyPEM = `-----BEGIN PRIVATE KEY----- 197MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgBwND/eHytW0I417J 198Hr+qcPlp5N1jM3ACXys57bPujg+hRANCAAQmdqXYl1GvY7y3jcTTK6MVXIQr44Tq 199ChRYI6IeV9tIB6jIsOY+Qol1bk8x/7A5FGOnUWFVLEAPEPSJwPndjolt 200-----END PRIVATE KEY-----` 201 202func ecdsaKeyFromPEMOrPanic(in string) *ecdsa.PrivateKey { 203 keyBlock, _ := pem.Decode([]byte(in)) 204 if keyBlock == nil || keyBlock.Type != "PRIVATE KEY" { 205 panic("could not decode private key") 206 } 207 key, err := x509.ParsePKCS8PrivateKey(keyBlock.Bytes) 208 if err != nil { 209 panic(err) 210 } 211 return key.(*ecdsa.PrivateKey) 212} 213