David K. Bainbridge | 215e024 | 2017-09-05 23:18:24 -0700 | [diff] [blame] | 1 | package libtrust |
| 2 | |
| 3 | import ( |
| 4 | "crypto/rand" |
| 5 | "crypto/x509" |
| 6 | "crypto/x509/pkix" |
| 7 | "encoding/pem" |
| 8 | "fmt" |
| 9 | "io/ioutil" |
| 10 | "math/big" |
| 11 | "net" |
| 12 | "time" |
| 13 | ) |
| 14 | |
| 15 | type certTemplateInfo struct { |
| 16 | commonName string |
| 17 | domains []string |
| 18 | ipAddresses []net.IP |
| 19 | isCA bool |
| 20 | clientAuth bool |
| 21 | serverAuth bool |
| 22 | } |
| 23 | |
| 24 | func generateCertTemplate(info *certTemplateInfo) *x509.Certificate { |
| 25 | // Generate a certificate template which is valid from the past week to |
| 26 | // 10 years from now. The usage of the certificate depends on the |
| 27 | // specified fields in the given certTempInfo object. |
| 28 | var ( |
| 29 | keyUsage x509.KeyUsage |
| 30 | extKeyUsage []x509.ExtKeyUsage |
| 31 | ) |
| 32 | |
| 33 | if info.isCA { |
| 34 | keyUsage = x509.KeyUsageCertSign |
| 35 | } |
| 36 | |
| 37 | if info.clientAuth { |
| 38 | extKeyUsage = append(extKeyUsage, x509.ExtKeyUsageClientAuth) |
| 39 | } |
| 40 | |
| 41 | if info.serverAuth { |
| 42 | extKeyUsage = append(extKeyUsage, x509.ExtKeyUsageServerAuth) |
| 43 | } |
| 44 | |
| 45 | return &x509.Certificate{ |
| 46 | SerialNumber: big.NewInt(0), |
| 47 | Subject: pkix.Name{ |
| 48 | CommonName: info.commonName, |
| 49 | }, |
| 50 | NotBefore: time.Now().Add(-time.Hour * 24 * 7), |
| 51 | NotAfter: time.Now().Add(time.Hour * 24 * 365 * 10), |
| 52 | DNSNames: info.domains, |
| 53 | IPAddresses: info.ipAddresses, |
| 54 | IsCA: info.isCA, |
| 55 | KeyUsage: keyUsage, |
| 56 | ExtKeyUsage: extKeyUsage, |
| 57 | BasicConstraintsValid: info.isCA, |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | func generateCert(pub PublicKey, priv PrivateKey, subInfo, issInfo *certTemplateInfo) (cert *x509.Certificate, err error) { |
| 62 | pubCertTemplate := generateCertTemplate(subInfo) |
| 63 | privCertTemplate := generateCertTemplate(issInfo) |
| 64 | |
| 65 | certDER, err := x509.CreateCertificate( |
| 66 | rand.Reader, pubCertTemplate, privCertTemplate, |
| 67 | pub.CryptoPublicKey(), priv.CryptoPrivateKey(), |
| 68 | ) |
| 69 | if err != nil { |
| 70 | return nil, fmt.Errorf("failed to create certificate: %s", err) |
| 71 | } |
| 72 | |
| 73 | cert, err = x509.ParseCertificate(certDER) |
| 74 | if err != nil { |
| 75 | return nil, fmt.Errorf("failed to parse certificate: %s", err) |
| 76 | } |
| 77 | |
| 78 | return |
| 79 | } |
| 80 | |
| 81 | // GenerateSelfSignedServerCert creates a self-signed certificate for the |
| 82 | // given key which is to be used for TLS servers with the given domains and |
| 83 | // IP addresses. |
| 84 | func GenerateSelfSignedServerCert(key PrivateKey, domains []string, ipAddresses []net.IP) (*x509.Certificate, error) { |
| 85 | info := &certTemplateInfo{ |
| 86 | commonName: key.KeyID(), |
| 87 | domains: domains, |
| 88 | ipAddresses: ipAddresses, |
| 89 | serverAuth: true, |
| 90 | } |
| 91 | |
| 92 | return generateCert(key.PublicKey(), key, info, info) |
| 93 | } |
| 94 | |
| 95 | // GenerateSelfSignedClientCert creates a self-signed certificate for the |
| 96 | // given key which is to be used for TLS clients. |
| 97 | func GenerateSelfSignedClientCert(key PrivateKey) (*x509.Certificate, error) { |
| 98 | info := &certTemplateInfo{ |
| 99 | commonName: key.KeyID(), |
| 100 | clientAuth: true, |
| 101 | } |
| 102 | |
| 103 | return generateCert(key.PublicKey(), key, info, info) |
| 104 | } |
| 105 | |
| 106 | // GenerateCACert creates a certificate which can be used as a trusted |
| 107 | // certificate authority. |
| 108 | func GenerateCACert(signer PrivateKey, trustedKey PublicKey) (*x509.Certificate, error) { |
| 109 | subjectInfo := &certTemplateInfo{ |
| 110 | commonName: trustedKey.KeyID(), |
| 111 | isCA: true, |
| 112 | } |
| 113 | issuerInfo := &certTemplateInfo{ |
| 114 | commonName: signer.KeyID(), |
| 115 | } |
| 116 | |
| 117 | return generateCert(trustedKey, signer, subjectInfo, issuerInfo) |
| 118 | } |
| 119 | |
| 120 | // GenerateCACertPool creates a certificate authority pool to be used for a |
| 121 | // TLS configuration. Any self-signed certificates issued by the specified |
| 122 | // trusted keys will be verified during a TLS handshake |
| 123 | func GenerateCACertPool(signer PrivateKey, trustedKeys []PublicKey) (*x509.CertPool, error) { |
| 124 | certPool := x509.NewCertPool() |
| 125 | |
| 126 | for _, trustedKey := range trustedKeys { |
| 127 | cert, err := GenerateCACert(signer, trustedKey) |
| 128 | if err != nil { |
| 129 | return nil, fmt.Errorf("failed to generate CA certificate: %s", err) |
| 130 | } |
| 131 | |
| 132 | certPool.AddCert(cert) |
| 133 | } |
| 134 | |
| 135 | return certPool, nil |
| 136 | } |
| 137 | |
| 138 | // LoadCertificateBundle loads certificates from the given file. The file should be pem encoded |
| 139 | // containing one or more certificates. The expected pem type is "CERTIFICATE". |
| 140 | func LoadCertificateBundle(filename string) ([]*x509.Certificate, error) { |
| 141 | b, err := ioutil.ReadFile(filename) |
| 142 | if err != nil { |
| 143 | return nil, err |
| 144 | } |
| 145 | certificates := []*x509.Certificate{} |
| 146 | var block *pem.Block |
| 147 | block, b = pem.Decode(b) |
| 148 | for ; block != nil; block, b = pem.Decode(b) { |
| 149 | if block.Type == "CERTIFICATE" { |
| 150 | cert, err := x509.ParseCertificate(block.Bytes) |
| 151 | if err != nil { |
| 152 | return nil, err |
| 153 | } |
| 154 | certificates = append(certificates, cert) |
| 155 | } else { |
| 156 | return nil, fmt.Errorf("invalid pem block type: %s", block.Type) |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | return certificates, nil |
| 161 | } |
| 162 | |
| 163 | // LoadCertificatePool loads a CA pool from the given file. The file should be pem encoded |
| 164 | // containing one or more certificates. The expected pem type is "CERTIFICATE". |
| 165 | func LoadCertificatePool(filename string) (*x509.CertPool, error) { |
| 166 | certs, err := LoadCertificateBundle(filename) |
| 167 | if err != nil { |
| 168 | return nil, err |
| 169 | } |
| 170 | pool := x509.NewCertPool() |
| 171 | for _, cert := range certs { |
| 172 | pool.AddCert(cert) |
| 173 | } |
| 174 | return pool, nil |
| 175 | } |