Skip to content

Commit

Permalink
Merge "[FABG-563] CreateSigningIdentity with cert and key"
Browse files Browse the repository at this point in the history
  • Loading branch information
troyronda authored and Gerrit Code Review committed Aug 24, 2018
2 parents 3e8f41f + 9af76a3 commit cb2a4cf
Show file tree
Hide file tree
Showing 8 changed files with 274 additions and 4 deletions.
6 changes: 6 additions & 0 deletions pkg/client/msp/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,12 @@ func (c *Client) GetSigningIdentity(id string) (mspctx.SigningIdentity, error) {
return si, nil
}

// CreateSigningIdentity creates a signing identity with the given options
func (c *Client) CreateSigningIdentity(opts ...mspctx.SigningIdentityOption) (mspctx.SigningIdentity, error) {
im, _ := c.ctx.IdentityManager(c.orgName)
return im.CreateSigningIdentity(opts...)
}

//prepareOptsFromOptions reads request options from Option array
func (c *Client) prepareOptsFromOptions(ctx context.Client, options ...RequestOption) (requestOptions, error) {
opts := requestOptions{}
Expand Down
4 changes: 4 additions & 0 deletions pkg/client/msp/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,10 @@ func cleanup(storePath string) {
if err != nil {
panic(fmt.Sprintf("Failed to remove dir %s: %s\n", storePath, err))
}
// Recreate the directory only
if err := os.MkdirAll(storePath, os.FileMode(os.ModePerm)); err != nil {
panic(fmt.Sprintf("Failed to recreate dir %s: %s\n", storePath, err))
}
}

func randomUsername() string {
Expand Down
75 changes: 75 additions & 0 deletions pkg/client/msp/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import (
"fmt"

"github.com/cloudflare/cfssl/log"
fabricCaUtil "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric-ca/util"
"github.com/hyperledger/fabric-sdk-go/pkg/common/logging"
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context"
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/msp"
)

func Example() {
Expand Down Expand Up @@ -208,6 +210,79 @@ func ExampleClient_GetSigningIdentity() {
// Output: enroll user is completed
}

func ExampleClient_CreateSigningIdentity() {

ctx := mockClientProvider()

// Create msp client
c, err := New(ctx)
if err != nil {
fmt.Println("failed to create msp client")
return
}

testPrivKey := `-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgp4qKKB0WCEfx7XiB
5Ul+GpjM1P5rqc6RhjD5OkTgl5OhRANCAATyFT0voXX7cA4PPtNstWleaTpwjvbS
J3+tMGTG67f+TdCfDxWYMpQYxLlE8VkbEzKWDwCYvDZRMKCQfv2ErNvb
-----END PRIVATE KEY-----`

testCert := `-----BEGIN CERTIFICATE-----
MIICGTCCAcCgAwIBAgIRALR/1GXtEud5GQL2CZykkOkwCgYIKoZIzj0EAwIwczEL
MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG
cmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh
Lm9yZzEuZXhhbXBsZS5jb20wHhcNMTcwNzI4MTQyNzIwWhcNMjcwNzI2MTQyNzIw
WjBbMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMN
U2FuIEZyYW5jaXNjbzEfMB0GA1UEAwwWVXNlcjFAb3JnMS5leGFtcGxlLmNvbTBZ
MBMGByqGSM49AgEGCCqGSM49AwEHA0IABPIVPS+hdftwDg8+02y1aV5pOnCO9tIn
f60wZMbrt/5N0J8PFZgylBjEuUTxWRsTMpYPAJi8NlEwoJB+/YSs29ujTTBLMA4G
A1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMCsGA1UdIwQkMCKAIIeR0TY+iVFf
mvoEKwaToscEu43ZXSj5fTVJornjxDUtMAoGCCqGSM49BAMCA0cAMEQCID+dZ7H5
AiaiI2BjxnL3/TetJ8iFJYZyWvK//an13WV/AiARBJd/pI5A7KZgQxJhXmmR8bie
XdsmTcdRvJ3TS/6HCA==
-----END CERTIFICATE-----`

// Create signing identity based on certificate and private key
id, err := c.CreateSigningIdentity(msp.WithCert([]byte(testCert)), msp.WithPrivateKey([]byte(testPrivKey)))
if err != nil {
fmt.Printf("failed when creating identity based on certificate and private key: %s\n", err)
return
}
if string(id.EnrollmentCertificate()) != testCert {
fmt.Printf("certificate mismatch\n")
return
}

// In this user case client might want to import keys directly into keystore
// out of band instead of enrolling the user via SDK. User enrolment creates a cert
// and stores it into local SDK user store, while user might not want SDK to manage certs.
err = importPrivateKeyOutOfBand([]byte(testPrivKey), c)
if err != nil {
fmt.Printf("failed to import key: %s\n", err)
return
}

// Create signing identity using certificate. SDK will lookup the private key based on the certificate.
id, err = c.CreateSigningIdentity(msp.WithCert([]byte(testCert)))
if err != nil {
fmt.Printf("failed when creating identity using certificate: %s\n", err)
return
}
if string(id.EnrollmentCertificate()) != testCert {
fmt.Printf("certificate mismatch\n")
return
}

fmt.Println("create signing identity is completed")

// Output: create signing identity is completed
}

func importPrivateKeyOutOfBand(privateKey []byte, c *Client) error {
_, err := fabricCaUtil.ImportBCCSPKeyFromPEMBytes([]byte(privateKey), c.ctx.CryptoSuite(), false)
return err
}

func ExampleClient_Revoke() {

ctx := mockClientProvider()
Expand Down
1 change: 1 addition & 0 deletions pkg/client/msp/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ var (
// IdentityManager provides management of identities in a Fabric network
type IdentityManager interface {
GetSigningIdentity(name string) (msp.SigningIdentity, error)
CreateSigningIdentity(ops ...msp.SigningIdentityOption) (msp.SigningIdentity, error)
}
28 changes: 28 additions & 0 deletions pkg/common/providers/msp/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,37 @@ var (
ErrUserNotFound = errors.New("user not found")
)

// IdentityOption captures options used for creating a new SigningIdentity instance
type IdentityOption struct {
Cert []byte
PrivateKey []byte
}

// SigningIdentityOption describes a functional parameter for creating a new SigningIdentity instance
type SigningIdentityOption func(*IdentityOption) error

// WithPrivateKey can be passed as an option when creating a new SigningIdentity.
// It cannot be used without the WithCert option
func WithPrivateKey(key []byte) SigningIdentityOption {
return func(o *IdentityOption) error {
o.PrivateKey = key
return nil
}
}

// WithCert can be passed as an option when creating a new SigningIdentity.
// When used alone, SDK will lookup the corresponding private key.
func WithCert(cert []byte) SigningIdentityOption {
return func(o *IdentityOption) error {
o.Cert = cert
return nil
}
}

// IdentityManager provides management of identities in Fabric network
type IdentityManager interface {
GetSigningIdentity(name string) (SigningIdentity, error)
CreateSigningIdentity(ops ...SigningIdentityOption) (SigningIdentity, error)
}

// Identity represents a Fabric client identity
Expand Down
5 changes: 5 additions & 0 deletions pkg/fab/mocks/mockidentitymgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,8 @@ func (mgr *MockIdentityManager) GetSigningIdentity(id string) (msp.SigningIdenti
}
return si, nil
}

// CreateSigningIdentity creates a signing identity with the given options
func (mgr *MockIdentityManager) CreateSigningIdentity(opts ...msp.SigningIdentityOption) (msp.SigningIdentity, error) {
return nil, errors.New("not implemented")
}
36 changes: 36 additions & 0 deletions pkg/msp/getsigid.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,42 @@ func (mgr *IdentityManager) GetSigningIdentity(id string) (msp.SigningIdentity,
return user, nil
}

// CreateSigningIdentity creates a signing identity with the given options
func (mgr *IdentityManager) CreateSigningIdentity(opts ...msp.SigningIdentityOption) (msp.SigningIdentity, error) {
opt := msp.IdentityOption{}
for _, param := range opts {
err := param(&opt)
if err != nil {
return nil, errors.WithMessage(err, "failed to create identity")
}
}
if opt.Cert == nil {
return nil, errors.New("missing certificate")
}
var privateKey core.Key
if opt.PrivateKey == nil {
pubKey, err := cryptoutil.GetPublicKeyFromCert(opt.Cert, mgr.cryptoSuite)
if err != nil {
return nil, errors.WithMessage(err, "fetching public key from cert failed")
}
privateKey, err = mgr.cryptoSuite.GetKey(pubKey.SKI())
if err != nil {
return nil, errors.WithMessage(err, "could not find matching key for SKI")
}
} else {
var err error
privateKey, err = fabricCaUtil.ImportBCCSPKeyFromPEMBytes(opt.PrivateKey, mgr.cryptoSuite, true)
if err != nil {
return nil, errors.WithMessage(err, "failed to import key")
}
}
return &User{
mspID: mgr.orgMSPID,
enrollmentCertificate: opt.Cert,
privateKey: privateKey,
}, nil
}

// GetUser returns a user for the given user name
func (mgr *IdentityManager) GetUser(username string) (*User, error) { //nolint

Expand Down
123 changes: 119 additions & 4 deletions pkg/msp/getsigid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ func TestGetSigningIdentity(t *testing.T) {

// "Manually" enroll User1
enrollUser1(cryptoSuite, t, mspID, testUsername, userStore, mgr)
// Should succeed after enrollment
if err := checkSigningIdentity(mgr, testUsername); err != nil {
t.Fatalf("checkSigningIdentity failed: %s", err)
}
}

func getConfigs(t *testing.T) (core.CryptoSuiteConfig, providersFab.EndpointConfig, msp.IdentityConfig, providersFab.OrganizationConfig) {
Expand Down Expand Up @@ -136,10 +140,6 @@ func enrollUser1(cryptoSuite core.CryptoSuite, t *testing.T, mspID string, testU
if err != nil {
t.Fatalf("userStore.Store: %s", err)
}
// Should succeed after enrollment
if err := checkSigningIdentity(mgr, testUsername); err != nil {
t.Fatalf("checkSigningIdentity failed: %s", err)
}
}

func checkSigningIdentity(mgr msp.IdentityManager, user string) error {
Expand Down Expand Up @@ -290,6 +290,121 @@ func checkSigningIdentityFromEmbeddedCryptoConfig(mgr *IdentityManager, t *testi
}
}

func TestCreateSigningIdentityNegative(t *testing.T) {
cryptoConfig, endpointConfig, identityConfig, _ := getConfigs(t)
clientConfig := identityConfig.Client()

// Cleanup key store and user store
cleanupTestPath(t, cryptoConfig.KeyStorePath())
defer cleanupTestPath(t, cryptoConfig.KeyStorePath())
cleanupTestPath(t, clientConfig.CredentialStore.Path)
defer cleanupTestPath(t, clientConfig.CredentialStore.Path)

cryptoSuite, err := sw.GetSuiteByConfig(cryptoConfig)
if err != nil {
t.Fatalf("Failed to setup cryptoSuite: %s", err)
}

// userStore should be probably nil in this use case,
// as client doesn't want SDK to manage certs.
userStore := msp.UserStore(nil)
mgr, err := NewIdentityManager(orgName, userStore, cryptoSuite, endpointConfig)
if err != nil {
t.Fatalf("Failed to setup credential manager: %s", err)
}

_, err = mgr.CreateSigningIdentity()
if err == nil {
t.Fatalf("Should have failed to create signing identity with no certificate")
}

_, err = mgr.CreateSigningIdentity(msp.WithPrivateKey([]byte(testPrivKey)))
if err == nil {
t.Fatalf("Should have failed to create signing identity with only private key")
}

_, err = mgr.CreateSigningIdentity(func(_ *msp.IdentityOption) error {
return errors.New("failed")
})
if err == nil {
t.Fatalf("Should have failed with failing option")
}

_, err = mgr.CreateSigningIdentity(msp.WithCert([]byte(testCert)))
if err == nil {
t.Fatalf("Should have failed to create signing identity without imported private key")
}
}

func TestCreateSigningIdentity(t *testing.T) {
cryptoConfig, endpointConfig, identityConfig, _ := getConfigs(t)
clientConfig := identityConfig.Client()

// Cleanup key store and user store
cleanupTestPath(t, cryptoConfig.KeyStorePath())
defer cleanupTestPath(t, cryptoConfig.KeyStorePath())
cleanupTestPath(t, clientConfig.CredentialStore.Path)
defer cleanupTestPath(t, clientConfig.CredentialStore.Path)

cryptoSuite, err := sw.GetSuiteByConfig(cryptoConfig)
if err != nil {
t.Fatalf("Failed to setup cryptoSuite: %s", err)
}

// userStore should be probably nil in this use case,
// as client doesn't want SDK to manage certs.
userStore := msp.UserStore(nil)
mgr, err := NewIdentityManager(orgName, userStore, cryptoSuite, endpointConfig)
if err != nil {
t.Fatalf("Failed to setup credential manager: %s", err)
}

id, err := mgr.CreateSigningIdentity(msp.WithCert([]byte(testCert)), msp.WithPrivateKey([]byte(testPrivKey)))
if err != nil {
t.Fatalf("Failed when creating identity based on certificate and private key: %s", err)
}
if err := validateTestIdentity(id); err != nil {
t.Fatal(err)
}

// In this user case client might want to import keys directly into keystore
// out of band instead of enrolling the user via SDK. User enrolment creates a cert
// and stores it into local SDK user store, while user might not want SDK to manage certs.
err = importPrivateKeyOutOfBand([]byte(testPrivKey), mgr.cryptoSuite)
if err != nil {
t.Fatalf("failed to import key: %s", err)
}

id, err = mgr.CreateSigningIdentity(msp.WithCert([]byte(testCert)))
if err != nil {
t.Fatalf("Failed when creating identity based on certificate: %s", err)
}
if err := validateTestIdentity(id); err != nil {
t.Fatal(err)
}
}

func importPrivateKeyOutOfBand(privateKey []byte, cs core.CryptoSuite) error {
_, err := fabricCaUtil.ImportBCCSPKeyFromPEMBytes([]byte(privateKey), cs, false)
return err
}

func validateTestIdentity(id msp.SigningIdentity) error {
if id == nil {
return errors.New("SigningIdentity is nil")
}
if string(id.EnrollmentCertificate()) != testCert {
return errors.New("Enrollment cert not equal")
}
if id.Identifier().MSPID == "" {
return errors.New("MSPID is missing")
}
if id.PrivateKey() == nil {
return errors.New("private key is missing")
}
return nil
}

func createRandomName() string {
return "user" + strconv.Itoa(rand.Intn(500000))
}

0 comments on commit cb2a4cf

Please sign in to comment.