frp/test/e2e/pkg/cert/selfsigned.go

170 lines
4.5 KiB
Go

package cert
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"errors"
"fmt"
"math"
"math/big"
"net"
"time"
"k8s.io/client-go/util/cert"
"k8s.io/client-go/util/keyutil"
)
type SelfSignedCertGenerator struct {
caKey []byte
caCert []byte
}
var _ Generator = &SelfSignedCertGenerator{}
// SetCA sets the PEM-encoded CA private key and CA cert for signing the generated serving cert.
func (cp *SelfSignedCertGenerator) SetCA(caKey, caCert []byte) {
cp.caKey = caKey
cp.caCert = caCert
}
// Generate creates and returns a CA certificate, certificate and
// key for the server or client. Key and Cert are used by the server or client
// to establish trust for others, CA certificate is used by the
// client or server to verify the other's authentication chain.
// The cert will be valid for 365 days.
func (cp *SelfSignedCertGenerator) Generate(commonName string) (*Artifacts, error) {
var signingKey *rsa.PrivateKey
var signingCert *x509.Certificate
var valid bool
var err error
valid, signingKey, signingCert = cp.validCACert()
if !valid {
signingKey, err = NewPrivateKey()
if err != nil {
return nil, fmt.Errorf("failed to create the CA private key: %v", err)
}
signingCert, err = cert.NewSelfSignedCACert(cert.Config{CommonName: commonName}, signingKey)
if err != nil {
return nil, fmt.Errorf("failed to create the CA cert: %v", err)
}
}
hostIP := net.ParseIP(commonName)
var altIPs []net.IP
DNSNames := []string{"localhost"}
if hostIP.To4() != nil {
altIPs = append(altIPs, hostIP.To4())
} else {
DNSNames = append(DNSNames, commonName)
}
key, err := NewPrivateKey()
if err != nil {
return nil, fmt.Errorf("failed to create the private key: %v", err)
}
signedCert, err := NewSignedCert(
cert.Config{
CommonName: commonName,
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
AltNames: cert.AltNames{IPs: altIPs, DNSNames: DNSNames},
},
key, signingCert, signingKey,
)
if err != nil {
return nil, fmt.Errorf("failed to create the cert: %v", err)
}
return &Artifacts{
Key: EncodePrivateKeyPEM(key),
Cert: EncodeCertPEM(signedCert),
CAKey: EncodePrivateKeyPEM(signingKey),
CACert: EncodeCertPEM(signingCert),
}, nil
}
func (cp *SelfSignedCertGenerator) validCACert() (bool, *rsa.PrivateKey, *x509.Certificate) {
if !ValidCACert(cp.caKey, cp.caCert, cp.caCert, "",
time.Now().AddDate(1, 0, 0)) {
return false, nil, nil
}
var ok bool
key, err := keyutil.ParsePrivateKeyPEM(cp.caKey)
if err != nil {
return false, nil, nil
}
privateKey, ok := key.(*rsa.PrivateKey)
if !ok {
return false, nil, nil
}
certs, err := cert.ParseCertsPEM(cp.caCert)
if err != nil {
return false, nil, nil
}
if len(certs) != 1 {
return false, nil, nil
}
return true, privateKey, certs[0]
}
// NewPrivateKey creates an RSA private key
func NewPrivateKey() (*rsa.PrivateKey, error) {
return rsa.GenerateKey(rand.Reader, 2048)
}
// NewSignedCert creates a signed certificate using the given CA certificate and key
func NewSignedCert(cfg cert.Config, key crypto.Signer, caCert *x509.Certificate, caKey crypto.Signer) (*x509.Certificate, error) {
serial, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64))
if err != nil {
return nil, err
}
if len(cfg.CommonName) == 0 {
return nil, errors.New("must specify a CommonName")
}
if len(cfg.Usages) == 0 {
return nil, errors.New("must specify at least one ExtKeyUsage")
}
certTmpl := x509.Certificate{
Subject: pkix.Name{
CommonName: cfg.CommonName,
Organization: cfg.Organization,
},
DNSNames: cfg.AltNames.DNSNames,
IPAddresses: cfg.AltNames.IPs,
SerialNumber: serial,
NotBefore: caCert.NotBefore,
NotAfter: time.Now().Add(time.Hour * 24 * 365 * 10).UTC(),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: cfg.Usages,
}
certDERBytes, err := x509.CreateCertificate(rand.Reader, &certTmpl, caCert, key.Public(), caKey)
if err != nil {
return nil, err
}
return x509.ParseCertificate(certDERBytes)
}
// EncodePrivateKeyPEM returns PEM-encoded private key data
func EncodePrivateKeyPEM(key *rsa.PrivateKey) []byte {
block := pem.Block{
Type: keyutil.RSAPrivateKeyBlockType,
Bytes: x509.MarshalPKCS1PrivateKey(key),
}
return pem.EncodeToMemory(&block)
}
// EncodeCertPEM returns PEM-encoded certificate data
func EncodeCertPEM(ct *x509.Certificate) []byte {
block := pem.Block{
Type: cert.CertificateBlockType,
Bytes: ct.Raw,
}
return pem.EncodeToMemory(&block)
}