certgen/server.go

77 lines
2.2 KiB
Go

package certgen
import (
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"math/big"
"net"
"time"
)
// MakeServerTls generates a server TLS certificate using a CA to sign it
// If ca is nil then the server will sign its own certificate
// dnsNames and ipAddresses can be nil if they are not required on the certificate
func MakeServerTls(ca *CertGen, bits int, name pkix.Name, serialNumber *big.Int, future Future, dnsNames []string, ipAddresses []net.IP) (*CertGen, error) {
// generate rsa private key
serverPrivKey, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
return nil, fmt.Errorf("failed to generate server private key: %w", err)
}
// generate SubjectKeyId from sha1 hash of public key bytes
pubKeyBytes := x509.MarshalPKCS1PublicKey(&serverPrivKey.PublicKey)
pubKeyHash := sha1.Sum(pubKeyBytes)
// base certificate data
now := time.Now()
cert := &x509.Certificate{
SerialNumber: serialNumber,
Subject: name,
DNSNames: dnsNames,
IPAddresses: ipAddresses,
NotBefore: now,
NotAfter: future(now),
SubjectKeyId: pubKeyHash[:],
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature,
}
// use current certificate as CA if nil
if ca == nil {
ca = &CertGen{cert: cert, key: serverPrivKey}
}
// create certificate bytes
serverBytes, err := x509.CreateCertificate(rand.Reader, cert, ca.cert, serverPrivKey.Public(), ca.key)
if err != nil {
return nil, fmt.Errorf("failed to generate server certificate bytes: %w", err)
}
// add the raw certificate bytes so `*x509.Certificate.Equal(*x509.Certificate)` is valid
cert.Raw = serverBytes
// get private key bytes
privKeyBytes := x509.MarshalPKCS1PrivateKey(serverPrivKey)
gen := &CertGen{cert: cert, certBytes: serverBytes, key: serverPrivKey, keyBytes: privKeyBytes}
// generate pem blocks
err = gen.generatePem()
if err != nil {
return nil, fmt.Errorf("failed to generate PEM encoding: %w", err)
}
// generate key pair
caKeyPair, err := tls.X509KeyPair(gen.certPem, gen.keyPem)
if err != nil {
return nil, fmt.Errorf("failed to generate server key pair: %w", err)
}
gen.tlsCert = caKeyPair
return gen, nil
}