2
2
Fork 1
gemcert/main.go

199 lines
5.5 KiB
Go
Raw Permalink Normal View History

2020-07-08 19:07:08 +00:00
// Includes some code from src/crypto/tls/generate_cert.go
package main
import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
2020-07-08 19:07:08 +00:00
"crypto/x509"
"crypto/x509/pkix"
"encoding/hex"
2020-07-08 19:07:08 +00:00
"encoding/pem"
"flag"
2020-07-08 22:14:39 +00:00
"fmt"
2020-07-08 19:07:08 +00:00
"log"
"math/big"
"os"
"time"
)
func main() {
// Parse command line arguments
var client bool
var server bool
var ed25519 bool
var nowild bool
2020-07-08 19:07:08 +00:00
var domain string
var cn string
var years int
var months int
var days int
var hours int
flag.BoolVar(&client, "client", false, "generate a client certificate.")
flag.BoolVar(&server, "server", false, "generate a server certificate.")
flag.BoolVar(&ed25519, "ed25519", false, "use ed25519 instead of ECDSA.")
flag.BoolVar(&nowild, "nowild", false, "do not include a wildcard entry in SAN.")
2020-07-08 19:07:08 +00:00
flag.StringVar(&domain, "domain", "example.com", "server domain.")
flag.StringVar(&cn, "cn", "gemini", "client certificate CN.")
2020-07-08 19:07:08 +00:00
flag.IntVar(&years, "years", 0, "years of validity.")
flag.IntVar(&months, "months", 0, "months of validity.")
flag.IntVar(&days, "days", 0, "days of validity.")
flag.IntVar(&hours, "hours", 0, "hours of validity.")
flag.Parse()
// Check validity of supplied arguments
if client && server {
log.Fatal("You can only specify one of -server or -client!")
} else if !client && !server {
log.Fatal("You must specify -server or -client!")
}
2020-07-08 22:14:39 +00:00
// Compute validity dates
2020-07-09 10:42:29 +00:00
if years+months+days+hours == 0 {
2020-07-08 22:14:39 +00:00
if server {
// Default server cert lifespan
years = 5
} else {
2020-08-01 16:53:57 +00:00
// Default client cert lifespan
2020-07-09 10:42:29 +00:00
days = 1
2020-07-08 22:14:39 +00:00
}
}
notBefore := time.Now()
notAfter := notBefore.AddDate(years, months, days)
hoursDuration, _ := time.ParseDuration(fmt.Sprintf("%dh", hours))
notAfter = notAfter.Add(hoursDuration)
2020-07-08 19:07:08 +00:00
// Build certificate template
var template x509.Certificate
if server {
template = getServerCertTemplate(domain, !nowild, notBefore, notAfter)
2020-07-08 19:07:08 +00:00
} else {
2020-07-08 22:14:39 +00:00
template = getClientCertTemplate(cn, notBefore, notAfter)
2020-07-08 19:07:08 +00:00
}
// Generate keys, sign cert and write everything to disk
if ed25519 {
generateEd25519KeyAndCertFromTemplate(template, server)
} else {
generateEcdsaKeyAndCertFromTemplate(template, server)
2020-07-08 19:07:08 +00:00
}
}
func getServerCertTemplate(domain string, wildcard bool, notBefore time.Time, notAfter time.Time) x509.Certificate {
2020-07-09 10:42:29 +00:00
template := getCommonCertTemplate(notBefore, notAfter)
template.Subject = pkix.Name{
CommonName: domain,
2020-07-09 10:42:29 +00:00
}
template.DNSNames = append(template.DNSNames, domain)
if wildcard {
wildcard := "*." + domain
template.DNSNames = append(template.DNSNames, wildcard)
}
2020-07-09 10:42:29 +00:00
return template
2020-07-08 19:07:08 +00:00
}
func getClientCertTemplate(cn string, notBefore time.Time, notAfter time.Time) x509.Certificate {
template := getCommonCertTemplate(notBefore, notAfter)
template.Subject = pkix.Name{
CommonName: cn,
}
return template
2020-07-08 19:07:08 +00:00
}
2020-07-08 22:14:39 +00:00
func getCommonCertTemplate(notBefore time.Time, notAfter time.Time) x509.Certificate {
2020-07-08 19:07:08 +00:00
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: %v", err)
}
template := x509.Certificate{
SerialNumber: serialNumber,
2020-07-09 10:42:29 +00:00
NotBefore: notBefore,
NotAfter: notAfter,
2020-07-08 19:07:08 +00:00
}
return template
}
func generateEcdsaKeyAndCertFromTemplate(template x509.Certificate, isServer bool) {
2020-07-08 19:07:08 +00:00
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
log.Fatal(err)
}
cert, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
if err != nil {
log.Fatal(err)
}
writeAndPrint(priv, cert, isServer)
2020-07-08 19:07:08 +00:00
}
func generateEd25519KeyAndCertFromTemplate(template x509.Certificate, isServer bool) {
2020-07-08 19:07:08 +00:00
var pub ed25519.PublicKey
pub, priv, err := ed25519.GenerateKey(nil)
if err != nil {
log.Fatal(err)
}
cert, err := x509.CreateCertificate(nil, &template, &template, pub, priv)
if err != nil {
log.Fatal(err)
}
writeAndPrint(priv, cert, isServer)
2020-07-08 19:07:08 +00:00
}
func writeAndPrint(privkey interface{}, cert []byte, isServer bool) {
isClient := !isServer
parsedCert, _ := x509.ParseCertificate(cert)
// Derive filenames from domain for server certs
var certFilename, keyFilename string
if isServer {
certFilename = parsedCert.Subject.CommonName + ".crt"
keyFilename = parsedCert.Subject.CommonName + ".key"
} else {
certFilename = "cert.pem"
keyFilename = "key.pem"
}
2020-07-08 19:07:08 +00:00
// Write cert
certOut, err := os.Create(certFilename)
2020-07-08 19:07:08 +00:00
if err != nil {
log.Fatalf("Failed to open certificate file for writing: %v", err)
2020-07-08 19:07:08 +00:00
}
if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: cert}); err != nil {
log.Fatalf("Failed to write data to certificate file: %v", err)
2020-07-08 19:07:08 +00:00
}
if err := certOut.Close(); err != nil {
log.Fatalf("Error closing certificate file: %v", err)
2020-07-08 19:07:08 +00:00
}
log.Printf("wrote %s\n", certFilename)
2020-07-08 19:07:08 +00:00
// Write key
keyOut, err := os.OpenFile(keyFilename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
2020-07-08 19:07:08 +00:00
if err != nil {
log.Fatalf("Failed to open key file for writing: %v", err)
2020-07-08 19:07:08 +00:00
return
}
privBytes, err := x509.MarshalPKCS8PrivateKey(privkey)
if err != nil {
log.Fatalf("Unable to marshal private key: %v", err)
}
if err := pem.Encode(keyOut, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}); err != nil {
log.Fatalf("Failed to write data to key file: %v", err)
2020-07-08 19:07:08 +00:00
}
if err := keyOut.Close(); err != nil {
log.Fatalf("Error closing key file: %v", err)
2020-07-08 19:07:08 +00:00
}
log.Printf("wrote %s\n", keyFilename)
// Print fingerprint of client certs
if isClient {
hash := sha256.Sum256(cert)
fingerprint := hex.EncodeToString(hash[:])
log.Printf("Certificate fingerprint (SHA256): " + fingerprint)
}
2020-07-08 19:07:08 +00:00
}