go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/sdk/certutil/cert_option.go (about) 1 /* 2 3 Copyright (c) 2023 - Present. Will Charczuk. All rights reserved. 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file at the root of the repository. 5 6 */ 7 8 package certutil 9 10 import ( 11 "crypto/rand" 12 "crypto/rsa" 13 "crypto/x509" 14 "math/big" 15 "net" 16 "os" 17 "time" 18 19 "go.charczuk.com/sdk/errutil" 20 ) 21 22 // CertOptions are required arguments when creating certificates. 23 type CertOptions struct { 24 x509.Certificate 25 PrivateKey *rsa.PrivateKey 26 NotBeforeProvider func() time.Time 27 NotAfterProvider func() time.Time 28 } 29 30 // ResolveCertOptions resolves the common create cert options. 31 func ResolveCertOptions(createOptions *CertOptions, options ...CertOption) error { 32 var err error 33 for _, option := range options { 34 if err = option(createOptions); err != nil { 35 return err 36 } 37 } 38 39 if createOptions.PrivateKey == nil { 40 createOptions.PrivateKey, err = rsa.GenerateKey(rand.Reader, 2048) 41 if err != nil { 42 return errutil.New(err) 43 } 44 } 45 46 if createOptions.SerialNumber == nil { 47 serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) 48 createOptions.SerialNumber, err = rand.Int(rand.Reader, serialNumberLimit) 49 if err != nil { 50 return errutil.New(err) 51 } 52 } 53 54 var output CertBundle 55 output.PrivateKey = createOptions.PrivateKey 56 output.PublicKey = &createOptions.PrivateKey.PublicKey 57 58 if createOptions.NotAfter.IsZero() && createOptions.NotAfterProvider != nil { 59 createOptions.NotAfter = createOptions.NotAfterProvider() 60 } 61 if createOptions.NotAfter.IsZero() && createOptions.NotAfterProvider != nil { 62 createOptions.NotAfter = createOptions.NotAfterProvider() 63 } 64 65 return nil 66 } 67 68 // CertOption is an option for creating certs. 69 type CertOption func(*CertOptions) error 70 71 // OptSubjectCommonName sets the subject common name. 72 func OptSubjectCommonName(commonName string) CertOption { 73 return func(csr *CertOptions) error { 74 csr.Subject.CommonName = commonName 75 return nil 76 } 77 } 78 79 // OptSubjectOrganization sets the subject organization names. 80 func OptSubjectOrganization(organization ...string) CertOption { 81 return func(csr *CertOptions) error { 82 csr.Subject.Organization = organization 83 return nil 84 } 85 } 86 87 // OptSubjectOrganizationalUnit sets the subject organization names. 88 func OptSubjectOrganizationalUnit(organizationalUnits ...string) CertOption { 89 return func(csr *CertOptions) error { 90 csr.Subject.OrganizationalUnit = organizationalUnits 91 return nil 92 } 93 } 94 95 // OptSubjectCountry sets the subject country names. 96 func OptSubjectCountry(country ...string) CertOption { 97 return func(csr *CertOptions) error { 98 csr.Subject.Country = country 99 return nil 100 } 101 } 102 103 // OptSubjectProvince sets the subject province names. 104 func OptSubjectProvince(province ...string) CertOption { 105 return func(csr *CertOptions) error { 106 csr.Subject.Province = province 107 return nil 108 } 109 } 110 111 // OptSubjectLocality sets the subject locality names. 112 func OptSubjectLocality(locality ...string) CertOption { 113 return func(csr *CertOptions) error { 114 csr.Subject.Locality = locality 115 return nil 116 } 117 } 118 119 // OptNotAfter sets the not after time. 120 func OptNotAfter(notAfter time.Time) CertOption { 121 return func(csr *CertOptions) error { 122 csr.NotAfter = notAfter 123 return nil 124 } 125 } 126 127 // OptNotBefore sets the not before time. 128 func OptNotBefore(notBefore time.Time) CertOption { 129 return func(csr *CertOptions) error { 130 csr.NotBefore = notBefore 131 return nil 132 } 133 } 134 135 // OptIsCA sets the is certificate authority flag. 136 func OptIsCA(isCA bool) CertOption { 137 return func(csr *CertOptions) error { 138 csr.IsCA = isCA 139 return nil 140 } 141 } 142 143 // OptKeyUsage sets the key usage flags. 144 func OptKeyUsage(keyUsage x509.KeyUsage) CertOption { 145 return func(csr *CertOptions) error { 146 csr.KeyUsage = keyUsage 147 return nil 148 } 149 } 150 151 // OptDNSNames sets valid dns names for the cert. 152 func OptDNSNames(dnsNames ...string) CertOption { 153 return func(csr *CertOptions) error { 154 csr.DNSNames = dnsNames 155 return nil 156 } 157 } 158 159 // OptIPSANs sets valid ip subject alternate names for the cert. 160 func OptIPSANs(ipAddresses ...string) CertOption { 161 return func(csr *CertOptions) error { 162 var parsedAddresses []net.IP 163 for _, rawAddress := range ipAddresses { 164 parsedAddresses = append(parsedAddresses, net.ParseIP(rawAddress)) 165 } 166 csr.IPAddresses = parsedAddresses 167 return nil 168 } 169 } 170 171 // OptAddDNSNames adds valid dns names for the cert. 172 func OptAddDNSNames(dnsNames ...string) CertOption { 173 return func(csr *CertOptions) error { 174 csr.DNSNames = append(csr.DNSNames, dnsNames...) 175 return nil 176 } 177 } 178 179 // OptSerialNumber sets the serial number for the certificate. 180 // If this option isn't provided, a random one is generated. 181 func OptSerialNumber(serialNumber *big.Int) CertOption { 182 return func(cco *CertOptions) error { 183 cco.SerialNumber = serialNumber 184 return nil 185 } 186 } 187 188 // OptPrivateKey sets the private key to use when generating the certificate. 189 // If this option isn't provided, a new one is generated. 190 func OptPrivateKey(privateKey *rsa.PrivateKey) CertOption { 191 return func(cco *CertOptions) error { 192 cco.PrivateKey = privateKey 193 return nil 194 } 195 } 196 197 // OptPrivateKeyFromPath reads a private key from a given path and parses it as PKCS1PrivateKey. 198 func OptPrivateKeyFromPath(path string) CertOption { 199 return func(cco *CertOptions) error { 200 contents, err := os.ReadFile(path) 201 if err != nil { 202 return errutil.New(err) 203 } 204 privateKey, err := x509.ParsePKCS1PrivateKey(contents) 205 if err != nil { 206 return errutil.New(err) 207 } 208 cco.PrivateKey = privateKey 209 return nil 210 } 211 } 212 213 // OptSubjectKeyID sets the subject key id. 214 func OptSubjectKeyID(keyID []byte) CertOption { 215 return func(csr *CertOptions) error { 216 csr.SubjectKeyId = keyID 217 return nil 218 } 219 } 220 221 // OptIssuerCommonName sets the subject common name. 222 func OptIssuerCommonName(commonName string) CertOption { 223 return func(csr *CertOptions) error { 224 csr.Issuer.CommonName = commonName 225 return nil 226 } 227 } 228 229 // OptIssuerOrganization sets the subject organization names. 230 func OptIssuerOrganization(organization ...string) CertOption { 231 return func(csr *CertOptions) error { 232 csr.Issuer.Organization = organization 233 return nil 234 } 235 } 236 237 // OptIssuerOrganization sets the subject organization names. 238 func OptIssuerOrganizationalUnit(organizationalUnits ...string) CertOption { 239 return func(csr *CertOptions) error { 240 csr.Issuer.OrganizationalUnit = organizationalUnits 241 return nil 242 } 243 } 244 245 // OptIssuerCountry sets the subject country names. 246 func OptIssuerCountry(country ...string) CertOption { 247 return func(csr *CertOptions) error { 248 csr.Issuer.Country = country 249 return nil 250 } 251 } 252 253 // OptIssuerProvince sets the subject province names. 254 func OptIssuerProvince(province ...string) CertOption { 255 return func(csr *CertOptions) error { 256 csr.Issuer.Province = province 257 return nil 258 } 259 } 260 261 // OptIssuerLocality sets the subject locality names. 262 func OptIssuerLocality(locality ...string) CertOption { 263 return func(csr *CertOptions) error { 264 csr.Issuer.Locality = locality 265 return nil 266 } 267 }