| package libtrust |
| |
| import ( |
| "crypto/rand" |
| "crypto/x509" |
| "crypto/x509/pkix" |
| "encoding/pem" |
| "fmt" |
| "io/ioutil" |
| "math/big" |
| "net" |
| "time" |
| ) |
| |
| type certTemplateInfo struct { |
| commonName string |
| domains []string |
| ipAddresses []net.IP |
| isCA bool |
| clientAuth bool |
| serverAuth bool |
| } |
| |
| func generateCertTemplate(info *certTemplateInfo) *x509.Certificate { |
| // Generate a certificate template which is valid from the past week to |
| // 10 years from now. The usage of the certificate depends on the |
| // specified fields in the given certTempInfo object. |
| var ( |
| keyUsage x509.KeyUsage |
| extKeyUsage []x509.ExtKeyUsage |
| ) |
| |
| if info.isCA { |
| keyUsage = x509.KeyUsageCertSign |
| } |
| |
| if info.clientAuth { |
| extKeyUsage = append(extKeyUsage, x509.ExtKeyUsageClientAuth) |
| } |
| |
| if info.serverAuth { |
| extKeyUsage = append(extKeyUsage, x509.ExtKeyUsageServerAuth) |
| } |
| |
| return &x509.Certificate{ |
| SerialNumber: big.NewInt(0), |
| Subject: pkix.Name{ |
| CommonName: info.commonName, |
| }, |
| NotBefore: time.Now().Add(-time.Hour * 24 * 7), |
| NotAfter: time.Now().Add(time.Hour * 24 * 365 * 10), |
| DNSNames: info.domains, |
| IPAddresses: info.ipAddresses, |
| IsCA: info.isCA, |
| KeyUsage: keyUsage, |
| ExtKeyUsage: extKeyUsage, |
| BasicConstraintsValid: info.isCA, |
| } |
| } |
| |
| func generateCert(pub PublicKey, priv PrivateKey, subInfo, issInfo *certTemplateInfo) (cert *x509.Certificate, err error) { |
| pubCertTemplate := generateCertTemplate(subInfo) |
| privCertTemplate := generateCertTemplate(issInfo) |
| |
| certDER, err := x509.CreateCertificate( |
| rand.Reader, pubCertTemplate, privCertTemplate, |
| pub.CryptoPublicKey(), priv.CryptoPrivateKey(), |
| ) |
| if err != nil { |
| return nil, fmt.Errorf("failed to create certificate: %s", err) |
| } |
| |
| cert, err = x509.ParseCertificate(certDER) |
| if err != nil { |
| return nil, fmt.Errorf("failed to parse certificate: %s", err) |
| } |
| |
| return |
| } |
| |
| // GenerateSelfSignedServerCert creates a self-signed certificate for the |
| // given key which is to be used for TLS servers with the given domains and |
| // IP addresses. |
| func GenerateSelfSignedServerCert(key PrivateKey, domains []string, ipAddresses []net.IP) (*x509.Certificate, error) { |
| info := &certTemplateInfo{ |
| commonName: key.KeyID(), |
| domains: domains, |
| ipAddresses: ipAddresses, |
| serverAuth: true, |
| } |
| |
| return generateCert(key.PublicKey(), key, info, info) |
| } |
| |
| // GenerateSelfSignedClientCert creates a self-signed certificate for the |
| // given key which is to be used for TLS clients. |
| func GenerateSelfSignedClientCert(key PrivateKey) (*x509.Certificate, error) { |
| info := &certTemplateInfo{ |
| commonName: key.KeyID(), |
| clientAuth: true, |
| } |
| |
| return generateCert(key.PublicKey(), key, info, info) |
| } |
| |
| // GenerateCACert creates a certificate which can be used as a trusted |
| // certificate authority. |
| func GenerateCACert(signer PrivateKey, trustedKey PublicKey) (*x509.Certificate, error) { |
| subjectInfo := &certTemplateInfo{ |
| commonName: trustedKey.KeyID(), |
| isCA: true, |
| } |
| issuerInfo := &certTemplateInfo{ |
| commonName: signer.KeyID(), |
| } |
| |
| return generateCert(trustedKey, signer, subjectInfo, issuerInfo) |
| } |
| |
| // GenerateCACertPool creates a certificate authority pool to be used for a |
| // TLS configuration. Any self-signed certificates issued by the specified |
| // trusted keys will be verified during a TLS handshake |
| func GenerateCACertPool(signer PrivateKey, trustedKeys []PublicKey) (*x509.CertPool, error) { |
| certPool := x509.NewCertPool() |
| |
| for _, trustedKey := range trustedKeys { |
| cert, err := GenerateCACert(signer, trustedKey) |
| if err != nil { |
| return nil, fmt.Errorf("failed to generate CA certificate: %s", err) |
| } |
| |
| certPool.AddCert(cert) |
| } |
| |
| return certPool, nil |
| } |
| |
| // LoadCertificateBundle loads certificates from the given file. The file should be pem encoded |
| // containing one or more certificates. The expected pem type is "CERTIFICATE". |
| func LoadCertificateBundle(filename string) ([]*x509.Certificate, error) { |
| b, err := ioutil.ReadFile(filename) |
| if err != nil { |
| return nil, err |
| } |
| certificates := []*x509.Certificate{} |
| var block *pem.Block |
| block, b = pem.Decode(b) |
| for ; block != nil; block, b = pem.Decode(b) { |
| if block.Type == "CERTIFICATE" { |
| cert, err := x509.ParseCertificate(block.Bytes) |
| if err != nil { |
| return nil, err |
| } |
| certificates = append(certificates, cert) |
| } else { |
| return nil, fmt.Errorf("invalid pem block type: %s", block.Type) |
| } |
| } |
| |
| return certificates, nil |
| } |
| |
| // LoadCertificatePool loads a CA pool from the given file. The file should be pem encoded |
| // containing one or more certificates. The expected pem type is "CERTIFICATE". |
| func LoadCertificatePool(filename string) (*x509.CertPool, error) { |
| certs, err := LoadCertificateBundle(filename) |
| if err != nil { |
| return nil, err |
| } |
| pool := x509.NewCertPool() |
| for _, cert := range certs { |
| pool.AddCert(cert) |
| } |
| return pool, nil |
| } |