2022-03-13 01:45:20 +00:00
|
|
|
package certgen
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/rand"
|
|
|
|
"crypto/rsa"
|
2023-06-19 16:12:47 +01:00
|
|
|
"crypto/sha1"
|
2022-03-13 01:45:20 +00:00
|
|
|
"crypto/tls"
|
|
|
|
"crypto/x509"
|
|
|
|
"crypto/x509/pkix"
|
2023-06-19 16:12:47 +01:00
|
|
|
"fmt"
|
2022-03-13 01:45:20 +00:00
|
|
|
"math/big"
|
2022-03-13 10:22:53 +00:00
|
|
|
"net"
|
2022-03-13 01:45:20 +00:00
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2023-06-19 16:12:47 +01:00
|
|
|
// 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()
|
2022-03-13 01:45:20 +00:00
|
|
|
cert := &x509.Certificate{
|
2022-03-13 10:22:53 +00:00
|
|
|
SerialNumber: serialNumber,
|
|
|
|
Subject: name,
|
|
|
|
DNSNames: dnsNames,
|
|
|
|
IPAddresses: ipAddresses,
|
2023-06-19 16:12:47 +01:00
|
|
|
NotBefore: now,
|
|
|
|
NotAfter: future(now),
|
|
|
|
SubjectKeyId: pubKeyHash[:],
|
|
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
2022-03-13 02:24:11 +00:00
|
|
|
KeyUsage: x509.KeyUsageDigitalSignature,
|
2022-03-13 01:45:20 +00:00
|
|
|
}
|
|
|
|
|
2023-06-19 16:12:47 +01:00
|
|
|
// use current certificate as CA if nil
|
2022-03-13 14:09:12 +00:00
|
|
|
if ca == nil {
|
|
|
|
ca = &CertGen{cert: cert, key: serverPrivKey}
|
|
|
|
}
|
2023-06-19 16:12:47 +01:00
|
|
|
|
|
|
|
// create certificate bytes
|
2022-03-13 01:45:20 +00:00
|
|
|
serverBytes, err := x509.CreateCertificate(rand.Reader, cert, ca.cert, serverPrivKey.Public(), ca.key)
|
|
|
|
if err != nil {
|
2023-06-19 16:12:47 +01:00
|
|
|
return nil, fmt.Errorf("failed to generate server certificate bytes: %w", err)
|
2022-03-13 01:45:20 +00:00
|
|
|
}
|
2023-06-19 16:12:47 +01:00
|
|
|
|
|
|
|
// add the raw certificate bytes so `*x509.Certificate.Equal(*x509.Certificate)` is valid
|
|
|
|
cert.Raw = serverBytes
|
|
|
|
|
|
|
|
// get private key bytes
|
2022-03-13 01:45:20 +00:00
|
|
|
privKeyBytes := x509.MarshalPKCS1PrivateKey(serverPrivKey)
|
|
|
|
gen := &CertGen{cert: cert, certBytes: serverBytes, key: serverPrivKey, keyBytes: privKeyBytes}
|
2023-06-19 16:12:47 +01:00
|
|
|
|
|
|
|
// generate pem blocks
|
2022-03-13 01:45:20 +00:00
|
|
|
err = gen.generatePem()
|
|
|
|
if err != nil {
|
2023-06-19 16:12:47 +01:00
|
|
|
return nil, fmt.Errorf("failed to generate PEM encoding: %w", err)
|
2022-03-13 01:45:20 +00:00
|
|
|
}
|
2023-06-19 16:12:47 +01:00
|
|
|
|
|
|
|
// generate key pair
|
2022-03-13 01:45:20 +00:00
|
|
|
caKeyPair, err := tls.X509KeyPair(gen.certPem, gen.keyPem)
|
|
|
|
if err != nil {
|
2023-06-19 16:12:47 +01:00
|
|
|
return nil, fmt.Errorf("failed to generate server key pair: %w", err)
|
2022-03-13 01:45:20 +00:00
|
|
|
}
|
2023-06-19 16:12:47 +01:00
|
|
|
|
2022-03-13 01:45:20 +00:00
|
|
|
gen.tlsCert = caKeyPair
|
|
|
|
return gen, nil
|
|
|
|
}
|