1// Copyright (C) 2023 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15// Utility methods for generating X.509 certificate chains. 16package certutil 17 18import ( 19 "crypto/ecdsa" 20 "crypto/elliptic" 21 "crypto/rand" 22 "crypto/x509" 23 "crypto/x509/pkix" 24 "encoding/pem" 25 "log" 26 "math/big" 27 "os" 28 "time" 29) 30 31type Entity struct { 32 PrivateKey *ecdsa.PrivateKey 33 Template *x509.Certificate 34 // CA entities only 35 LastSerial *big.Int 36} 37 38var two32 = big.NewInt(1 << 32) 39var one = big.NewInt(1) 40 41func newKey() *ecdsa.PrivateKey { 42 key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 43 if err != nil { 44 log.Fatal(err) 45 } 46 return key 47} 48 49func newTemplate(cn string) *x509.Certificate { 50 notBefore, err := time.Parse(time.RFC3339, "2020-01-01T00:00:00Z") 51 if err != nil { 52 log.Fatal(err) 53 } 54 notAfter, err := time.Parse(time.RFC3339, "2030-01-01T00:00:00Z") 55 if err != nil { 56 log.Fatal(err) 57 } 58 return &x509.Certificate{ 59 NotBefore: notBefore, 60 NotAfter: notAfter, 61 Subject: pkix.Name{CommonName: cn}, 62 } 63} 64 65func NewEntity(name string) *Entity { 66 return &Entity{ 67 PrivateKey: newKey(), 68 Template: newTemplate(name), 69 } 70} 71 72func NewCA(name string) *Entity { 73 ca := NewEntity(name) 74 ca.Template.BasicConstraintsValid = true 75 ca.Template.IsCA = true 76 ca.LastSerial = mustRandInt(two32) 77 return ca 78} 79 80func (e *Entity) publicKey() *ecdsa.PublicKey { 81 return &e.PrivateKey.PublicKey 82} 83 84func (e *Entity) name() string { 85 return e.Template.Subject.String() 86} 87 88func (e *Entity) nextSerial() *big.Int { 89 if e.LastSerial == nil { 90 log.Fatal("Not a CA: " + e.name()) 91 } 92 e.LastSerial = e.LastSerial.Add(e.LastSerial, one) 93 return e.LastSerial 94} 95 96func (ca *Entity) doSign(childTemplate *x509.Certificate, pubKey *ecdsa.PublicKey) []byte { 97 copyTemplate := *childTemplate 98 copyTemplate.SerialNumber = ca.nextSerial() 99 cert, err := x509.CreateCertificate( 100 rand.Reader, 101 ©Template, 102 ca.Template, 103 pubKey, 104 ca.PrivateKey) 105 if err != nil { 106 log.Fatal(err) 107 } 108 return cert 109} 110 111func (ca *Entity) Sign(child *Entity) []byte { 112 return ca.doSign(child.Template, child.publicKey()) 113} 114 115func (ca *Entity) SignWithAlgorithm(child *Entity, algorithm x509.SignatureAlgorithm) []byte { 116 copyTemplate := *child.Template 117 copyTemplate.SignatureAlgorithm = algorithm 118 return ca.doSign(©Template, child.publicKey()) 119} 120 121func (ca *Entity) SignToPEM(child *Entity, filename string) { 122 cert := ca.Sign(child) 123 mustWriteFile(filename+".pem", encodePEM(cert)) 124} 125 126func (ca *Entity) SignWithAlgorithmToPEM(child *Entity, algorithm x509.SignatureAlgorithm, filename string) { 127 cert := ca.SignWithAlgorithm(child, algorithm) 128 mustWriteFile(filename+".pem", encodePEM(cert)) 129} 130 131func encodePEM(b []byte) []byte { 132 return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: b}) 133} 134 135func mustWriteFile(path string, bs []byte) { 136 if err := os.WriteFile(path, bs, 0666); err != nil { 137 log.Fatal(err) 138 } 139} 140 141func mustRandInt(max *big.Int) *big.Int { 142 r, err := rand.Int(rand.Reader, max) 143 if err != nil { 144 log.Fatal(err) 145 } 146 return r 147} 148