Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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() {
privateKey, 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, &privateKey.PublicKey, privateKey)
if err != nil {
log.Fatal(err)
}
if err := writeKeyFile("key.pem", privateKey); err != nil {
log.Fatal(err)
}
if err := writeCertFile("cert.pem", derBytes); err != nil {
log.Fatal(err)
}
}
@mdione-cloudian
Copy link

mdione-cloudian commented Jun 3, 2021

You call a variable root, but it's not a root certificate, it's a private key. This can confuse people who know their beacons (not necessarily their way) around x509. Thanks for the code :)

@gabriel-samfira
Copy link
Author

gabriel-samfira commented Jun 3, 2021

You call a variable root, but it's not a root certificate, it's a private key. This can confuse people who know their beacons (not necessarily their way) around x509. Thanks for the code :)

Indeed! I updated the variable name to correctly describe the type of value it holds. Thanks for that!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment