Skip to content

Commit

Permalink
Restore RSA support for x509 public key import
Browse files Browse the repository at this point in the history
While RSA has never been supported by Fabric for transaction signing and
verification, prior to version 2.x, MSPs could include CA certificates
that included RSA public keys. This was broken with the removal of the
unsupported RSA code.

This change modifies the key import function used by the MSP to convert
an RSA public key to a bccsp.Key that can then be used as part of an MSP
identity.

This code does *not* add support for RSA on transaction paths.

Signed-off-by: Matthew Sykes <sykesmat@us.ibm.com>
  • Loading branch information
sykesm authored and denyeart committed Nov 17, 2020
1 parent c545195 commit 52f511d
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 3 deletions.
9 changes: 7 additions & 2 deletions bccsp/sw/keyimport.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package sw

import (
"crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
"errors"
"fmt"
Expand Down Expand Up @@ -123,12 +124,16 @@ func (ki *x509PublicKeyImportOptsKeyImporter) KeyImport(raw interface{}, opts bc

pk := x509Cert.PublicKey

switch pk.(type) {
switch pk := pk.(type) {
case *ecdsa.PublicKey:
return ki.bccsp.KeyImporters[reflect.TypeOf(&bccsp.ECDSAGoPublicKeyImportOpts{})].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]")
}
}
14 changes: 13 additions & 1 deletion bccsp/sw/keyimport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,5 +192,17 @@ func TestX509PublicKeyImportOptsKeyImporter(t *testing.T) {
cert.PublicKey = "Hello world"
_, err = ki.KeyImport(cert, &mocks2.KeyImportOpts{})
require.Error(t, err)
require.Contains(t, err.Error(), "Certificate's public key type not recognized. Supported keys: [ECDSA]")
require.Contains(t, err.Error(), "Certificate's public key type not recognized. Supported keys: [ECDSA, RSA]")
}

func TestX509RSAKeyImport(t *testing.T) {
pk, err := rsa.GenerateKey(rand.Reader, 2048)
require.NoError(t, err, "key generation failed")

cert := &x509.Certificate{PublicKey: pk.Public()}
ki := x509PublicKeyImportOptsKeyImporter{}
key, err := ki.KeyImport(cert, nil)
require.NoError(t, err, "key import failed")
require.NotNil(t, key, "key must not be nil")
require.Equal(t, &rsaPublicKey{pubKey: &pk.PublicKey}, key)
}
52 changes: 52 additions & 0 deletions bccsp/sw/rsa.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package sw

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[:]
}
58 changes: 58 additions & 0 deletions bccsp/sw/rsa_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package sw

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.")
}

0 comments on commit 52f511d

Please sign in to comment.