Go: How do I add an extension (subjectAltName) to a x509.Certificate?

走远了吗. 提交于 2020-05-12 02:51:48

问题


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.



来源:https://stackoverflow.com/questions/26441547/go-how-do-i-add-an-extension-subjectaltname-to-a-x509-certificate

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!