Skip to content

Commit

Permalink
[FAB-7452] Allow embedding cryptoconfig in the Config
Browse files Browse the repository at this point in the history
This change makes it possible to embed the crypto material
from the cryptoconfig and cryptoPaths for the users of an organization
into the SDK's config.yaml.

The apiconfig.OrganizationConfig was updated with a the new field
`Users map[string]UserConfig`, which contains a map of the embedded
users, where `UserConfig` is:

type UserConfig struct {
    Key  string
    Cert string
}

Additionally, the default CredentialManager's constructor
and its method `GetSigningIdentity` were updated to make
use of the embedded users. `GetSigningIdentity` first checks if
an embedded user exists and if not, looks in the supplied cryptoPath.

Lastly, some logic was extracted from the `util.ImportBCCSPKeyFromPEM`
method into the `util.ImportBCCSPKeyFromPEMBytes` method in order
to allow importing of keys directly from a byte array rather than a file path.

Change-Id: I0333637ca03be5243d298158d128eb1fa8e399e0
Signed-off-by: Emil Nikolov <emil.e.nikolov@gmail.com>
  • Loading branch information
e-nikolov committed Dec 17, 2017
1 parent dafcfbc commit 24f9ecc
Show file tree
Hide file tree
Showing 7 changed files with 642 additions and 29 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -13,3 +13,4 @@ vendor/

