How do I load a x509 certificate?

2,563 views
Skip to first unread message

Darko Luketic

unread,
Oct 19, 2014, 12:28:02 PM10/19/14
to golan...@googlegroups.com
I have a certificate that was generated with the x509 package, a CA certificate that was signed by another CA certificate that was also generated with the x509 package.
All in 1 go.

open out file
create der with x509.CreateCertificate()
marshall pem with pem.Encode()

the CA certs are valid, also imported in various browsers without complaint

openssl -text also reports parsable

I tried tls.LoadX509KeyPair()
and

func LoadX509KeyPair(certFile, keyFile string) (*x509.Certificate, *rsa.PrivateKey) {
    cf, e := ioutil.ReadFile(certFile)
    if e != nil {
        fmt.Println("cfload:", e.Error())
        os.Exit(1)
    }

    kf, e := ioutil.ReadFile(keyFile)
    if e != nil {
        fmt.Println("kfload:", e.Error())
        os.Exit(1)
    }
    cpb, cr := pem.Decode(cf)
    fmt.Println(string(cr))
    kpb, kr := pem.Decode(kf)
    fmt.Println(string(kr))
    crt, e := x509.ParseCertificate(cpb.Bytes)

    if e != nil {
        fmt.Println("parsex509:", e.Error())
        os.Exit(1)
    }
    key, e := x509.ParsePKCS1PrivateKey(kpb.Bytes)
    if e != nil {
        fmt.Println("parsekey:", e.Error())
        os.Exit(1)
    }
    return crt, key
}


however,

parsex509: asn1: syntax error: data truncated
exit status 1

same with this or tls.LoadX509KeyPair()

Darko Luketic

unread,
Oct 19, 2014, 12:43:48 PM10/19/14
to golan...@googlegroups.com
This is how the certs are generated

package main

import (
    "crypto/rand"
    "crypto/rsa"
    "crypto/sha512"
    "crypto/x509"
    "crypto/x509/pkix"
    "encoding/asn1"
    "encoding/pem"
    "fmt"
    "math/big"
    "net"
    "os"
    "time"
    //"strconv"
)

var (
    organization  = []string{"domain.tld", "My Name"}
    streetaddress = []string{"Street. 123"}
    postalcode    = []string{"12345"}
    province      = []string{"Province"}
    locality      = []string{"City"}
    country       = []string{"DE"}
)

var (
    rcat  = rootCATemplate()
    icat  = intermediateCATemplate()
    rpriv *rsa.PrivateKey
    ipriv *rsa.PrivateKey
)

func main() {
    rootCA()
    intermediateCA()
    serverCert("server1.name", "1.2.3.4", "2a01::2")
    serverCert("server2.name", "2.3.4.5", "2a02::2")
}

func rootCATemplate() x509.Certificate {
    template := x509.Certificate{}
    template.Subject = pkix.Name{
        Organization:  organization,
        StreetAddress: streetaddress,
        PostalCode:    postalcode,
        Province:      province,
        Locality:      locality,
        Country:       country,
        CommonName:    "icod.de CA",
    }

    template.NotBefore = time.Now()
    template.NotAfter = template.NotBefore.Add(172800 * time.Hour)
    template.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCRLSign
    template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}
    template.IsCA = true
    template.BasicConstraintsValid = true
    extSubjectAltName := pkix.Extension{}
    extSubjectAltName.Id = asn1.ObjectIdentifier{2, 5, 29, 17}
    extSubjectAltName.Critical = false
    extSubjectAltName.Value = []byte(`email:in...@dom1.de, URI:http://ca.dom1.de/`)
    template.ExtraExtensions = []pkix.Extension{extSubjectAltName}
    return template
}

func intermediateCATemplate() x509.Certificate {
    template := x509.Certificate{}
    template.Subject = pkix.Name{
        Organization:  organization,
        StreetAddress: streetaddress,
        PostalCode:    postalcode,
        Province:      province,
        Locality:      locality,
        Country:       country,
        CommonName:    "SelfTLS CA",
    }

    template.NotBefore = time.Now()
    template.NotAfter = template.NotBefore.Add(172800 * time.Hour)
    template.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCRLSign
    template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}
    template.IsCA = true
    template.BasicConstraintsValid = true
    extSubjectAltName := pkix.Extension{}
    extSubjectAltName.Id = asn1.ObjectIdentifier{2, 5, 29, 17}
    extSubjectAltName.Critical = false
    extSubjectAltName.Value = []byte(`email:c...@dom2.com, URI:http://ca.dom2.com/`)
    template.ExtraExtensions = []pkix.Extension{extSubjectAltName}
    return template
}

