Generate a self signed x509 with Microsoft UPN extension
package main | |
import ( | |
"crypto/ecdsa" | |
"crypto/elliptic" | |
"crypto/rand" | |
"crypto/x509" | |
"crypto/x509/pkix" | |
"encoding/asn1" | |
"encoding/pem" | |
"fmt" | |
"log" | |
"math/big" | |
"os" | |
"time" | |
) | |
// UPN type for asn1 encoding. This will hold | |
// our utf-8 encoded string. | |
type UPN struct { | |
A string `asn1:"utf8"` | |
} | |
// OtherName type for asn1 encoding | |
type OtherName struct { | |
OID asn1.ObjectIdentifier | |
Value interface{} `asn1:"tag:0"` | |
} | |
// GeneralNames type for asn1 encoding | |
type GeneralNames struct { | |
OtherName OtherName `asn1:"tag:0"` | |
} | |
func writeKeyFile(path string, key *ecdsa.PrivateKey) error { | |
keyOut, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) | |
if err != nil { | |
return fmt.Errorf("failed to open %s for writing:", path, err) | |
} | |
b, err := x509.MarshalECPrivateKey(key) | |
if err != nil { | |
return err | |
} | |
if err := pem.Encode(keyOut, &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}); err != nil { | |
return fmt.Errorf("failed to write data to %s: %s", path, err) | |
} | |
if err := keyOut.Close(); err != nil { | |
return fmt.Errorf("error closing %s: %s", path, err) | |
} | |
return nil | |
} | |
func writeCertFile(path string, certBytes []byte) error { | |
certOut, err := os.Create(path) | |
if err != nil { | |
return fmt.Errorf("failed to open %s for writing: %s", path, err) | |
} | |
if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: certBytes}); err != nil { | |
return fmt.Errorf("failed to write data to %s: %s", path, err) | |
} | |
if err := certOut.Close(); err != nil { | |
return fmt.Errorf("error closing %s: %s", path, err) | |
} | |
return nil | |
} | |
func main() { | |
root, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) | |
if err != nil { | |
log.Fatal(err) | |
} | |
now := time.Now() | |
notBefore := now.Add(time.Duration(-24) * time.Hour) // 1 day ago. To account for any time skew. | |
notAfter := notBefore.Add(8760 * time.Hour) // 1 year | |
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) | |
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) | |
if err != nil { | |
log.Fatalf("failed to generate serial number: %s", err) | |
} | |
upnExt, err := asn1.Marshal(GeneralNames{ | |
OtherName: OtherName{ | |
// init our ASN.1 object identifier | |
OID: asn1.ObjectIdentifier{ | |
1, 3, 6, 1, 4, 1, 311, 20, 2, 3}, | |
Value: UPN{ | |
A: "johnDoe@example.com", | |
}, | |
}, | |
}) | |
if err != nil { | |
log.Fatal(err) | |
} | |
extSubjectAltName := pkix.Extension{ | |
Id: asn1.ObjectIdentifier{2, 5, 29, 17}, | |
Critical: false, | |
Value: upnExt, | |
} | |
// Define the x509 template | |
certificateTemplate := x509.Certificate{ | |
SerialNumber: serialNumber, | |
Subject: pkix.Name{ | |
CommonName: "johnDoe@example.com", | |
}, | |
NotBefore: notBefore, | |
NotAfter: notAfter, | |
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, | |
// Add subjectAltName | |
ExtraExtensions: []pkix.Extension{extSubjectAltName}, | |
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, | |
BasicConstraintsValid: true, | |
} | |
derBytes, err := x509.CreateCertificate(rand.Reader, &certificateTemplate, &certificateTemplate, &root.PublicKey, root) | |
if err != nil { | |
log.Fatal(err) | |
} | |
if err := writeKeyFile("key.pem", root); err != nil { | |
log.Fatal(err) | |
} | |
if err := writeCertFile("cert.pem", derBytes); err != nil { | |
log.Fatal(err) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment