How to turn an x509.Certificate into a tls.Certificate in Go?
Asked Answered
P

2

21

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?

Promulgate answered 10/12, 2015 at 1:32 Comment(0)
H
24

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.

Hottempered answered 10/12, 2015 at 5:7 Comment(7)
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.Promulgate
TLS servers will need the PrivateKey field set to successfully complete the handshake. I think the rest is optional.Hottempered
Looks like I need a PrivateKey for clients to. Looking into it. tls: client certificate private key of type <nil> does not implement crypto.SignerPromulgate
It looks like there just isn't a good way to do what I want with the current Go crypto APIs. X509KeyPair uses an internal parsePrivateKey function with keyDERBlock bytes which I'm not sure how to get out of the X509. It also does a bunch of PublicKey validation. I'd rather not copy/paste all that out of standard library, so I'm reverting to decoding twice.Promulgate
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/…Promulgate
also got this working with Wim's advice from the google groupMisvalue
Wim's Code: thing := tls.Certificate{ Certificate: [][]byte{ parsedCert.Raw }, PrivateKey: parsedPrivateKey, Leaf: parsedCert, }Michalmichalak
W
2

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
}
Warden answered 6/8, 2021 at 2:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.