Skip to content

Commit

Permalink
[FAB-7765] Use CryptoSuite to load private key/signer
Browse files Browse the repository at this point in the history
Change-Id: I16a3a4fc0275c38d86a85ede90899f0a623151e8
Signed-off-by: Sandra Vrtikapa <sandra.vrtikapa@securekey.com>
  • Loading branch information
sandrask committed Jan 16, 2018
1 parent c06069c commit a03806b
Show file tree
Hide file tree
Showing 10 changed files with 544 additions and 33 deletions.
42 changes: 31 additions & 11 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,13 @@ import (
"github.com/hyperledger/fabric-sdk-go/api/apiconfig"

"github.com/hyperledger/fabric-sdk-go/api/apilogging"
"github.com/hyperledger/fabric-sdk-go/pkg/config/cryptoutil"
"github.com/hyperledger/fabric-sdk-go/pkg/config/urlutil"
"github.com/hyperledger/fabric-sdk-go/pkg/errors"
"github.com/hyperledger/fabric-sdk-go/pkg/logging"
lu "github.com/hyperledger/fabric-sdk-go/pkg/logging/utils"

cs "github.com/hyperledger/fabric-sdk-go/pkg/cryptosuite"
)

var logger = logging.NewLogger(logModule)
Expand Down Expand Up @@ -828,24 +831,41 @@ func (c *Config) TLSClientCerts() ([]tls.Certificate, error) {
}
}

if clientConfig.TLSCerts.Client.KeyPem != "" {
kb = []byte(clientConfig.TLSCerts.Client.KeyPem)
} else if clientConfig.TLSCerts.Client.Keyfile != "" {
kb, err = loadByteKeyOrCertFromFile(&clientConfig, true)
if len(cb) == 0 {
// if no cert found in the config, return empty cert chain
return []tls.Certificate{clientCerts}, nil
}

// Load private key from cert using default crypto suite
cs := cs.GetDefault()
pk, err := cryptoutil.GetPrivateKeyFromCert(cb, cs)

// If CryptoSuite fails to load private key from cert then load private key from config
if err != nil || pk == nil {
logger.Debugf("Reading pk from config, unable to retrieve from cert: %s", err)
if clientConfig.TLSCerts.Client.KeyPem != "" {
kb = []byte(clientConfig.TLSCerts.Client.KeyPem)
} else if clientConfig.TLSCerts.Client.Keyfile != "" {
kb, err = loadByteKeyOrCertFromFile(&clientConfig, true)
if err != nil {
return nil, errors.Wrapf(err, "Failed to load key from file path '%s'", clientConfig.TLSCerts.Client.Keyfile)
}
}

// load the key/cert pair from []byte
clientCerts, err = tls.X509KeyPair(cb, kb)
if err != nil {
return nil, errors.Wrapf(err, "Failed to load key from file path '%s'", clientConfig.TLSCerts.Client.Keyfile)
return nil, errors.Errorf("Error loading cert/key pair as TLS client credentials: %v", err)
}
}

if len(cb) == 0 && len(kb) == 0 {
// if no cert found in the config, return empty cert chain
return []tls.Certificate{clientCerts}, nil

}

// load the key/cert pair from []byte
clientCerts, err = tls.X509KeyPair(cb, kb)
// private key was retrieved from cert
clientCerts, err = cryptoutil.X509KeyPair(cb, pk, cs)
if err != nil {
return nil, errors.Errorf("Error loading cert/key pair as TLS client credentials: %v", err)
return nil, err
}

return []tls.Certificate{clientCerts}, nil
Expand Down
130 changes: 130 additions & 0 deletions pkg/config/cryptoutil/cryptoutils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package cryptoutil

import (
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"io"

"github.com/hyperledger/fabric-sdk-go/api/apicryptosuite"
"github.com/hyperledger/fabric-sdk-go/pkg/errors"
"github.com/hyperledger/fabric-sdk-go/pkg/logging"

factory "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric-ca/sdkpatch/cryptosuitebridge"
)

var logger = logging.NewLogger("fabric_sdk_go")

// GetPrivateKeyFromCert will return private key represented by SKI in cert's public key
func GetPrivateKeyFromCert(cert []byte, cs apicryptosuite.CryptoSuite) (apicryptosuite.Key, error) {

dcert, _ := pem.Decode(cert)
if dcert == nil {
return nil, errors.Errorf("Unable to decode cert bytes [%v]", cert)
}

x509Cert, err := x509.ParseCertificate(dcert.Bytes)
if err != nil {
return nil, errors.Errorf("Unable to parse cert from decoded bytes: %s", err)
}

// get the public key in the right format
certPubK, err := cs.KeyImport(x509Cert, factory.GetX509PublicKeyImportOpts(true))
if err != nil {
return nil, errors.WithMessage(err, "Failed to import certificate's public key")
}

if certPubK == nil || certPubK.SKI() == nil {
return nil, errors.New("Failed to get SKI")
}

// Get the key given the SKI value
key, err := cs.GetKey(certPubK.SKI())
if err != nil {
return nil, errors.WithMessage(err, "Could not find matching key for SKI")
}

if key != nil && key.Private() == false {
return nil, errors.Errorf("Found key is not private, SKI: %s", certPubK.SKI())
}

return key, nil
}

// X509KeyPair will return cer/key pair used for mutual TLS
func X509KeyPair(certPEMBlock []byte, pk apicryptosuite.Key, cs apicryptosuite.CryptoSuite) (tls.Certificate, error) {

fail := func(err error) (tls.Certificate, error) { return tls.Certificate{}, err }

var cert tls.Certificate
for {
var certDERBlock *pem.Block
certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
if certDERBlock == nil {
break
}
if certDERBlock.Type == "CERTIFICATE" {
cert.Certificate = append(cert.Certificate, certDERBlock.Bytes)
} else {
logger.Debugf("Skipping block type: %s", certDERBlock.Type)
}
}

if len(cert.Certificate) == 0 {
return fail(errors.New("No certs available from bytes"))
}

// We are parsing public key for TLS to find its type
x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
if err != nil {
return fail(err)
}

switch x509Cert.PublicKey.(type) {
case *rsa.PublicKey:
cert.PrivateKey = &PrivateKey{cs, pk, &rsa.PublicKey{}}
case *ecdsa.PublicKey:
cert.PrivateKey = &PrivateKey{cs, pk, &ecdsa.PublicKey{}}
default:
return fail(errors.New("tls: unknown public key algorithm"))
}

return cert, nil
}

//PrivateKey is signer implementation for golang client TLS
type PrivateKey struct {
cryptoSuite apicryptosuite.CryptoSuite
key apicryptosuite.Key
publicKey crypto.PublicKey
}

// Public returns the public key corresponding to private key
func (priv *PrivateKey) Public() crypto.PublicKey {
return priv.publicKey
}

// Sign signs msg with priv, reading randomness from rand. If opts is a
// *PSSOptions then the PSS algorithm will be used, otherwise PKCS#1 v1.5 will
// be used. This method is intended to support keys where the private part is
// kept in, for example, a hardware module.
func (priv *PrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ([]byte, error) {
if priv.cryptoSuite == nil {
return nil, errors.New("Crypto suite not set")
}

if priv.key == nil {
return nil, errors.New("Private key not set")
}

return priv.cryptoSuite.Sign(priv.key, msg, opts)
}
Loading

0 comments on commit a03806b

Please sign in to comment.