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  }