Go: How do I add an extension (subjectAltName) to

2020-07-24 03:27发布

问题:

I'm creating a CA cert. And I'd like to add the subjectAltName extension with some values like email or crl or public cert location and so on.

package main

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

func main() {
    template := x509.Certificate{}
    template.Subject = pkix.Name{
        Organization:  []string{"domain.tld", "My Name"},
        StreetAddress: []string{"Whatever. 123"},
        PostalCode:    []string{"12345"},
        Province:      []string{"Redneckville"},
        Locality:      []string{"Woods"},
        Country:       []string{"US"},
        CommonName:    "CA domain my name",
    }

    template.NotBefore = time.Now()
    template.NotAfter = template.NotBefore.Add(87658 * 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
    var e error
    extSubjectAltName.Value, e = asn1.Marshal([]string{`email:mail@domain.tld`, `URI:http://ca.domain.tld/`})
    if e != nil {
        fmt.Println(e.Error())
        return
    }
    template.Extensions = []pkix.Extension{extSubjectAltName}

    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)
    template.SerialNumber, err = rand.Int(rand.Reader, serialNumberLimit)
    if err != nil {
        fmt.Println("Failed to generate serial number:", err)
        os.Exit(1)
    }
    derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
    if err != nil {
        fmt.Println("Failed to create certificate:", err)
        os.Exit(1)
    }
    certOut, err := os.Create("ca.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("ca.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(priv)})
    keyOut.Close()
}

When I do this, the result is

X509v3 extensions:
    X509v3 Key Usage: critical
        Digital Signature, Key Encipherment, Certificate Sign, CRL Sign
    X509v3 Extended Key Usage: 
        TLS Web Client Authentication, TLS Web Server Authentication
    X509v3 Basic Constraints: critical
        CA:TRUE

So but I expect something like

    X509v3 Subject Alternative Name: 
        email:caoperator@disig.sk, URI:http://www.disig.sk/ca

How do I add the extension with those values?

I also tried Value: []byte(``email:my@email.com, URI:http://some.tld/uri``) < double "`" because formatting

回答1:

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

note: template.ExtraExtensions instead of template.Extensions



回答2:

I know this is a late answer, but this question came up for me in a Google search for golang x509 SubjectAltName, so I thought I'd throw in my 2 cents for future Googlers:

According to the x509.Certificate spec, SubjectAltNames should be put in the x509.Certificate's DNSName, EmailAddresses, or IPAddresses properties.

Your example + happy SAN:

package main

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

func main() {
    template := x509.Certificate{}
    template.Subject = pkix.Name{
        Organization:  []string{"domain.tld", "My Name"},
        StreetAddress: []string{"Whatever. 123"},
        PostalCode:    []string{"12345"},
        Province:      []string{"Redneckville"},
        Locality:      []string{"Woods"},
        Country:       []string{"US"},
        CommonName:    "CA domain my name",
    }

    template.NotBefore = time.Now()
    template.NotAfter = template.NotBefore.Add(87658 * 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
    template.DNSNames = []string{"ca.my.domain", "ca"}

    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)
    template.SerialNumber, err = rand.Int(rand.Reader, serialNumberLimit)
    if err != nil {
        fmt.Println("Failed to generate serial number:", err)
        os.Exit(1)
    }
    derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
    if err != nil {
        fmt.Println("Failed to create certificate:", err)
        os.Exit(1)
    }
    certOut, err := os.Create("ca.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("ca.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(priv)})
    keyOut.Close()
}


回答3:

X.509 extensions are ASN.1 DER encoded. Placing an ASCII representation of a SAN extension directly into the binary of the certificate won't work and will truncate the data. This is probably the reason behind the OP's problem.

If you are trying to add any SAN supported by Go, the way to do it is how Cole Brumley specified. This is because Go handles the ASN.1 serialization and this avoids you needing to write any extra code. Look at the section Subject Alternate Name values to see what Go supports as far as SANs: https://godoc.org/crypto/x509#Certificate.

If you are trying to add some SAN type not supported by Go, like a URI, take a look at how the marshalling is done for dns, ip and email using raw values and that's probably going to help you figure it out: https://github.com/golang/go/blob/2a26f5809e4e80e7d8d4e20b9965efb2eefe71c5/src/crypto/x509/x509.go#L1439-L1456. You'll probably need to find out what the corresponding tag is.



标签: go x509