# Files auto-generated by docker-compose
test/fixtures/fabricca/tls/certs/server/ca.org*.example.com-cert.pem
.idea/
6 changes: 6 additions & 0 deletions api/apiconfig/network.go
Expand Up @@ -91,12 +91,18 @@ type NetworkPeer struct {
type OrganizationConfig struct {
MspID string
CryptoPath string
Users map[string]UserConfig
Peers []string
CertificateAuthorities []string
AdminPrivateKey TLSConfig
SignedCert TLSConfig
}

type UserConfig struct {
Key string
Cert string
}

// OrdererConfig defines an orderer configuration
type OrdererConfig struct {
URL string
Expand Down
23 changes: 17 additions & 6 deletions internal/github.com/hyperledger/fabric-ca/util/csp.go
Expand Up @@ -126,28 +126,39 @@ func ImportBCCSPKeyFromPEM(keyFile string, myCSP apicryptosuite.CryptoSuite, tem
if err != nil {
return nil, err
}
key, err := factory.PEMtoPrivateKey(keyBuff, nil)

key, err := ImportBCCSPKeyFromPEMBytes(keyBuff, myCSP, temporary)

if err != nil {
return nil, errors.WithMessage(err, fmt.Sprintf("Failed parsing private key from %s", keyFile))
}

return key, nil
}

// ImportBCCSPKeyFromPEMBytes attempts to create a private BCCSP key from a pem byte slice
func ImportBCCSPKeyFromPEMBytes(keyBuff []byte, myCSP apicryptosuite.CryptoSuite, temporary bool) (apicryptosuite.Key, error) {
key, err := factory.PEMtoPrivateKey(keyBuff, nil)
if err != nil {
return nil, err
}
switch key.(type) {
case *ecdsa.PrivateKey:
priv, err := factory.PrivateKeyToDER(key.(*ecdsa.PrivateKey))
if err != nil {
return nil, errors.WithMessage(err, fmt.Sprintf("Failed to convert ECDSA private key for '%s'", keyFile))
return nil, errors.WithMessage(err, fmt.Sprintf("Failed to convert ECDSA private key"))
}
sk, err := myCSP.KeyImport(priv, factory.GetECDSAPrivateKeyImportOpts(temporary))
if err != nil {
return nil, errors.WithMessage(err, fmt.Sprintf("Failed to import ECDSA private key for '%s'", keyFile))
return nil, errors.WithMessage(err, fmt.Sprintf("Failed to import ECDSA private key"))
}
return sk, nil
case *rsa.PrivateKey:
return nil, errors.Errorf("Failed to import RSA key from %s; RSA private key import is not supported", keyFile)
return nil, errors.Errorf("Failed to import RSA key: RSA private key import is not supported")
default:
return nil, errors.Errorf("Failed to import key from %s: invalid secret key type", keyFile)
return nil, errors.Errorf("Failed to import key: invalid secret key type")
}
}

// LoadX509KeyPair reads and parses a public/private key pair from a pair
// of files. The files must contain PEM encoded data. The certificate file
// may contain intermediate certificates following the leaf certificate to
Expand Down
31 changes: 31 additions & 0 deletions pkg/config/config_test.go
Expand Up @@ -28,6 +28,7 @@ var org0 = "org0"
var org1 = "Org1"
var configTestFilePath = "../../test/fixtures/config/config_test.yaml"
var configPemTestFilePath = "testdata/config_test_pem.yaml"
var configEmbeddedUsersTestFilePath = "../../test/fixtures/config/config_test_embedded_pems.yaml"
var configType = "yaml"

func TestDefaultConfig(t *testing.T) {
Expand Down Expand Up @@ -837,6 +838,36 @@ O94CDp7l2k7hMQI0zQ==
}
}

func TestLoadConfigWithEmbeddedUsers(t *testing.T) {
// get a config file with embedded users
c, err := InitConfig(configEmbeddedUsersTestFilePath)
if err != nil {
t.Fatal(err)
}

conf, err := c.NetworkConfig()

if err != nil {
t.Fatal(err)
}

if conf.Organizations[strings.ToLower(org1)].Users[strings.ToLower("EmbeddedUser")].Cert == "" {
t.Fatal("Failed to parse the embedded cert for user EmbeddedUser")
}

if conf.Organizations[strings.ToLower(org1)].Users[strings.ToLower("EmbeddedUser")].Key == "" {
t.Fatal("Failed to parse the embedded key for user EmbeddedUser")
}

if conf.Organizations[strings.ToLower(org1)].Users[strings.ToLower("NonExistentEmbeddedUser")].Key != "" {
t.Fatal("Mistakenly found an embedded key for user NonExistentEmbeddedUser")
}

if conf.Organizations[strings.ToLower(org1)].Users[strings.ToLower("NonExistentEmbeddedUser")].Cert != "" {
t.Fatal("Mistakenly found an embedded cert for user NonExistentEmbeddedUser")
}
}

func TestInitConfigFromBytesWrongType(t *testing.T) {
// get a config byte for testing
cBytes, err := loadConfigBytesFromFile(t, configPemTestFilePath)
Expand Down
63 changes: 40 additions & 23 deletions pkg/fabric-client/credentialmgr/credentialmgr.go
Expand Up @@ -25,6 +25,7 @@ var logger = logging.NewLogger("fabric_sdk_go")
// CredentialManager is used for retriving user's signing identity (ecert + private key)
type CredentialManager struct {
orgName string
embeddedUsers map[string]apiconfig.UserConfig
keyDir string
certDir string
config apiconfig.Config
Expand All @@ -47,16 +48,16 @@ func NewCredentialManager(orgName string, config apiconfig.Config, cryptoProvide
return nil, errors.New("org config retrieval failed")
}

if orgConfig.CryptoPath == "" {
return nil, errors.New("CryptoPath is required")
if orgConfig.CryptoPath == "" && len(orgConfig.Users) == 0 {
return nil, errors.New("Either a cryptopath or an embedded list of users is required")
}

orgCryptoPath := orgConfig.CryptoPath
if !filepath.IsAbs(orgCryptoPath) {
orgCryptoPath = filepath.Join(config.CryptoConfigPath(), orgCryptoPath)
}

return &CredentialManager{orgName: orgName, config: config, keyDir: orgCryptoPath + "/keystore", certDir: orgCryptoPath + "/signcerts", cryptoProvider: cryptoProvider}, nil
return &CredentialManager{orgName: orgName, config: config, embeddedUsers: orgConfig.Users, keyDir: orgCryptoPath + "/keystore", certDir: orgCryptoPath + "/signcerts", cryptoProvider: cryptoProvider}, nil
}

// GetSigningIdentity will sign the given object with provided key,
Expand All @@ -66,31 +67,47 @@ func (mgr *CredentialManager) GetSigningIdentity(userName string) (*apifabclient
return nil, errors.New("username is required")
}

privateKeyDir := strings.Replace(mgr.keyDir, "{userName}", userName, -1)
enrollmentCertDir := strings.Replace(mgr.certDir, "{userName}", userName, -1)

privateKeyPath, err := getFirstPathFromDir(privateKeyDir)
if err != nil {
return nil, errors.WithMessage(err, "find private key path failed")
}

enrollmentCertPath, err := getFirstPathFromDir(enrollmentCertDir)
if err != nil {
return nil, errors.WithMessage(err, "find enrollment cert path failed")
}

mspID, err := mgr.config.MspID(mgr.orgName)
if err != nil {
return nil, errors.WithMessage(err, "MSP ID config read failed")
}

privateKey, err := fabricCaUtil.ImportBCCSPKeyFromPEM(privateKeyPath, mgr.cryptoProvider, true)
if err != nil {
return nil, errors.Wrap(err, "import private key failed")
}
enrollmentCert, err := ioutil.ReadFile(enrollmentCertPath)
if err != nil {
return nil, errors.Wrap(err, "reading enrollment cert path failed")
var privateKey apicryptosuite.Key
var enrollmentCert []byte

embeddedCertBytes := []byte(mgr.embeddedUsers[strings.ToLower(userName)].Cert)
embeddedKeyBytes := []byte(mgr.embeddedUsers[strings.ToLower(userName)].Key)

// First check the embedded users and then the paths
if len(embeddedCertBytes) != 0 && len(embeddedKeyBytes) != 0 {
privateKey, err = fabricCaUtil.ImportBCCSPKeyFromPEMBytes(embeddedKeyBytes, mgr.cryptoProvider, true)
if err != nil {
return nil, errors.Wrapf(err, "import private key failed %v", embeddedKeyBytes)
}

enrollmentCert = embeddedCertBytes
} else {
privateKeyDir := strings.Replace(mgr.keyDir, "{userName}", userName, -1)
enrollmentCertDir := strings.Replace(mgr.certDir, "{userName}", userName, -1)

privateKeyPath, err := getFirstPathFromDir(privateKeyDir)
if err != nil {
return nil, errors.WithMessage(err, "find private key path failed")
}

enrollmentCertPath, err := getFirstPathFromDir(enrollmentCertDir)
if err != nil {
return nil, errors.WithMessage(err, "find enrollment cert path failed")
}

privateKey, err = fabricCaUtil.ImportBCCSPKeyFromPEM(privateKeyPath, mgr.cryptoProvider, true)
if err != nil {
return nil, errors.Wrap(err, "import private key failed")
}
enrollmentCert, err = ioutil.ReadFile(enrollmentCertPath)
if err != nil {
return nil, errors.Wrap(err, "reading enrollment cert path failed")
}
}

signingIdentity := &apifabclient.SigningIdentity{MspID: mspID, PrivateKey: privateKey, EnrollmentCert: enrollmentCert}
Expand Down
28 changes: 28 additions & 0 deletions pkg/fabric-client/credentialmgr/credentialmgr_test.go
Expand Up @@ -66,3 +66,31 @@ func TestInvalidOrgCredentialManager(t *testing.T) {
}

}

func TestCredentialManagerFromEmbeddedCryptoConfig(t *testing.T) {
config, err := config.InitConfig("../../../test/fixtures/config/config_test_embedded_pems.yaml")

if err != nil {
t.Fatalf(err.Error())
}

credentialMgr, err := NewCredentialManager("Org1", config, &fcmocks.MockCryptoSuite{})
if err != nil {
t.Fatalf("Failed to setup credential manager: %s", err)
}

_, err = credentialMgr.GetSigningIdentity("")
if err == nil {
t.Fatalf("Should have failed to retrieve signing identity for empty user name")
}

_, err = credentialMgr.GetSigningIdentity("Non-Existent")
if err == nil {
t.Fatalf("Should have failed to retrieve signing identity for non-existent user")
}

_, err = credentialMgr.GetSigningIdentity("EmbeddedUser")
if err != nil {
t.Fatalf("Failed to retrieve signing identity: %+v", err)
}
}

0 comments on commit 24f9ecc

Please sign in to comment.