// hosts: []string{"hostname","ipv4addr","ipv6addr"}
func serverTemplate(hosts []string) x509.Certificate {
    template := x509.Certificate{}
    template.Subject = pkix.Name{
        Organization:  organization,
        StreetAddress: streetaddress,
        PostalCode:    postalcode,
        Province:      province,
        Locality:      locality,
        Country:       country,
    }

    template.NotBefore = time.Now()
    template.NotAfter = template.NotBefore.Add(86400 * time.Hour)
    template.KeyUsage = x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature
    template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}
    template.BasicConstraintsValid = true

    for _, h := range hosts {
        if ip := net.ParseIP(h); ip != nil {
            template.IPAddresses = append(template.IPAddresses, ip)
        } else {
            template.DNSNames = append(template.DNSNames, h)
            template.Subject.CommonName = h
        }
    }
    return template
}

func serverCert(host, ipv4, ipv6 string) {
    tpl := serverTemplate([]string{host, ipv4, ipv6})
    priv, err := rsa.GenerateKey(rand.Reader, 4096)
    if err != nil {
        fmt.Println("Failed to generate private key:", err)
        os.Exit(1)
    }
    serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
    tpl.SerialNumber, err = rand.Int(rand.Reader, serialNumberLimit)
    if err != nil {
        fmt.Println("Failed to generate serial number:", err)
        os.Exit(1)
    }
    h := sha512.New()
    pb, e := x509.MarshalPKIXPublicKey(&priv.PublicKey)
    if e != nil {
        fmt.Println(e.Error())
        return
    }
    h.Write(pb)
    tpl.SubjectKeyId = h.Sum(nil)
    derBytes, err := x509.CreateCertificate(rand.Reader, &tpl, &icat, &priv.PublicKey, ipriv)
    if err != nil {
        fmt.Println("Failed to create certificate:", err)
        os.Exit(1)
    }
    certOut, err := os.Create(host + ".crt")
    if err != nil {
        fmt.Println("Failed to open "+host+".crt for writing:", err)
        os.Exit(1)
    }
    pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
    certOut.Close()
    keyOut, err := os.OpenFile(host+".key", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
    if err != nil {
        fmt.Println("failed to open "+host+".key for writing:", err)
        os.Exit(1)
    }
    pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
    keyOut.Close()
}

func intermediateCA() {
    var err error
    ipriv, err = rsa.GenerateKey(rand.Reader, 4096)
    if err != nil {
        fmt.Println("Failed to generate private key:", err)
        os.Exit(1)
    }
    serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
    icat.SerialNumber, err = rand.Int(rand.Reader, serialNumberLimit)
    if err != nil {
        fmt.Println("Failed to generate serial number:", err)
        os.Exit(1)
    }
    h := sha512.New()
    pb, e := x509.MarshalPKIXPublicKey(&ipriv.PublicKey)
    if e != nil {
        fmt.Println(e.Error())
        return
    }
    h.Write(pb)
    icat.SubjectKeyId = h.Sum(nil)

    derBytes, err := x509.CreateCertificate(rand.Reader, &icat, &rcat, &ipriv.PublicKey, rpriv)
    if err != nil {
        fmt.Println("Failed to create certificate:", err)
        os.Exit(1)
    }
    certOut, err := os.Create("intermediate.crt")
    if err != nil {
        fmt.Println("Failed to open ca.pem for writing:", err)
        os.Exit(1)
    }
    pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
    certOut.Close()
    keyOut, err := os.OpenFile("intermediate.key", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
    if err != nil {
        fmt.Println("failed to open ca.key for writing:", err)
        os.Exit(1)
    }
    pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(ipriv)})
    keyOut.Close()
}

func rootCA() {
    var err error
    rpriv, err = rsa.GenerateKey(rand.Reader, 4096)
    if err != nil {
        fmt.Println("Failed to generate private key:", err)
        os.Exit(1)
    }
    serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
    rcat.SerialNumber, err = rand.Int(rand.Reader, serialNumberLimit)
    if err != nil {
        fmt.Println("Failed to generate serial number:", err)
        os.Exit(1)
    }
    h := sha512.New()
    pb, e := x509.MarshalPKIXPublicKey(&rpriv.PublicKey)
    if e != nil {
        fmt.Println(e.Error())
        return
    }
    h.Write(pb)
    rcat.SubjectKeyId = h.Sum(nil)

    derBytes, err := x509.CreateCertificate(rand.Reader, &rcat, &rcat, &rpriv.PublicKey, rpriv)
    if err != nil {
        fmt.Println("Failed to create certificate:", err)
        os.Exit(1)
    }
    certOut, err := os.Create("rootca.crt")
    if err != nil {
        fmt.Println("Failed to open ca.pem for writing:", err)
        os.Exit(1)
    }
    pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
    certOut.Close()
    keyOut, err := os.OpenFile("rootca.key", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
    if err != nil {
        fmt.Println("failed to open ca.key for writing:", err)
        os.Exit(1)
    }
    pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(rpriv)})
    keyOut.Close()
}

