// FromPemBytes loads a PEM certificate from an in memory byte array and // returns a tls.Certificate. This function is similar to the crypto/tls // X509KeyPair function, however it supports PEM files with the cert and // key combined, as well as password protected keys which are both common with // APNs certificates. // // Use "" as the password argument if the PEM certificate is not password // protected. func FromPemBytes(bytes []byte, password string) (tls.Certificate, error) { var cert tls.Certificate var block *pem.Block for { block, bytes = pem.Decode(bytes) if block == nil { break } if block.Type == "CERTIFICATE" { cert.Certificate = append(cert.Certificate, block.Bytes) } if block.Type == "PRIVATE KEY" || strings.HasSuffix(block.Type, "PRIVATE KEY") { key, err := unencryptPrivateKey(block, password) if err != nil { return tls.Certificate{}, err } cert.PrivateKey = key } } if len(cert.Certificate) == 0 { return tls.Certificate{}, ErrNoCertificate } if cert.PrivateKey == nil { return tls.Certificate{}, ErrNoPrivateKey } if c, e := x509.ParseCertificate(cert.Certificate[0]); e == nil { cert.Leaf = c } return cert, nil }
// GetTLSConfig returns a TLS config generally suitable for client // authentiation. The returned TLS config can be modified slightly // to be made suitable for a server requiring client authentication; // specifically, you should set the value of ClientAuth in the returned // config to match your needs. func (p *ParsedCertBundle) GetTLSConfig(usage TLSUsage) (*tls.Config, error) { tlsCert := tls.Certificate{ Certificate: [][]byte{}, } tlsConfig := &tls.Config{ MinVersion: tls.VersionTLS12, } if p.Certificate != nil { tlsCert.Leaf = p.Certificate } if p.PrivateKey != nil { tlsCert.PrivateKey = p.PrivateKey } if p.CertificateBytes != nil && len(p.CertificateBytes) > 0 { tlsCert.Certificate = append(tlsCert.Certificate, p.CertificateBytes) } if len(p.CAChain) > 0 { for _, cert := range p.CAChain { tlsCert.Certificate = append(tlsCert.Certificate, cert.Bytes) } // Technically we only need one cert, but this doesn't duplicate code certBundle, err := p.ToCertBundle() if err != nil { return nil, fmt.Errorf("Error converting parsed bundle to string bundle when getting TLS config: %s", err) } caPool := x509.NewCertPool() ok := caPool.AppendCertsFromPEM([]byte(certBundle.CAChain[0])) if !ok { return nil, fmt.Errorf("Could not append CA certificate") } if usage&TLSServer > 0 { tlsConfig.ClientCAs = caPool tlsConfig.ClientAuth = tls.VerifyClientCertIfGiven } if usage&TLSClient > 0 { tlsConfig.RootCAs = caPool } } if tlsCert.Certificate != nil && len(tlsCert.Certificate) > 0 { tlsConfig.Certificates = []tls.Certificate{tlsCert} tlsConfig.BuildNameToCertificate() } return tlsConfig, nil }