22

I'm using x/crypto/pkcs12 to load a DER formatted *.p12 file. There is an example in the documentation that uses tls.X509KeyPair to make a tls.Certificate which can be used for an HTTP client.

That's perfect, and works fine. But then I also want to verify that the certificate hasn't expired. The pkcs12 library also has a Decode function which returns an x509 certificate, that I can than use the Verify method on. This also works fine.

It just seems odd to me that I'm decoding the DER twice. Once for an x509.Certificate to verify, and again to get a tls.Certificate. I don't know the relationship between these two Certificate structures, but seeing as the tls package has a function named tls.X509KeyPair that takes some bytes, shouldn't there also be an obvious way to get a tls.Certificate from an x509.Certificate or visa versa? What am I missing?

2 Answers 2

25

A tls.Certificate often stores a certificate chain - in other words, > 1 certificate. Notice its Certificate field is of type [][]byte, where each certificate is a []byte.

The tls package imports the x509 package, so there isn't a function in x509 to get a tls.Certificate; that would cause an import cycle. But if you have an x509.Certificate, you already have a tls.Certificate; just put the x509.Certificate's Raw bytes into a tls.Certificate's Certificate slice.

Sign up to request clarification or add additional context in comments.

7 Comments

Thanks Matt. I'll give it a try. Do I need to do anything with the other fields of tls.Certificate or call any functions, or should it "just work"? I noticed that x509.Certificate.Verify() also returns chains.
TLS servers will need the PrivateKey field set to successfully complete the handshake. I think the rest is optional.
Looks like I need a PrivateKey for clients to. Looking into it. tls: client certificate private key of type <nil> does not implement crypto.Signer
I gave up to soon. There is a response from Wim Lewis with Leaf and ParsedKey set. It appears to be working now (on Go tip at least) groups.google.com/forum/#!msg/golang-nuts/7l75mp2gh1o/…
Wim's Code: thing := tls.Certificate{ Certificate: [][]byte{ parsedCert.Raw }, PrivateKey: parsedPrivateKey, Leaf: parsedCert, }
|
4

you can do like this:

func LoadP12TLSCfg(keystore, password string) (*x509.CertPool, tls.Certificate, error) { data, err := ioutil.ReadFile(keystore) if err != nil { return nil, tls.Certificate{}, err } pk, crt, caCrts, err := pkcs12.DecodeChain(data, password) if err != nil { return nil, tls.Certificate{}, err } pool := x509.NewCertPool() pool.AddCert(caCrts[0]) tlsCrt := tls.Certificate{ Certificate: [][]byte{crt.Raw}, Leaf: crt, PrivateKey: pk, } return pool, tlsCrt, nil } func LoadServerTLSCfg(keystore, password string) (*tls.Config, error) { pool, crt, err := LoadP12TLSCfg(keystore, password) if err != nil { return nil, err } cfg := &tls.Config{ ClientCAs: pool, ClientAuth: tls.RequireAndVerifyClientCert, Certificates: []tls.Certificate{crt}, } return cfg, nil } func LoadClientTLSCfg(keystore, password string, serverName string) (*tls.Config, error) { pool, crt, err := LoadP12TLSCfg(keystore, password) if err != nil { return nil, err } cfg := &tls.Config{ RootCAs: pool, Certificates: []tls.Certificate{crt}, ServerName: serverName, } return cfg, nil } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.