Darko Luketic

unread,
Oct 19, 2014, 11:08:22 PM10/19/14
to golan...@googlegroups.com
Alright, the first message is the correct way.

But no matter what I do, a certificate created with Go will always yield the error


asn1: syntax error: data truncated

go version go1.3.3 linux/amd64

So this is either a bug in Go (encoding/asn1 or similar) or I don't know how to properly create a CA certificate or a certificate at all with all the required fields.
openssl created certificates work.

Darko Luketic

unread,
Oct 19, 2014, 11:26:41 PM10/19/14
to golan...@googlegroups.com
this bit is causing the ... asn1 to be truncated

    extSubjectAltName := pkix.Extension{}
    extSubjectAltName.Id = asn1.ObjectIdentifier{2, 5, 29, 17}
    extSubjectAltName.Critical = false
    extSubjectAltName.Value = []byte(`email:c...@dom2.com, URI:http://ca.dom2.com/`)
    template.ExtraExtensions = []pkix.Extension{extSubjectAltName}

I'm currently extending generate_cert.go from the stdlib bit by bit
however when I remove it in my original source it also generates a truncated cert (if I didn't make a mistake by uncommenting the wrong one)
Next suspect is the SubjectKeyId

Darko Luketic

unread,
Oct 19, 2014, 11:42:47 PM10/19/14
to golan...@googlegroups.com
creating a sha512 hash of the public key and setting it as SubjectKeyId works.

So only this bit doesn't work, which leads to the question:
How do I add a custom extension without asn1 errors?

Is it a bug or am I doing something wrong?

sunil....@gmail.com

unread,
Jul 17, 2016, 12:14:13 AM7/17/16
to golang-nuts
Hello, this is 2016 and I am walking on this path now..

In the rootCA function and elsewhere, you have this line:

derBytes, err := x509.CreateCertificate(rand.Reader, &rcat, &rcat, &rpriv.PublicKey, rpriv)

However, the last two parameters to this function, according to the golang refs (https://golang.org/pkg/crypto/x509/#CreateCertificate) are:

"
The parameter pub is the public key of the signee"

and

"
priv is the private key of the signer"

(For quick reference, here is the function signature from the same page: ...)

func CreateCertificate(rand io.Reader, template, parent *Certificate, pub, priv interface{}) (cert []byte, err error)

This does not seem to tally with your usage.

BTW, I much appreciate your work here. I hope perhaps it could be led to correctness slowly and form a great reference?




    extSubjectAltName.Value = []byte(`email...@dom1.de, URI:http://ca.dom1.de/`)

    template.ExtraExtensions = []pkix.Extension{extSubjectAltName}
    return template
}

func intermediateCATemplate() x509.Certificate {
    template := x509.Certificate{}
    template.Subject = pkix.Name{
        Organization:  organization,
        StreetAddress: streetaddress,
        PostalCode:    postalcode,
        Province:      province,
        Locality:      locality,
        Country:       country,
        CommonName:    "SelfTLS CA",
    }

    template.NotBefore = time.Now()
    template.NotAfter = template.NotBefore.Add(172800 * time.Hour)
    template.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCRLSign
    template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}
    template.IsCA = true
    template.BasicConstraintsValid = true
    extSubjectAltName := pkix.Extension{}
    extSubjectAltName.Id = asn1.ObjectIdentifier{2, 5, 29, 17}
    extSubjectAltName.Critical = false
    extSubjectAltName.Value = []byte(`emai...@dom2.com, URI:http://ca.dom2.com/`)

Darko Luketic

unread,
Jul 17, 2016, 6:27:35 AM7/17/16
to golang-nuts, sunil....@gmail.com
The signature obviously changed.

I filed a bug and the resolution was that the contents had to be asn1 encoded.
While Firefox and openssl displayed the correct content of this line the certificate was invalid.

Idk if the bug is still hosted on google code
or if the previous bugs were exported to github
Reply all
Reply to author
Forward
0 new messages