From a25a83a10241a8379425db86a29959155f6fecdc Mon Sep 17 00:00:00 2001 From: cviecco Date: Sun, 9 Jun 2024 13:03:05 -0700 Subject: [PATCH] simplicicaiton generation of ca, enhance test to ensure stable subject (#236) * simplicicaiton generation of ca * enhance test to ensure stable subject on ca generation --- lib/certgen/certgen.go | 61 ++++++++++++++++--------------------- lib/certgen/certgen_test.go | 41 ++++++++++++++++++++++--- 2 files changed, 64 insertions(+), 38 deletions(-) diff --git a/lib/certgen/certgen.go b/lib/certgen/certgen.go index b18f289..654312a 100644 --- a/lib/certgen/certgen.go +++ b/lib/certgen/certgen.go @@ -8,7 +8,6 @@ import ( "crypto" "crypto/ecdsa" "crypto/ed25519" - "crypto/elliptic" "crypto/rand" "crypto/rsa" "crypto/sha256" @@ -201,32 +200,6 @@ func GetSignerFromPEMBytes(privateKey []byte) (crypto.Signer, error) { } } -func getSignerHashFromPublic(pub interface{}) (crypto.Hash, error) { - // crypto.SHA256 - switch pub := pub.(type) { - case *rsa.PublicKey: - return crypto.SHA256, nil - case *ecdsa.PublicKey: - switch pub.Curve { - case elliptic.P224(), elliptic.P256(): - return crypto.SHA256, nil - case elliptic.P384(): - return crypto.SHA384, nil - case elliptic.P521(): - return crypto.SHA512, nil - default: - return 0, fmt.Errorf("x509: unknown elliptic curve") - } - //Ed25519 signatures (by default) dont have a prefered signer - case *ed25519.PublicKey, ed25519.PublicKey: - return 0, nil - - default: - return 0, fmt.Errorf("unknown key type") - } - -} - // ValidatePublicKeyStrenght checks if the "strength" of the key is good enough to be considered secure // At this moment it checks for sizes of parameters only. For RSA it means bits>=2041 && exponent>=65537, // For EC curves it means bitsize>=256. ec25519 is considered secure. All other public keys are not @@ -266,6 +239,31 @@ func derBytesCertToCertAndPem(derBytes []byte) (*x509.Certificate, string, error } */ +// On the initial version of keymaster we used the base64 encoding +// of the sha256sum of the rsa signature of the sha256 of the +// common name. This to have a stable, key dependent +// serial number. +// However this was a bad idea as: +// 1. Not all signers can use sha256 +// 2. Not all signatures are stable. +// +// Thus we will keep the rsa behaviour for compatiblity reasons +// But for all other keys we will just return the pkix asn1 encoding +// of the public key +func getKMCompatbileKeyStableBytesForSerial(priv interface{}, commonName []byte) ([]byte, error) { + switch v := priv.(type) { + case *rsa.PrivateKey: + sum := sha256.Sum256(commonName) + return v.Sign(rand.Reader, sum[:], crypto.SHA256) + case *ecdsa.PrivateKey: + return x509.MarshalPKIXPublicKey(v.Public()) + case ed25519.PrivateKey: + return x509.MarshalPKIXPublicKey(v.Public()) + default: + return nil, fmt.Errorf("Type not recognized %T!\n", v) + } +} + // return both an internal representation an the pem representation of the string // As long as the issuer value matches THEN the serial number can be different every time func GenSelfSignedCACert(commonName string, organization string, caPriv crypto.Signer) ([]byte, error) { @@ -278,16 +276,11 @@ func GenSelfSignedCACert(commonName string, organization string, caPriv crypto.S if err != nil { return nil, err } - sum := sha256.Sum256([]byte(commonName)) - hashfunc, err := getSignerHashFromPublic(caPriv.Public()) - if err != nil { - return nil, err - } - signedCN, err := caPriv.Sign(rand.Reader, sum[:], hashfunc) + keyStableBytes, err := getKMCompatbileKeyStableBytesForSerial(caPriv, []byte(commonName)) if err != nil { return nil, err } - sigSum := sha256.Sum256(signedCN) + sigSum := sha256.Sum256(keyStableBytes) sig := base64.StdEncoding.EncodeToString(sigSum[:]) template := x509.Certificate{ SerialNumber: serialNumber, diff --git a/lib/certgen/certgen_test.go b/lib/certgen/certgen_test.go index 84d9ad0..326f5dc 100644 --- a/lib/certgen/certgen_test.go +++ b/lib/certgen/certgen_test.go @@ -1,6 +1,7 @@ package certgen import ( + "bytes" "crypto" "crypto/sha256" "crypto/x509" @@ -215,6 +216,22 @@ AAAECdSciYZnODYp2QC0s838bYh8d2XEOuvBOqcOEA6MUjL8gKfkOxEiNHgbIpVQ+5xYns Df7tMqg18r8iaCVRzqyFAAAAHWN2aWVjY29AY3ZpZWNjby0tTWFjQm9va1BybzE1 -----END OPENSSH PRIVATE KEY-----` +// We should not be using p224 keys except for testing +// openssl ecparam -name secp224r1 -genkey +const testP224Privatekey = `-----BEGIN EC PRIVATE KEY----- +MGgCAQEEHNBN+rQ+YDZ27lRc6tHu5myU+kq8Tetzodw4bfOgBwYFK4EEACGhPAM6 +AARWr9bjMJaYzHyQjD2za224ohGmBg6/6H5pomxWY8fkAfZy/DmjRRCD72pX86xp +PSDtPZDi9/ao4g== +-----END EC PRIVATE KEY-----` + +// The tranformation requires the full information of the private key +// +// openssl ec -in private.ec.key -pubout +const testP224PublicKey = `-----BEGIN PUBLIC KEY----- +ME4wEAYHKoZIzj0CAQYFK4EEACEDOgAEVq/W4zCWmMx8kIw9s2ttuKIRpgYOv+h+ +aaJsVmPH5AH2cvw5o0UQg+9qV/OsaT0g7T2Q4vf2qOI= +-----END PUBLIC KEY-----` + const testDuration = time.Duration(120 * time.Second) // SSSD tests do require some setup... in this case we do some checks to ensure @@ -541,6 +558,8 @@ func TestGenx509CertGoodWithRealm(t *testing.T) { // GenSelfSignedCACert func TestGenSelfSignedCACertGood(t *testing.T) { validPemKeys := []string{testSignerPrivateKey, pkcs8ecPrivateKey, pkcs8Ed25519PrivateKey} + publcKeyPems := []string{testUserPEMPublicKey, testP224PublicKey} + for _, signerPem := range validPemKeys { caPriv, err := GetSignerFromPEMBytes([]byte(signerPem)) if err != nil { @@ -557,16 +576,30 @@ func TestGenSelfSignedCACertGood(t *testing.T) { } t.Logf("got '%s'", pemCert) - // Now we use it to generate a user Cert - userPub, err := getPubKeyFromPem(testUserPEMPublicKey) + derCaCert2, err := GenSelfSignedCACert("some hostname", "some organization", caPriv) if err != nil { t.Fatal(err) } - _, err = GenUserX509Cert("username", userPub, cert, caPriv, nil, - testDuration, nil, nil, nil, testlogger.New(t)) + cacert2, _, err := derBytesCertToCertAndPem(derCaCert2) if err != nil { t.Fatal(err) } + if !bytes.Equal(cert.RawSubject, cacert2.RawSubject) { + t.Fatalf("subjects across generations should match") + } + + // Now we use it to generate a user Cert + for _, publicPem := range publcKeyPems { + userPub, err := getPubKeyFromPem(publicPem) + if err != nil { + t.Fatal(err) + } + _, err = GenUserX509Cert("username", userPub, cert, caPriv, nil, + testDuration, nil, nil, nil, testlogger.New(t)) + if err != nil { + t.Fatal(err) + } + } //t.Logf("got '%s'", certString) }