• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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