From 394ef779642368001bd9721e11c51647266bf6be Mon Sep 17 00:00:00 2001 From: Thiago Mariano Date: Thu, 30 Mar 2023 11:33:50 -0300 Subject: [PATCH] bug fix 4127. PKCS11 from Fabric 2.2.x don't support MSPs with CA certificates that included RSA public keys Signed-off-by: Thiago Mariano --- bccsp/pkcs11/ecdsakey_test.go | 2 +- bccsp/pkcs11/pkcs11.go | 9 ++++-- bccsp/pkcs11/pkcs11_test.go | 11 +++++++ bccsp/pkcs11/rsa.go | 52 +++++++++++++++++++++++++++++++ bccsp/pkcs11/rsa_test.go | 58 +++++++++++++++++++++++++++++++++++ 5 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 bccsp/pkcs11/rsa.go create mode 100644 bccsp/pkcs11/rsa_test.go diff --git a/bccsp/pkcs11/ecdsakey_test.go b/bccsp/pkcs11/ecdsakey_test.go index df90215f6e8..0d5b41347db 100644 --- a/bccsp/pkcs11/ecdsakey_test.go +++ b/bccsp/pkcs11/ecdsakey_test.go @@ -32,5 +32,5 @@ func TestX509PublicKeyImportOptsKeyImporter(t *testing.T) { cert.PublicKey = "Hello world" _, err = ki.KeyImport(cert, &bccsp.X509PublicKeyImportOpts{}) assert.Error(t, err) - assert.Contains(t, err.Error(), "Certificate's public key type not recognized. Supported keys: [ECDSA]") + assert.Contains(t, err.Error(), "Certificate's public key type not recognized. Supported keys: [ECDSA, RSA]") } diff --git a/bccsp/pkcs11/pkcs11.go b/bccsp/pkcs11/pkcs11.go index 2d3ab885db9..f974101f028 100644 --- a/bccsp/pkcs11/pkcs11.go +++ b/bccsp/pkcs11/pkcs11.go @@ -9,6 +9,7 @@ package pkcs11 import ( "crypto/ecdsa" "crypto/elliptic" + "crypto/rsa" "crypto/sha256" "crypto/x509" "encoding/asn1" @@ -218,11 +219,15 @@ func (csp *impl) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.K pk := x509Cert.PublicKey - switch pk.(type) { + switch pk := pk.(type) { case *ecdsa.PublicKey: return csp.KeyImport(pk, &bccsp.ECDSAGoPublicKeyImportOpts{Temporary: opts.Ephemeral()}) + case *rsa.PublicKey: + // This path only exists to support environments that use RSA certificate + // authorities to issue ECDSA certificates. + return &rsaPublicKey{pubKey: pk}, nil default: - return nil, errors.New("Certificate's public key type not recognized. Supported keys: [ECDSA]") + return nil, errors.New("Certificate's public key type not recognized. Supported keys: [ECDSA, RSA]") } default: diff --git a/bccsp/pkcs11/pkcs11_test.go b/bccsp/pkcs11/pkcs11_test.go index 9d0830e455a..36b06a5914d 100644 --- a/bccsp/pkcs11/pkcs11_test.go +++ b/bccsp/pkcs11/pkcs11_test.go @@ -13,6 +13,7 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" + "crypto/rsa" "crypto/sha256" "crypto/sha512" "crypto/x509" @@ -847,6 +848,16 @@ func TestECDSAKeyImportFromECDSAPublicKey(t *testing.T) { } } +func TestX509RSAKeyImport(t *testing.T) { + pk, err := rsa.GenerateKey(rand.Reader, 2048) + assert.NoError(t, err, "key generation failed") + cert := &x509.Certificate{PublicKey: pk.Public()} + key, err := currentBCCSP.KeyImport(cert, &bccsp.X509PublicKeyImportOpts{Temporary: false}) + assert.NoError(t, err, "key import failed") + assert.NotNil(t, key, "key must not be nil") + assert.Equal(t, &rsaPublicKey{pubKey: &pk.PublicKey}, key) +} + func TestKeyImportFromX509ECDSAPublicKey(t *testing.T) { // Generate an ECDSA key k, err := currentBCCSP.KeyGen(&bccsp.ECDSAKeyGenOpts{Temporary: false}) diff --git a/bccsp/pkcs11/rsa.go b/bccsp/pkcs11/rsa.go new file mode 100644 index 00000000000..22261a4d3be --- /dev/null +++ b/bccsp/pkcs11/rsa.go @@ -0,0 +1,52 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package pkcs11 + +import ( + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "errors" + "fmt" + + "github.com/hyperledger/fabric/bccsp" +) + +// An rsaPublicKey wraps the standard library implementation of an RSA public +// key with functions that satisfy the bccsp.Key interface. +// +// NOTE: Fabric does not support RSA signing or verification. This code simply +// allows MSPs to include RSA CAs in their certificate chains. +type rsaPublicKey struct{ pubKey *rsa.PublicKey } + +func (k *rsaPublicKey) Symmetric() bool { return false } +func (k *rsaPublicKey) Private() bool { return false } +func (k *rsaPublicKey) PublicKey() (bccsp.Key, error) { return k, nil } + +// Bytes converts this key to its serialized representation. +func (k *rsaPublicKey) Bytes() (raw []byte, err error) { + if k.pubKey == nil { + return nil, errors.New("Failed marshalling key. Key is nil.") + } + raw, err = x509.MarshalPKIXPublicKey(k.pubKey) + if err != nil { + return nil, fmt.Errorf("Failed marshalling key [%s]", err) + } + return +} + +// SKI returns the subject key identifier of this key. +func (k *rsaPublicKey) SKI() []byte { + if k.pubKey == nil { + return nil + } + + // Marshal the public key and hash it + raw := x509.MarshalPKCS1PublicKey(k.pubKey) + hash := sha256.Sum256(raw) + return hash[:] +} diff --git a/bccsp/pkcs11/rsa_test.go b/bccsp/pkcs11/rsa_test.go new file mode 100644 index 00000000000..bce8573b2d1 --- /dev/null +++ b/bccsp/pkcs11/rsa_test.go @@ -0,0 +1,58 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package pkcs11 + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/asn1" + "math/big" + "testing" + + "github.com/stretchr/testify/require" +) + +type rsaPublicKeyASN struct { + N *big.Int + E int +} + +func TestRSAPublicKey(t *testing.T) { + lowLevelKey, err := rsa.GenerateKey(rand.Reader, 2048) + require.NoError(t, err) + k := &rsaPublicKey{&lowLevelKey.PublicKey} + + require.False(t, k.Symmetric()) + require.False(t, k.Private()) + + k.pubKey = nil + ski := k.SKI() + require.Nil(t, ski) + + k.pubKey = &lowLevelKey.PublicKey + ski = k.SKI() + raw, err := asn1.Marshal(rsaPublicKeyASN{N: k.pubKey.N, E: k.pubKey.E}) + require.NoError(t, err, "asn1 marshal failed") + hash := sha256.New() + hash.Write(raw) + ski2 := hash.Sum(nil) + require.Equal(t, ski, ski2, "SKI is not computed in the right way.") + + pk, err := k.PublicKey() + require.NoError(t, err) + require.Equal(t, k, pk) + + bytes, err := k.Bytes() + require.NoError(t, err) + bytes2, err := x509.MarshalPKIXPublicKey(k.pubKey) + require.Equal(t, bytes2, bytes, "bytes are not computed in the right way.") + + _, err = (&rsaPublicKey{}).Bytes() + require.EqualError(t, err, "Failed marshalling key. Key is nil.") +}