Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the ability to create ecdsa and ed25519 keys. #51

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 71 additions & 14 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package main
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
Expand All @@ -24,6 +27,12 @@ import (
"time"
)

var (
ed25519Key bool
ecdsaCurve string
rsaBits int
)

func main() {
err := main2()
if err != nil {
Expand All @@ -32,7 +41,7 @@ func main() {
}

type issuer struct {
key crypto.Signer
key interface{}
cert *x509.Certificate
}

Expand All @@ -54,13 +63,14 @@ func getIssuer(keyFile, certFile string) (*issuer, error) {
if err != nil {
return nil, fmt.Errorf("reading private key from %s: %s", keyFile, err)
}
pubKey := publicKey(key)

cert, err := readCert(certContents)
if err != nil {
return nil, fmt.Errorf("reading CA certificate from %s: %s", certFile, err)
}

equal, err := publicKeysEqual(key.Public(), cert.PublicKey)
equal, err := publicKeysEqual(pubKey, cert.PublicKey)
if err != nil {
return nil, fmt.Errorf("comparing public keys: %s", err)
} else if !equal {
Expand All @@ -70,14 +80,14 @@ func getIssuer(keyFile, certFile string) (*issuer, error) {
return &issuer{key, cert}, nil
}

func readPrivateKey(keyContents []byte) (crypto.Signer, error) {
func readPrivateKey(keyContents []byte) (interface{}, error) {
block, _ := pem.Decode(keyContents)
if block == nil {
return nil, fmt.Errorf("no PEM found")
} else if block.Type != "RSA PRIVATE KEY" && block.Type != "ECDSA PRIVATE KEY" {
} else if block.Type != "PRIVATE KEY" {
return nil, fmt.Errorf("incorrect PEM type %s", block.Type)
}
return x509.ParsePKCS1PrivateKey(block.Bytes)
return x509.ParsePKCS8PrivateKey(block.Bytes)
}

func readCert(certContents []byte) (*x509.Certificate, error) {
Expand All @@ -102,22 +112,45 @@ func makeIssuer(keyFile, certFile string) error {
return nil
}

func makeKey(filename string) (*rsa.PrivateKey, error) {
key, err := rsa.GenerateKey(rand.Reader, 2048)
func makeKey(filename string) (interface{}, error) {
var err error
var key crypto.PrivateKey
var der []byte

switch ecdsaCurve {
case "":
if ed25519Key {
_, key, err = ed25519.GenerateKey(rand.Reader)
} else {
key, err = rsa.GenerateKey(rand.Reader, rsaBits)
}
case "P224":
key, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
case "P256":
key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
case "P384":
key, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
case "P521":
key, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
default:
return nil, fmt.Errorf("Unrecognized curve: %q", ecdsaCurve)
}
if err != nil {
return nil, err
}
der := x509.MarshalPKCS1PrivateKey(key)

der, err = x509.MarshalPKCS8PrivateKey(key)
if err != nil {
return nil, err
}

file, err := os.OpenFile(filename, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0600)
if err != nil {
return nil, err
}
defer file.Close()
err = pem.Encode(file, &pem.Block{
Type: "RSA PRIVATE KEY",
Type: "PRIVATE KEY",
Bytes: der,
})
if err != nil {
Expand All @@ -126,12 +159,15 @@ func makeKey(filename string) (*rsa.PrivateKey, error) {
return key, nil
}

func makeRootCert(key crypto.Signer, filename string) (*x509.Certificate, error) {
func makeRootCert(key interface{}, filename string) (*x509.Certificate, error) {
serial, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
if err != nil {
return nil, err
}
skid, err := calculateSKID(key.Public())

pubKey := publicKey(key)

skid, err := calculateSKID(pubKey)
if err != nil {
return nil, err
}
Expand All @@ -152,7 +188,7 @@ func makeRootCert(key crypto.Signer, filename string) (*x509.Certificate, error)
MaxPathLenZero: true,
}

der, err := x509.CreateCertificate(rand.Reader, template, template, key.Public(), key)
der, err := x509.CreateCertificate(rand.Reader, template, template, pubKey, key)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -213,6 +249,18 @@ func calculateSKID(pubKey crypto.PublicKey) ([]byte, error) {
return skid[:], nil
}

func publicKey(privKey interface{}) interface{} {
switch k := privKey.(type) {
case *rsa.PrivateKey:
return &k.PublicKey
case *ecdsa.PrivateKey:
return &k.PublicKey
case ed25519.PrivateKey:
return k.Public().(ed25519.PublicKey)
}
return nil
}

func sign(iss *issuer, domains []string, ipAddresses []string) (*x509.Certificate, error) {
var cn string
if len(domains) > 0 {
Expand All @@ -231,6 +279,7 @@ func sign(iss *issuer, domains []string, ipAddresses []string) (*x509.Certificat
if err != nil {
return nil, err
}
pubKey := publicKey(key)
parsedIPs, err := parseIPs(ipAddresses)
if err != nil {
return nil, err
Expand All @@ -253,12 +302,17 @@ func sign(iss *issuer, domains []string, ipAddresses []string) (*x509.Certificat
// https://derflounder.wordpress.com/2019/06/06/new-tls-security-requirements-for-ios-13-and-macos-catalina-10-15/
NotAfter: time.Now().AddDate(2, 0, 30),

KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
KeyUsage: x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
BasicConstraintsValid: true,
IsCA: false,
}
der, err := x509.CreateCertificate(rand.Reader, template, iss.cert, key.Public(), iss.key)

if !ed25519Key && ecdsaCurve == "" {
template.KeyUsage |= x509.KeyUsageKeyEncipherment
}

der, err := x509.CreateCertificate(rand.Reader, template, iss.cert, pubKey, iss.key)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -289,6 +343,9 @@ func main2() error {
var caCert = flag.String("ca-cert", "minica.pem", "Root certificate filename, PEM encoded.")
var domains = flag.String("domains", "", "Comma separated domain names to include as Server Alternative Names.")
var ipAddresses = flag.String("ip-addresses", "", "Comma separated IP addresses to include as Server Alternative Names.")
flag.BoolVar(&ed25519Key, "ed25519", false, "Generate ED25519 keys")
flag.IntVar(&rsaBits, "rsa-bits", 4096, "RSA key size in bits.")
flag.StringVar(&ecdsaCurve, "ecdsa-curve", "", "ECDSA curve used when generating keys (P224, P256 (recommended), P384, P521).")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
fmt.Fprintf(os.Stderr, `
Expand Down