| // Copyright (c) 2020, Google Inc. |
| // |
| // Permission to use, copy, modify, and/or distribute this software for any |
| // purpose with or without fee is hereby granted, provided that the above |
| // copyright notice and this permission notice appear in all copies. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
| // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
| // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
| // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| |
| //go:build ignore |
| |
| // make_policy_certs.go generates certificates for testing policy handling. |
| package main |
| |
| import ( |
| "crypto/ecdsa" |
| "crypto/rand" |
| "crypto/x509" |
| "crypto/x509/pkix" |
| "encoding/asn1" |
| "encoding/pem" |
| "flag" |
| "math/big" |
| "os" |
| "time" |
| |
| "golang.org/x/crypto/cryptobyte" |
| cbasn1 "golang.org/x/crypto/cryptobyte/asn1" |
| ) |
| |
| var resetFlag = flag.Bool("reset", false, "if set, regenerates certificates that already exist") |
| |
| var ( |
| // https://davidben.net/oid |
| testOID1 = asn1.ObjectIdentifier([]int{1, 2, 840, 113554, 4, 1, 72585, 2, 1}) |
| testOID2 = asn1.ObjectIdentifier([]int{1, 2, 840, 113554, 4, 1, 72585, 2, 2}) |
| testOID3 = asn1.ObjectIdentifier([]int{1, 2, 840, 113554, 4, 1, 72585, 2, 3}) |
| testOID4 = asn1.ObjectIdentifier([]int{1, 2, 840, 113554, 4, 1, 72585, 2, 4}) |
| testOID5 = asn1.ObjectIdentifier([]int{1, 2, 840, 113554, 4, 1, 72585, 2, 5}) |
| |
| // https://www.rfc-editor.org/rfc/rfc5280.html#section-4.2.1.4 |
| certificatePoliciesOID = asn1.ObjectIdentifier([]int{2, 5, 29, 32}) |
| anyPolicyOID = asn1.ObjectIdentifier([]int{2, 5, 29, 32, 0}) |
| |
| // https://www.rfc-editor.org/rfc/rfc5280.html#section-4.2.1.5 |
| policyMappingsOID = asn1.ObjectIdentifier([]int{2, 5, 29, 33}) |
| |
| // https://www.rfc-editor.org/rfc/rfc5280.html#section-4.2.1.11 |
| policyConstraintsOID = asn1.ObjectIdentifier([]int{2, 5, 29, 36}) |
| ) |
| |
| var leafKey, intermediateKey, rootKey *ecdsa.PrivateKey |
| |
| func init() { |
| leafKey = mustParseECDSAKey(leafKeyPEM) |
| intermediateKey = mustParseECDSAKey(intermediateKeyPEM) |
| rootKey = mustParseECDSAKey(rootKeyPEM) |
| } |
| |
| type templateAndKey struct { |
| template x509.Certificate |
| key *ecdsa.PrivateKey |
| } |
| |
| func mustGenerateCertificate(path string, subject, issuer *templateAndKey) { |
| if !*resetFlag { |
| // Skip if the file already exists. |
| _, err := os.Stat(path) |
| if err == nil { |
| return |
| } |
| if !os.IsNotExist(err) { |
| panic(err) |
| } |
| } |
| cert, err := x509.CreateCertificate(rand.Reader, &subject.template, &issuer.template, &subject.key.PublicKey, issuer.key) |
| if err != nil { |
| panic(err) |
| } |
| file, err := os.Create(path) |
| if err != nil { |
| panic(err) |
| } |
| defer file.Close() |
| err = pem.Encode(file, &pem.Block{Type: "CERTIFICATE", Bytes: cert}) |
| if err != nil { |
| panic(err) |
| } |
| } |
| |
| func main() { |
| flag.Parse() |
| |
| notBefore, err := time.Parse(time.RFC3339, "2000-01-01T00:00:00Z") |
| if err != nil { |
| panic(err) |
| } |
| notAfter, err := time.Parse(time.RFC3339, "2100-01-01T00:00:00Z") |
| if err != nil { |
| panic(err) |
| } |
| |
| root2 := templateAndKey{ |
| template: x509.Certificate{ |
| SerialNumber: new(big.Int).SetInt64(1), |
| Subject: pkix.Name{CommonName: "Policy Root 2"}, |
| NotBefore: notBefore, |
| NotAfter: notAfter, |
| BasicConstraintsValid: true, |
| IsCA: true, |
| ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, |
| KeyUsage: x509.KeyUsageCertSign, |
| SignatureAlgorithm: x509.ECDSAWithSHA256, |
| }, |
| key: rootKey, |
| } |
| root := templateAndKey{ |
| template: x509.Certificate{ |
| SerialNumber: new(big.Int).SetInt64(1), |
| Subject: pkix.Name{CommonName: "Policy Root"}, |
| NotBefore: notBefore, |
| NotAfter: notAfter, |
| BasicConstraintsValid: true, |
| IsCA: true, |
| ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, |
| KeyUsage: x509.KeyUsageCertSign, |
| SignatureAlgorithm: x509.ECDSAWithSHA256, |
| }, |
| key: rootKey, |
| } |
| intermediate := templateAndKey{ |
| template: x509.Certificate{ |
| SerialNumber: new(big.Int).SetInt64(2), |
| Subject: pkix.Name{CommonName: "Policy Intermediate"}, |
| NotBefore: notBefore, |
| NotAfter: notAfter, |
| BasicConstraintsValid: true, |
| IsCA: true, |
| ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, |
| KeyUsage: x509.KeyUsageCertSign, |
| SignatureAlgorithm: x509.ECDSAWithSHA256, |
| PolicyIdentifiers: []asn1.ObjectIdentifier{testOID1, testOID2}, |
| }, |
| key: intermediateKey, |
| } |
| leaf := templateAndKey{ |
| template: x509.Certificate{ |
| SerialNumber: new(big.Int).SetInt64(3), |
| Subject: pkix.Name{CommonName: "www.example.com"}, |
| NotBefore: notBefore, |
| NotAfter: notAfter, |
| BasicConstraintsValid: true, |
| IsCA: false, |
| ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, |
| KeyUsage: x509.KeyUsageCertSign, |
| SignatureAlgorithm: x509.ECDSAWithSHA256, |
| DNSNames: []string{"www.example.com"}, |
| PolicyIdentifiers: []asn1.ObjectIdentifier{testOID1, testOID2}, |
| }, |
| key: leafKey, |
| } |
| |
| // Generate a valid certificate chain from the templates. |
| mustGenerateCertificate("policy_root.pem", &root, &root) |
| mustGenerateCertificate("policy_intermediate.pem", &intermediate, &root) |
| mustGenerateCertificate("policy_leaf.pem", &leaf, &intermediate) |
| |
| // root2 is used for tests that need a longer chain, using a Root/Root2 |
| // cross-sign as one of the certificates. |
| mustGenerateCertificate("policy_root2.pem", &root2, &root2) |
| |
| // Introduce syntax errors in the leaf and intermediate. |
| leafInvalid := leaf |
| leafInvalid.template.PolicyIdentifiers = nil |
| leafInvalid.template.ExtraExtensions = []pkix.Extension{{Id: certificatePoliciesOID, Value: []byte("INVALID")}} |
| mustGenerateCertificate("policy_leaf_invalid.pem", &leafInvalid, &root) |
| |
| intermediateInvalid := intermediate |
| intermediateInvalid.template.PolicyIdentifiers = nil |
| intermediateInvalid.template.ExtraExtensions = []pkix.Extension{{Id: certificatePoliciesOID, Value: []byte("INVALID")}} |
| mustGenerateCertificate("policy_intermediate_invalid.pem", &intermediateInvalid, &root) |
| |
| // Duplicates are not allowed in certificatePolicies. |
| leafDuplicate := leaf |
| leafDuplicate.template.PolicyIdentifiers = []asn1.ObjectIdentifier{testOID1, testOID2, testOID2} |
| mustGenerateCertificate("policy_leaf_duplicate.pem", &leafDuplicate, &root) |
| |
| intermediateDuplicate := intermediate |
| intermediateDuplicate.template.PolicyIdentifiers = []asn1.ObjectIdentifier{testOID1, testOID2, testOID2} |
| mustGenerateCertificate("policy_intermediate_duplicate.pem", &intermediateDuplicate, &root) |
| |
| // Various policy constraints with requireExplicitPolicy values. |
| b := cryptobyte.NewBuilder(nil) |
| b.AddASN1(cbasn1.SEQUENCE, func(seq *cryptobyte.Builder) { |
| seq.AddASN1Int64WithTag(0, cbasn1.Tag(0).ContextSpecific()) |
| }) |
| requireExplicitPolicy0 := b.BytesOrPanic() |
| |
| b = cryptobyte.NewBuilder(nil) |
| b.AddASN1(cbasn1.SEQUENCE, func(seq *cryptobyte.Builder) { |
| seq.AddASN1Int64WithTag(1, cbasn1.Tag(0).ContextSpecific()) |
| }) |
| requireExplicitPolicy1 := b.BytesOrPanic() |
| |
| b = cryptobyte.NewBuilder(nil) |
| b.AddASN1(cbasn1.SEQUENCE, func(seq *cryptobyte.Builder) { |
| seq.AddASN1Int64WithTag(2, cbasn1.Tag(0).ContextSpecific()) |
| }) |
| requireExplicitPolicy2 := b.BytesOrPanic() |
| |
| // A version of the intermediate that sets requireExplicitPolicy, skipping |
| // zero certificates. |
| intermediateRequire := intermediate |
| intermediateRequire.template.ExtraExtensions = []pkix.Extension{{Id: policyConstraintsOID, Value: requireExplicitPolicy0}} |
| mustGenerateCertificate("policy_intermediate_require.pem", &intermediateRequire, &root) |
| |
| // Same as above, but there are no policies on the intermediate. |
| intermediateRequire.template.PolicyIdentifiers = nil |
| mustGenerateCertificate("policy_intermediate_require_no_policies.pem", &intermediateRequire, &root) |
| |
| // Same as above, but the policy list has duplicates. |
| intermediateRequire.template.PolicyIdentifiers = []asn1.ObjectIdentifier{testOID1, testOID2, testOID2} |
| mustGenerateCertificate("policy_intermediate_require_duplicate.pem", &intermediateRequire, &root) |
| |
| // Corresponding certificates that instead assert the anyPolicy OID. |
| intermediateAny := intermediate |
| intermediateAny.template.PolicyIdentifiers = []asn1.ObjectIdentifier{anyPolicyOID} |
| mustGenerateCertificate("policy_intermediate_any.pem", &intermediateAny, &root) |
| |
| // Other requireExplicitPolicy values, on the leaf and intermediate. |
| intermediateRequire = intermediate |
| intermediateRequire.template.ExtraExtensions = []pkix.Extension{{Id: policyConstraintsOID, Value: requireExplicitPolicy1}} |
| mustGenerateCertificate("policy_intermediate_require1.pem", &intermediateRequire, &root) |
| intermediateRequire.template.ExtraExtensions = []pkix.Extension{{Id: policyConstraintsOID, Value: requireExplicitPolicy2}} |
| mustGenerateCertificate("policy_intermediate_require2.pem", &intermediateRequire, &root) |
| leafRequire := leaf |
| leafRequire.template.ExtraExtensions = []pkix.Extension{{Id: policyConstraintsOID, Value: requireExplicitPolicy0}} |
| mustGenerateCertificate("policy_leaf_require.pem", &leafRequire, &intermediate) |
| leafRequire.template.ExtraExtensions = []pkix.Extension{{Id: policyConstraintsOID, Value: requireExplicitPolicy1}} |
| mustGenerateCertificate("policy_leaf_require1.pem", &leafRequire, &intermediate) |
| |
| leafAny := leaf |
| leafAny.template.PolicyIdentifiers = []asn1.ObjectIdentifier{anyPolicyOID} |
| mustGenerateCertificate("policy_leaf_any.pem", &leafAny, &intermediate) |
| |
| // An intermediate which maps OID1 to (OID2, OID3), and which asserts the |
| // input OIDs either all at once, or as anyPolicy. |
| b = cryptobyte.NewBuilder(nil) |
| b.AddASN1(cbasn1.SEQUENCE, func(seq *cryptobyte.Builder) { |
| // Map OID3 to (OID1, OID2). |
| seq.AddASN1(cbasn1.SEQUENCE, func(mapping *cryptobyte.Builder) { |
| mapping.AddASN1ObjectIdentifier(testOID3) |
| mapping.AddASN1ObjectIdentifier(testOID1) |
| }) |
| seq.AddASN1(cbasn1.SEQUENCE, func(mapping *cryptobyte.Builder) { |
| mapping.AddASN1ObjectIdentifier(testOID3) |
| mapping.AddASN1ObjectIdentifier(testOID2) |
| }) |
| |
| // Map all pairs of OID4 and OID5 to each other. |
| seq.AddASN1(cbasn1.SEQUENCE, func(mapping *cryptobyte.Builder) { |
| mapping.AddASN1ObjectIdentifier(testOID4) |
| mapping.AddASN1ObjectIdentifier(testOID4) |
| }) |
| seq.AddASN1(cbasn1.SEQUENCE, func(mapping *cryptobyte.Builder) { |
| mapping.AddASN1ObjectIdentifier(testOID4) |
| mapping.AddASN1ObjectIdentifier(testOID5) |
| }) |
| seq.AddASN1(cbasn1.SEQUENCE, func(mapping *cryptobyte.Builder) { |
| mapping.AddASN1ObjectIdentifier(testOID5) |
| mapping.AddASN1ObjectIdentifier(testOID4) |
| }) |
| seq.AddASN1(cbasn1.SEQUENCE, func(mapping *cryptobyte.Builder) { |
| mapping.AddASN1ObjectIdentifier(testOID5) |
| mapping.AddASN1ObjectIdentifier(testOID5) |
| }) |
| }) |
| intermediateMapped := intermediate |
| intermediateMapped.template.PolicyIdentifiers = []asn1.ObjectIdentifier{testOID1, testOID2, testOID3, testOID4, testOID5} |
| intermediateMapped.template.ExtraExtensions = []pkix.Extension{{Id: policyMappingsOID, Value: b.BytesOrPanic()}} |
| mustGenerateCertificate("policy_intermediate_mapped.pem", &intermediateMapped, &root) |
| |
| intermediateMapped.template.PolicyIdentifiers = []asn1.ObjectIdentifier{anyPolicyOID} |
| mustGenerateCertificate("policy_intermediate_mapped_any.pem", &intermediateMapped, &root) |
| |
| intermediateMapped.template.PolicyIdentifiers = []asn1.ObjectIdentifier{testOID3} |
| mustGenerateCertificate("policy_intermediate_mapped_oid3.pem", &intermediateMapped, &root) |
| |
| // Leaves which assert more specific OIDs, to test intermediate_mapped. |
| leafSingle := leaf |
| leafSingle.template.PolicyIdentifiers = []asn1.ObjectIdentifier{testOID1} |
| mustGenerateCertificate("policy_leaf_oid1.pem", &leafSingle, &intermediate) |
| leafSingle.template.PolicyIdentifiers = []asn1.ObjectIdentifier{testOID2} |
| mustGenerateCertificate("policy_leaf_oid2.pem", &leafSingle, &intermediate) |
| leafSingle.template.PolicyIdentifiers = []asn1.ObjectIdentifier{testOID3} |
| mustGenerateCertificate("policy_leaf_oid3.pem", &leafSingle, &intermediate) |
| leafSingle.template.PolicyIdentifiers = []asn1.ObjectIdentifier{testOID4} |
| mustGenerateCertificate("policy_leaf_oid4.pem", &leafSingle, &intermediate) |
| leafSingle.template.PolicyIdentifiers = []asn1.ObjectIdentifier{testOID5} |
| mustGenerateCertificate("policy_leaf_oid5.pem", &leafSingle, &intermediate) |
| |
| leafNone := leaf |
| leafNone.template.PolicyIdentifiers = nil |
| mustGenerateCertificate("policy_leaf_none.pem", &leafNone, &intermediate) |
| |
| // Make version of Root, signed by Root 2, with policy mapping inhibited. |
| // This can be combined with intermediateMapped to test the combination. |
| b = cryptobyte.NewBuilder(nil) |
| b.AddASN1(cbasn1.SEQUENCE, func(seq *cryptobyte.Builder) { |
| seq.AddASN1Int64WithTag(0, cbasn1.Tag(1).ContextSpecific()) |
| }) |
| inhibitPolicyMapping0 := b.BytesOrPanic() |
| |
| inhibitMapping := root |
| inhibitMapping.template.PolicyIdentifiers = []asn1.ObjectIdentifier{anyPolicyOID} |
| inhibitMapping.template.ExtraExtensions = []pkix.Extension{{Id: policyConstraintsOID, Value: inhibitPolicyMapping0}} |
| mustGenerateCertificate("policy_root_cross_inhibit_mapping.pem", &inhibitMapping, &root2) |
| } |
| |
| const leafKeyPEM = `-----BEGIN PRIVATE KEY----- |
| MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgoPUXNXuH9mgiS/nk |
| 024SYxryxMa3CyGJldiHymLxSquhRANCAASRKti8VW2Rkma+Kt9jQkMNitlCs0l5 |
| w8u3SSwm7HZREvmcBCJBjVIREacRqI0umhzR2V5NLzBBP9yPD/A+Ch5X |
| -----END PRIVATE KEY-----` |
| |
| const intermediateKeyPEM = `-----BEGIN PRIVATE KEY----- |
| MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWHKCKgY058ahE3t6 |
| vpxVQgzlycgCVMogwjK0y3XMNfWhRANCAATiOnyojN4xS5C8gJ/PHL5cOEsMbsoE |
| Y6KT9xRQSh8lEL4d1Vb36kqUgkpqedEImo0Og4Owk6VWVVR/m4Lk+yUw |
| -----END PRIVATE KEY-----` |
| |
| const rootKeyPEM = `-----BEGIN PRIVATE KEY----- |
| MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgBwND/eHytW0I417J |
| Hr+qcPlp5N1jM3ACXys57bPujg+hRANCAAQmdqXYl1GvY7y3jcTTK6MVXIQr44Tq |
| ChRYI6IeV9tIB6jIsOY+Qol1bk8x/7A5FGOnUWFVLEAPEPSJwPndjolt |
| -----END PRIVATE KEY-----` |
| |
| func mustParseECDSAKey(in string) *ecdsa.PrivateKey { |
| keyBlock, _ := pem.Decode([]byte(in)) |
| if keyBlock == nil || keyBlock.Type != "PRIVATE KEY" { |
| panic("could not decode private key") |
| } |
| key, err := x509.ParsePKCS8PrivateKey(keyBlock.Bytes) |
| if err != nil { |
| panic(err) |
| } |
| return key.(*ecdsa.PrivateKey) |
| } |