Skip to content

Commit

Permalink
BCCSP ECDSA/RSA/X509 public/private key import
Browse files Browse the repository at this point in the history
This change-set introduces support for importing ecdsa/rsa public keys
in different formats, namely:
ECDSA - PKIX format and as ecdsa.PublicKey instances.
RSA - rsa.PublicKey instances.
X509 - The public key in the X509 certificate (ECDSA or RSA).
This change-set also adds support for importing ecdsa private keys
using DER or PKCS#8 format.
This change-set comes in the context of:
https://jira.hyperledger.org/browse/FAB-354

Change-Id: I6a875f34f3cccf52755aa7617a29c16014e17028
Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
  • Loading branch information
adecaro committed Nov 15, 2016
1 parent 3508592 commit 2f7153f
Show file tree
Hide file tree
Showing 5 changed files with 773 additions and 8 deletions.
113 changes: 113 additions & 0 deletions core/crypto/bccsp/bccsp_opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ limitations under the License.

package bccsp

import (
"crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
)

const (
// ECDSA Elliptic Curve Digital Signature Algorithm (key gen, import, sign, verify),
// at default security level (see primitives package).
Expand All @@ -36,6 +42,9 @@ const (

// SHA Secure Hash Algorithm using default family (see primitives package)
SHA = "SHA"

// X509Certificate Label for X509 certificate realted operation
X509Certificate = "X509Certificate"
)

// ECDSAKeyGenOpts contains options for ECDSA key generation.
Expand All @@ -55,6 +64,64 @@ func (opts *ECDSAKeyGenOpts) Ephemeral() bool {
return opts.Temporary
}

// ECDSAPKIXPublicKeyImportOpts contains options for ECDSA public key importation in PKIX format
type ECDSAPKIXPublicKeyImportOpts struct {
Temporary bool
}

// Algorithm returns an identifier for the algorithm to be used
// to generate a key.
func (opts *ECDSAPKIXPublicKeyImportOpts) Algorithm() string {
return ECDSA
}

// Ephemeral returns true if the key to generate has to be ephemeral,
// false otherwise.
func (opts *ECDSAPKIXPublicKeyImportOpts) Ephemeral() bool {
return opts.Temporary
}

// ECDSAPrivateKeyImportOpts contains options for ECDSA secret key importation in DER format
// or PKCS#8 format.
type ECDSAPrivateKeyImportOpts struct {
Temporary bool
}

// Algorithm returns an identifier for the algorithm to be used
// to generate a key.
func (opts *ECDSAPrivateKeyImportOpts) Algorithm() string {
return ECDSA
}

// Ephemeral returns true if the key to generate has to be ephemeral,
// false otherwise.
func (opts *ECDSAPrivateKeyImportOpts) Ephemeral() bool {
return opts.Temporary
}

// ECDSAGoPublicKeyImportOpts contains options for ECDSA key importation from ecdsa.PublicKey
type ECDSAGoPublicKeyImportOpts struct {
Temporary bool
PK *ecdsa.PublicKey
}

// Algorithm returns an identifier for the algorithm to be used
// to generate a key.
func (opts *ECDSAGoPublicKeyImportOpts) Algorithm() string {
return ECDSA
}

// Ephemeral returns true if the key to generate has to be ephemeral,
// false otherwise.
func (opts *ECDSAGoPublicKeyImportOpts) Ephemeral() bool {
return opts.Temporary
}

// PublicKey returns the ecdsa.PublicKey to be imported.
func (opts *ECDSAGoPublicKeyImportOpts) PublicKey() *ecdsa.PublicKey {
return opts.PK
}

// ECDSAReRandKeyOpts contains options for ECDSA key re-randomization.
type ECDSAReRandKeyOpts struct {
Temporary bool
Expand Down Expand Up @@ -206,3 +273,49 @@ func (opts *RSAKeyGenOpts) Algorithm() string {
func (opts *RSAKeyGenOpts) Ephemeral() bool {
return opts.Temporary
}

// ECDSAGoPublicKeyImportOpts contains options for RSA key importation from rsa.PublicKey
type RSAGoPublicKeyImportOpts struct {
Temporary bool
PK *rsa.PublicKey
}

// Algorithm returns an identifier for the algorithm to be used
// to generate a key.
func (opts *RSAGoPublicKeyImportOpts) Algorithm() string {
return RSA
}

// Ephemeral returns true if the key to generate has to be ephemeral,
// false otherwise.
func (opts *RSAGoPublicKeyImportOpts) Ephemeral() bool {
return opts.Temporary
}

// PublicKey returns the ecdsa.PublicKey to be imported.
func (opts *RSAGoPublicKeyImportOpts) PublicKey() *rsa.PublicKey {
return opts.PK
}

// X509PublicKeyImportOpts contains options for importing public keys from an x509 certificate
type X509PublicKeyImportOpts struct {
Temporary bool
Cert *x509.Certificate
}

// Algorithm returns an identifier for the algorithm to be used
// to generate a key.
func (opts *X509PublicKeyImportOpts) Algorithm() string {
return X509Certificate
}

// Ephemeral returns true if the key to generate has to be ephemeral,
// false otherwise.
func (opts *X509PublicKeyImportOpts) Ephemeral() bool {
return opts.Temporary
}

// PublicKey returns the ecdsa.PublicKey to be imported.
func (opts *X509PublicKeyImportOpts) Certificate() *x509.Certificate {
return opts.Cert
}
4 changes: 2 additions & 2 deletions core/crypto/bccsp/sw/ecdsakey.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type ecdsaPrivateKey struct {
k *ecdsa.PrivateKey
}

// ToByte converts this key to its byte representation,
// Bytes converts this key to its byte representation,
// if this operation is allowed.
func (k *ecdsaPrivateKey) Bytes() (raw []byte, err error) {
return
Expand Down Expand Up @@ -64,7 +64,7 @@ type ecdsaPublicKey struct {
k *ecdsa.PublicKey
}

// ToByte converts this key to its byte representation,
// Bytes converts this key to its byte representation,
// if this operation is allowed.
func (k *ecdsaPublicKey) Bytes() (raw []byte, err error) {
raw, err = x509.MarshalPKIXPublicKey(k.k)
Expand Down
152 changes: 147 additions & 5 deletions core/crypto/bccsp/sw/impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,18 +256,16 @@ func (csp *impl) KeyDeriv(k bccsp.Key, opts bccsp.KeyDerivOpts) (dk bccsp.Key, e
// The opts argument should be appropriate for the primitive used.
func (csp *impl) KeyImport(raw []byte, opts bccsp.KeyImportOpts) (k bccsp.Key, err error) {
// Validate arguments
if len(raw) == 0 {
return nil, errors.New("Invalid raw. Zero length.")
}
if opts == nil {
return nil, errors.New("Invalid Opts parameter. It must not be nil.")
}

switch opts.(type) {

case *bccsp.AES256ImportKeyOpts:

if len(raw) != 32 {
return nil, fmt.Errorf("Invalid Key Length [%d]. Must be 32 bytes", len(raw))
return nil, fmt.Errorf("[AES256ImportKeyOpts] Invalid Key Length [%d]. Must be 32 bytes", len(raw))
}

aesK := &aesPrivateKey{utils.Clone(raw), false}
Expand All @@ -282,8 +280,13 @@ func (csp *impl) KeyImport(raw []byte, opts bccsp.KeyImportOpts) (k bccsp.Key, e
}

return aesK, nil

case *bccsp.HMACImportKeyOpts:

if len(raw) == 0 {
return nil, errors.New("[HMACImportKeyOpts] Invalid raw. It must not be nil.")
}

aesK := &aesPrivateKey{utils.Clone(raw), false}

// If the key is not Ephemeral, store it.
Expand All @@ -296,6 +299,123 @@ func (csp *impl) KeyImport(raw []byte, opts bccsp.KeyImportOpts) (k bccsp.Key, e
}

return aesK, nil

case *bccsp.ECDSAPKIXPublicKeyImportOpts:

if len(raw) == 0 {
return nil, errors.New("[ECDSAPKIXPublicKeyImportOpts] Invalid raw. It must not be nil.")
}

lowLevelKey, err := primitives.DERToPublicKey(raw)
if err != nil {
return nil, fmt.Errorf("Failed converting PKIX to ECDSA public key [%s]", err)
}

ecdsaPK, ok := lowLevelKey.(*ecdsa.PublicKey)
if !ok {
return nil, errors.New("Failed casting to ECDSA public key. Invalid raw material.")
}

k = &ecdsaPublicKey{ecdsaPK}

// If the key is not Ephemeral, store it.
if !opts.Ephemeral() {
// Store the key
err = csp.ks.storePublicKey(hex.EncodeToString(k.SKI()), lowLevelKey)
if err != nil {
return nil, fmt.Errorf("Failed storing ECDSA key [%s]", err)
}
}

return k, nil

case *bccsp.ECDSAPrivateKeyImportOpts:

if len(raw) == 0 {
return nil, errors.New("[ECDSADERPrivateKeyImportOpts] Invalid raw. It must not be nil.")
}

lowLevelKey, err := primitives.DERToPrivateKey(raw)
if err != nil {
return nil, fmt.Errorf("Failed converting PKIX to ECDSA public key [%s]", err)
}

ecdsaSK, ok := lowLevelKey.(*ecdsa.PrivateKey)
if !ok {
return nil, errors.New("Failed casting to ECDSA public key. Invalid raw material.")
}

k = &ecdsaPrivateKey{ecdsaSK}

// If the key is not Ephemeral, store it.
if !opts.Ephemeral() {
// Store the key
err = csp.ks.storePrivateKey(hex.EncodeToString(k.SKI()), lowLevelKey)
if err != nil {
return nil, fmt.Errorf("Failed storing ECDSA key [%s]", err)
}
}

return k, nil

case *bccsp.ECDSAGoPublicKeyImportOpts:

lowLevelKey := opts.(*bccsp.ECDSAGoPublicKeyImportOpts).PublicKey()
if lowLevelKey == nil {
return nil, errors.New("Invalid Opts. ECDSA Public key cannot be nil")
}

k = &ecdsaPublicKey{lowLevelKey}

// If the key is not Ephemeral, store it.
if !opts.Ephemeral() {
// Store the key
err = csp.ks.storePublicKey(hex.EncodeToString(k.SKI()), lowLevelKey)
if err != nil {
return nil, fmt.Errorf("Failed storing ECDSA key [%s]", err)
}
}

return k, nil

case *bccsp.RSAGoPublicKeyImportOpts:

lowLevelKey := opts.(*bccsp.RSAGoPublicKeyImportOpts).PublicKey()
if lowLevelKey == nil {
return nil, errors.New("Invalid Opts. ECDSA Public key cannot be nil")
}

k = &rsaPublicKey{lowLevelKey}

// If the key is not Ephemeral, store it.
if !opts.Ephemeral() {
// Store the key
err = csp.ks.storePublicKey(hex.EncodeToString(k.SKI()), lowLevelKey)
if err != nil {
return nil, fmt.Errorf("Failed storing ECDSA key [%s]", err)
}
}

return k, nil

case *bccsp.X509PublicKeyImportOpts:

x509Cert := opts.(*bccsp.X509PublicKeyImportOpts).Certificate()
if x509Cert == nil {
return nil, errors.New("Invalid Opts. X509 certificate cannot be nil")
}

pk := x509Cert.PublicKey

switch pk.(type) {
case *ecdsa.PublicKey:
return csp.KeyImport(nil, &bccsp.ECDSAGoPublicKeyImportOpts{Temporary: opts.Ephemeral(), PK: pk.(*ecdsa.PublicKey)})
case *rsa.PublicKey:
return csp.KeyImport(nil, &bccsp.RSAGoPublicKeyImportOpts{Temporary: opts.Ephemeral(), PK: pk.(*rsa.PublicKey)})
default:
return nil, errors.New("Certificate public key type not recognized. Supported keys: [ECDSA, RSA]")
}

default:
return nil, errors.New("Import Key Options not recognized")
}
Expand Down Expand Up @@ -407,16 +527,38 @@ func (csp *impl) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.Signer
}

return ecdsa.Verify(&(k.(*ecdsaPrivateKey).k.PublicKey), digest, ecdsaSignature.R, ecdsaSignature.S), nil
case *ecdsaPublicKey:
ecdsaSignature := new(primitives.ECDSASignature)
_, err := asn1.Unmarshal(signature, ecdsaSignature)
if err != nil {
return false, fmt.Errorf("Failed unmashalling signature [%s]", err)
}

return ecdsa.Verify(k.(*ecdsaPublicKey).k, digest, ecdsaSignature.R, ecdsaSignature.S), nil
case *rsaPrivateKey:
if opts == nil {
return false, errors.New("Invalid options. Nil.")
return false, errors.New("Invalid options. It must not be nil.")
}
switch opts.(type) {
case *rsa.PSSOptions:
err := rsa.VerifyPSS(&(k.(*rsaPrivateKey).k.PublicKey),
(opts.(*rsa.PSSOptions)).Hash,
digest, signature, opts.(*rsa.PSSOptions))

return err == nil, err
default:
return false, fmt.Errorf("Opts type not recognized [%s]", opts)
}
case *rsaPublicKey:
if opts == nil {
return false, errors.New("Invalid options. It must not be nil.")
}
switch opts.(type) {
case *rsa.PSSOptions:
err := rsa.VerifyPSS(k.(*rsaPublicKey).k,
(opts.(*rsa.PSSOptions)).Hash,
digest, signature, opts.(*rsa.PSSOptions))

return err == nil, err
default:
return false, fmt.Errorf("Opts type not recognized [%s]", opts)
Expand Down
Loading

0 comments on commit 2f7153f

Please sign in to comment.