Skip to content

Instantly share code, notes, and snippets.

@AGWA
Last active December 3, 2015 04:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AGWA/c343e4be85399d65a81b to your computer and use it in GitHub Desktop.
Save AGWA/c343e4be85399d65a81b to your computer and use it in GitHub Desktop.
Go program to add name constraints to a certificate
/*
* Adds name constraints to a certificate. Useful if you need to
* import your organization's private CA into your web browser, but
* you only want to trust it for your organization's domains and not
* the Internet at large.
*
* The certificate is re-signed by an ephemeral issuer with a random
* key so you don't need access to the private key. A random serial number
* is placed in the Issuer DN so browsers don't attempt to verify the
* signature when you import the certificate.
*
* Note that name-constrained roots only work in some web browsers.
* So far, the certificates produced by this program have been tested
* successfully in:
*
* - Firefox 38 ESR on Linux
*
* A PEM-encoded certificate is read from stdin and the new PEM-encoded
* certificate is written to stdout. The domain names to which the
* certificate should be constrained are specified as command line arguments.
*
* Example:
* name_constrain example.com example.org < example-root.crt > example-root-constrained.crt
*
* Written in 2015 by Andrew Ayer <agwa@andrewayer.name>
*
* To the extent possible under law, the author(s) have dedicated all
* copyright and related and neighboring rights to this software to the
* public domain worldwide. This software is distributed without any
* warranty.
*
* You should have received a copy of the CC0 Public
* Domain Dedication along with this software. If not, see
* <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"math/big"
"os"
)
var maxSerialNumber = new(big.Int).Lsh(big.NewInt(1), 128)
func randomSerialNumber() string {
n, err := rand.Int(rand.Reader, maxSerialNumber)
if err != nil {
panic("Random number generator failed: " + err.Error())
}
return n.String()
}
func main() {
crtPem, err := ioutil.ReadAll(os.Stdin)
if err != nil {
fmt.Fprintln(os.Stderr, "Error reading from stdin:", err)
os.Exit(1)
}
pemBlock, _ := pem.Decode(crtPem)
if pemBlock == nil {
fmt.Fprintln(os.Stderr, "Invalid PEM read from stdin")
os.Exit(1)
}
crts, err := x509.ParseCertificates(pemBlock.Bytes)
if err != nil {
fmt.Fprintln(os.Stderr, "Error parsing certificate:", err)
os.Exit(1)
}
crts[0].PermittedDNSDomainsCritical = true
crts[0].PermittedDNSDomains = os.Args[1:]
parent := &x509.Certificate{Subject: crts[0].Issuer}
parent.Subject.OrganizationalUnit = append(parent.Subject.OrganizationalUnit, "Dummy issuer added by name_constrain.go")
parent.Subject.SerialNumber = randomSerialNumber()
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
fmt.Println("Error generating RSA key:", err)
os.Exit(1)
}
newCrtDer, err := x509.CreateCertificate(rand.Reader, crts[0], parent, crts[0].PublicKey, key)
if err != nil {
fmt.Println("Error creating certificate:", err)
os.Exit(1)
}
err = pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE", Bytes: newCrtDer})
if err != nil {
fmt.Println("Error writing certificate to stdout:", err)
os.Exit(1)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment