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