From e2514705c1dfec7f7a71af9391a588b90695f5e1 Mon Sep 17 00:00:00 2001 From: kkb0318 Date: Thu, 9 May 2024 20:42:55 +0900 Subject: [PATCH] create cert, key --- internal/selfhosted/cert.go | 1 - internal/selfhosted/certificate/create.go | 75 +++++++++++++++++++ .../selfhosted/certificate/create_test.go | 54 +++++++++++++ internal/selfhosted/jwks_test.go | 2 +- internal/selfhosted/selfhosted.go | 10 +-- 5 files changed, 135 insertions(+), 7 deletions(-) delete mode 100644 internal/selfhosted/cert.go create mode 100644 internal/selfhosted/certificate/create.go create mode 100644 internal/selfhosted/certificate/create_test.go diff --git a/internal/selfhosted/cert.go b/internal/selfhosted/cert.go deleted file mode 100644 index 54170da..0000000 --- a/internal/selfhosted/cert.go +++ /dev/null @@ -1 +0,0 @@ -package selfhosted diff --git a/internal/selfhosted/certificate/create.go b/internal/selfhosted/certificate/create.go new file mode 100644 index 0000000..853ddec --- /dev/null +++ b/internal/selfhosted/certificate/create.go @@ -0,0 +1,75 @@ +package certificate + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/base64" + "encoding/pem" + "math/big" + "time" + + "k8s.io/apimachinery/pkg/types" +) + +type TlsCredentials struct { + privateKey []byte + certificate []byte +} + +func (t *TlsCredentials) CaBundle() string { + return base64.StdEncoding.EncodeToString(t.certificate) +} + +func (t *TlsCredentials) Certificate() []byte { + return t.certificate +} + +func (t *TlsCredentials) PrivateKey() []byte { + return t.privateKey +} + +func CreateTlsCredential(serviceNamespacedName types.NamespacedName) (TlsCredentials, error) { + certificatePeriod := 365 // days + + // Generate RSA private key + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return TlsCredentials{}, err + } + + // Define certificate template + template := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: serviceNamespacedName.Name + "." + serviceNamespacedName.Namespace + ".svc", + }, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(0, 0, certificatePeriod), + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + IsCA: true, + } + + // Create the certificate + certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey) + if err != nil { + return TlsCredentials{}, err + } + + // Encode the private key to PEM format + privPemBytes := pem.EncodeToMemory(&pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(privateKey), + }) + + // Encode the certificate to PEM format + certPemBytes := pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: certBytes, + }) + + return TlsCredentials{privateKey: privPemBytes, certificate: certPemBytes}, nil +} diff --git a/internal/selfhosted/certificate/create_test.go b/internal/selfhosted/certificate/create_test.go new file mode 100644 index 0000000..fb1cea6 --- /dev/null +++ b/internal/selfhosted/certificate/create_test.go @@ -0,0 +1,54 @@ +package certificate + +import ( + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "testing" + + "k8s.io/apimachinery/pkg/types" +) + +func TestCreateTlsCredentials(t *testing.T) { + creds, err := CreateTlsCredential(types.NamespacedName{ + Name: "pod-identity-webhook", + Namespace: "kube-system", + }) + if err != nil { + t.Fatalf("Failed to create TLS credentials: %v", err) + } + + certBlock, _ := pem.Decode(creds.certificate) + if certBlock == nil { + t.Fatal("Failed to decode PEM block containing the certificate") + } + cert, err := x509.ParseCertificate(certBlock.Bytes) + if err != nil { + t.Fatalf("Failed to parse certificate: %v", err) + } + + keyBlock, _ := pem.Decode(creds.privateKey) + if keyBlock == nil { + t.Fatal("Failed to decode PEM block containing the private key") + } + key, err := x509.ParsePKCS1PrivateKey(keyBlock.Bytes) + if err != nil { + t.Fatalf("Failed to parse private key: %v", err) + } + + // Verify public keys are equivalent + if !publicKeysEqual(cert.PublicKey, &key.PublicKey) { + t.Fatal("Public key in certificate does not match public key in private key") + } +} + +// Helper function to compare public keys +func publicKeysEqual(pub1, pub2 interface{}) bool { + rsaPub1, ok1 := pub1.(*rsa.PublicKey) + rsaPub2, ok2 := pub2.(*rsa.PublicKey) + + if !ok1 || !ok2 { + return false + } + return rsaPub1.N.Cmp(rsaPub2.N) == 0 && rsaPub1.E == rsaPub2.E +} diff --git a/internal/selfhosted/jwks_test.go b/internal/selfhosted/jwks_test.go index e5bf3bd..b050ac8 100644 --- a/internal/selfhosted/jwks_test.go +++ b/internal/selfhosted/jwks_test.go @@ -22,7 +22,7 @@ func TestJWK(t *testing.T) { expected: rsaKeyID, }, { - name: "rsa", + name: "no rsa", filename: "testdata/ecdsa.pub", expectErr: true, }, diff --git a/internal/selfhosted/selfhosted.go b/internal/selfhosted/selfhosted.go index 2c4ea7f..12f8fd2 100644 --- a/internal/selfhosted/selfhosted.go +++ b/internal/selfhosted/selfhosted.go @@ -2,11 +2,11 @@ package selfhosted import "context" -func Execute(ctx context.Context, factory OIDCIdPFactory, forceUpdate bool) error { - issuerMeta := factory.IssuerMeta() - discovery := factory.IdPDiscovery() - discoveryContents := factory.IdPDiscoveryContents(issuerMeta) - idp, err := factory.IdP(issuerMeta) +func Execute(ctx context.Context, idpComponentsFactory OIDCIdPFactory, forceUpdate bool) error { + issuerMeta := idpComponentsFactory.IssuerMeta() + discovery := idpComponentsFactory.IdPDiscovery() + discoveryContents := idpComponentsFactory.IdPDiscoveryContents(issuerMeta) + idp, err := idpComponentsFactory.IdP(issuerMeta) if err != nil { return err }