From 4adb542befdaf27c34a0146ef380679d833cb445 Mon Sep 17 00:00:00 2001 From: Andreas Auernhammer Date: Tue, 30 Apr 2024 07:17:35 +0200 Subject: [PATCH] kms: export `GenerateCertificate` function This commit exposes the `GenerateCertificate` function for clients to use and generate their own certificates from API keys. Callers can customize the certificate using a template. Signed-off-by: Andreas Auernhammer --- kms/client.go | 49 +------------------------------------------ kms/identity.go | 55 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 48 deletions(-) diff --git a/kms/client.go b/kms/client.go index ca590eb..e79bae3 100644 --- a/kms/client.go +++ b/kms/client.go @@ -8,14 +8,9 @@ import ( "bytes" "compress/gzip" "context" - "crypto/rand" "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" "errors" "fmt" - "math/big" "net" "net/http" "net/url" @@ -68,7 +63,7 @@ func NewClient(conf *Config) (*Client, error) { tlsConf := conf.TLS.Clone() if conf.APIKey != nil { - cert, err := generateCertificate(conf.APIKey) + cert, err := GenerateCertificate(conf.APIKey, nil) if err != nil { return nil, err } @@ -1460,45 +1455,3 @@ func httpsURL(endpoint string) string { } return endpoint } - -func generateCertificate(key APIKey) (tls.Certificate, error) { - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) - if err != nil { - return tls.Certificate{}, err - } - - template := x509.Certificate{ - SerialNumber: serialNumber, - Subject: pkix.Name{ - CommonName: key.Identity().String(), - }, - NotBefore: time.Now().UTC(), - NotAfter: time.Now().UTC().Add(90 * 24 * time.Hour), - KeyUsage: x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{ - x509.ExtKeyUsageClientAuth, - }, - BasicConstraintsValid: true, - } - - certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, key.Public(), key.Private()) - if err != nil { - return tls.Certificate{}, err - } - privPKCS8, err := x509.MarshalPKCS8PrivateKey(key.Private()) - if err != nil { - return tls.Certificate{}, err - } - cert, err := tls.X509KeyPair( - pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}), - pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privPKCS8}), - ) - if err != nil { - return tls.Certificate{}, err - } - if cert.Leaf == nil { - cert.Leaf, _ = x509.ParseCertificate(cert.Certificate[0]) - } - return cert, nil -} diff --git a/kms/identity.go b/kms/identity.go index ec3e49a..a11e250 100644 --- a/kms/identity.go +++ b/kms/identity.go @@ -7,14 +7,20 @@ package kms import ( "crypto" "crypto/ed25519" + "crypto/rand" "crypto/sha256" + "crypto/tls" + "crypto/x509" "crypto/x509/pkix" "encoding/asn1" "encoding/base64" + "encoding/pem" "errors" "io" + "math/big" "strconv" "strings" + "time" ) // An Identity uniquely identifies a private/public key pair. @@ -178,6 +184,55 @@ func ParseAPIKey(s string) (APIKey, error) { }, nil } +// GenerateCertificate generates a new self-signed TLS certificate +// from the given template using the APIKey's private and public key. +// +// The template may be nil. In such a case the returned certificate +// is generated using a default template and valid for 90 days. +func GenerateCertificate(key APIKey, template *x509.Certificate) (tls.Certificate, error) { + if template == nil { + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + return tls.Certificate{}, err + } + + template = &x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + CommonName: key.Identity().String(), + }, + NotBefore: time.Now().UTC(), + NotAfter: time.Now().UTC().Add(90 * 24 * time.Hour), + KeyUsage: x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{ + x509.ExtKeyUsageClientAuth, + }, + BasicConstraintsValid: true, + } + } + + certDER, err := x509.CreateCertificate(rand.Reader, template, template, key.Public(), key.Private()) + if err != nil { + return tls.Certificate{}, err + } + privPKCS8, err := x509.MarshalPKCS8PrivateKey(key.Private()) + if err != nil { + return tls.Certificate{}, err + } + cert, err := tls.X509KeyPair( + pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}), + pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privPKCS8}), + ) + if err != nil { + return tls.Certificate{}, err + } + if cert.Leaf == nil { + cert.Leaf, _ = x509.ParseCertificate(cert.Certificate[0]) + } + return cert, nil +} + // apiKey is an APIKey implementation using Ed25519 public/private keys. type apiKey struct { key ed25519.PrivateKey