Skip to content

Commit

Permalink
[FAB-8597] Simplify Enroll and Reenroll
Browse files Browse the repository at this point in the history
Enroll and Reenroll no longer return the enrolled user's
private key and enrollment cert. Both functions now return
only error.

Before the latest changes described in FAB-8269, the application
(that uses the SDK) was required to manage user certs upon
enrollment. Therefor, it was necessary for Enroll and Reenroll
to return the newly generated enrollment cert to the application
for management. Now, both the generated private key and cert
are managed by the SDK in the SDK stores. Application can
obtain them by calling IdentityManager.GetSigningIdentity.
Also, when the WithUser(username) option is used to create
SDK services, the user's private key and cert are automatically
used for signing client messages.

Change-Id: Iff13eb55238191536691df124431c1af2643bdd7
Signed-off-by: Aleksandar Likic <aleksandar.likic@securekey.com>
  • Loading branch information
Aleksandar Likic committed Mar 1, 2018
1 parent 82e18d8 commit e7a7792
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 248 deletions.
4 changes: 2 additions & 2 deletions pkg/context/api/identitymgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ type SigningIdentity struct {
// IdentityManager provides management of identities in a Fabric network
type IdentityManager interface {
GetSigningIdentity(name string) (*SigningIdentity, error)
Enroll(enrollmentID string, enrollmentSecret string) (core.Key, []byte, error)
Reenroll(user User) (core.Key, []byte, error)
Enroll(enrollmentID string, enrollmentSecret string) error
Reenroll(user User) error
Register(request *RegistrationRequest) (string, error)
Revoke(request *RevocationRequest) (*RevocationResponse, error)
CAName() string
Expand Down
17 changes: 6 additions & 11 deletions pkg/context/api/mocks/mockidmgr.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pkg/core/identitymgr/caclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func (im *IdentityManager) getRegistrarSI(enrollID string, enrollSecret string)
}

// Attempt to enroll the registrar
_, _, err = im.Enroll(enrollID, enrollSecret)
err = im.Enroll(enrollID, enrollSecret)
if err != nil {
return nil, err
}
Expand Down
96 changes: 53 additions & 43 deletions pkg/core/identitymgr/enrollment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"github.com/golang/mock/gomock"
"github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric-ca/util"
"github.com/hyperledger/fabric-sdk-go/pkg/context/api"
"github.com/hyperledger/fabric-sdk-go/pkg/context/api/core"
apimocks "github.com/hyperledger/fabric-sdk-go/pkg/context/api/mocks"
"github.com/hyperledger/fabric-sdk-go/pkg/fab/identity"
Expand All @@ -20,6 +21,36 @@ import (
"github.com/hyperledger/fabric-sdk-go/pkg/core/cryptosuite/bccsp/sw"
)

var (
// "Generated" cert, the "result" of a CA CSR processing
generatedCertBytes = []byte(`-----BEGIN CERTIFICATE-----
MIICGjCCAcCgAwIBAgIRAIQkbh9nsGnLmDalAVlj8sUwCgYIKoZIzj0EAwIwczEL
MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG
cmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh
Lm9yZzEuZXhhbXBsZS5jb20wHhcNMTcwNzI4MTQyNzIwWhcNMjcwNzI2MTQyNzIw
WjBbMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMN
U2FuIEZyYW5jaXNjbzEfMB0GA1UEAwwWQWRtaW5Ab3JnMS5leGFtcGxlLmNvbTBZ
MBMGByqGSM49AgEGCCqGSM49AwEHA0IABH5hECfx0WkNAPK8MDsko+Xk+hl6ePeb
Uo6cyvL+Y5lydedMiHYBJXiyzxWW7MFzIcYC/sEKbFfEOSNxX17Ju/yjTTBLMA4G
A1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMCsGA1UdIwQkMCKAIIeR0TY+iVFf
mvoEKwaToscEu43ZXSj5fTVJornjxDUtMAoGCCqGSM49BAMCA0gAMEUCIQDVf8cL
NrfToiPzJpEFPGF+/8CpzOkl91oz+XJsvdgf5wIgI/e8mpvpplUQbU52+LejA36D
CsbWERvZPjR/GFEDEvc=
-----END CERTIFICATE-----`)

// "Generated" private key
generatedKeyBytes = []byte(`-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg5Ahcehypz6IpAYy6
DtIf5zZsRjP4PtsmDhLbBJsXmD6hRANCAAR+YRAn8dFpDQDyvDA7JKPl5PoZenj3
m1KOnMry/mOZcnXnTIh2ASV4ss8VluzBcyHGAv7BCmxXxDkjcV9eybv8
-----END PRIVATE KEY-----`)

userToEnroll = "enrollmentID"

userToEnrollMspID string
enrollmentTestUserStore api.UserStore
)

func TestGetSigningIdentityWithEnrollment(t *testing.T) {
config, err := config.FromFile("../../../test/fixtures/config/config_test.yaml")()
if err != nil {
Expand All @@ -38,6 +69,7 @@ func TestGetSigningIdentityWithEnrollment(t *testing.T) {
if !ok {
t.Fatalf("org config not found: %s", orgName)
}
userToEnrollMspID = orgConfig.MspID

// Delete all private keys from the crypto suite store
// and users from the user store
Expand All @@ -49,24 +81,22 @@ func TestGetSigningIdentityWithEnrollment(t *testing.T) {

cs, err := sw.GetSuiteByConfig(config)

credentialMgr, err := New(orgName, cs, config)
identityMgr, err := New(orgName, cs, config)
if err != nil {
t.Fatalf("Failed to setup credential manager: %s", err)
}

if err := checkSigningIdentity(credentialMgr, "User1"); err != nil {
if err := checkSigningIdentity(identityMgr, "User1"); err != nil {
t.Fatalf("checkSigningIdentity failed: %s", err)
}

// Refers to the same location used by the IdentityManager
userStore, err := identity.NewCertFileUserStore(clientCofig.CredentialStore.Path, cs)
enrollmentTestUserStore, err = identity.NewCertFileUserStore(clientCofig.CredentialStore.Path, cs)
if err != nil {
t.Fatalf("Failed to setup userStore: %s", err)
}

userToEnroll := "enrollmentID"

if err := checkSigningIdentity(credentialMgr, userToEnroll); err == nil {
if err := checkSigningIdentity(identityMgr, userToEnroll); err == nil {
t.Fatalf("checkSigningIdentity should fail for user who hasn't been enrolled")
}

Expand All @@ -77,21 +107,12 @@ func TestGetSigningIdentityWithEnrollment(t *testing.T) {
caClient := apimocks.NewMockIdentityManager(ctrl)
prepareForEnroll(t, caClient, cs)

_, certBytes, err := caClient.Enroll(userToEnroll, "enrollmentSecret")
err = caClient.Enroll(userToEnroll, "enrollmentSecret")
if err != nil {
t.Fatalf("fabricCAClient Enroll failed: %v", err)
}

// Private key is saved to key store by Enroll()
// For now, the app has to save the cert (user)
user := identity.NewUser(orgConfig.MspID, userToEnroll)
user.SetEnrollmentCertificate([]byte(certBytes))
err = userStore.Store(user)
if err != nil {
t.Fatalf("userStore.Store: %s", err)
}

if err := checkSigningIdentity(credentialMgr, userToEnroll); err != nil {
if err := checkSigningIdentity(identityMgr, userToEnroll); err != nil {
t.Fatalf("checkSigningIdentity shouldn't fail for user who hasn been just enrolled: %s", err)
}
}
Expand All @@ -101,35 +122,24 @@ func prepareForEnroll(t *testing.T, mc *apimocks.MockIdentityManager, cs core.Cr
// A real caClient.Enroll() generates a CSR. In the process, a crypto suite generates
// a new key pair, and the private key is stored into crypto suite private key storage.

// "Generated" private key
keyBytes := []byte(`-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg5Ahcehypz6IpAYy6
DtIf5zZsRjP4PtsmDhLbBJsXmD6hRANCAAR+YRAn8dFpDQDyvDA7JKPl5PoZenj3
m1KOnMry/mOZcnXnTIh2ASV4ss8VluzBcyHGAv7BCmxXxDkjcV9eybv8
-----END PRIVATE KEY-----`)

// "Generated" cert, the "result" of a CA CSR processing
certBytes := []byte(`-----BEGIN CERTIFICATE-----
MIICGjCCAcCgAwIBAgIRAIQkbh9nsGnLmDalAVlj8sUwCgYIKoZIzj0EAwIwczEL
MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG
cmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh
Lm9yZzEuZXhhbXBsZS5jb20wHhcNMTcwNzI4MTQyNzIwWhcNMjcwNzI2MTQyNzIw
WjBbMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMN
U2FuIEZyYW5jaXNjbzEfMB0GA1UEAwwWQWRtaW5Ab3JnMS5leGFtcGxlLmNvbTBZ
MBMGByqGSM49AgEGCCqGSM49AwEHA0IABH5hECfx0WkNAPK8MDsko+Xk+hl6ePeb
Uo6cyvL+Y5lydedMiHYBJXiyzxWW7MFzIcYC/sEKbFfEOSNxX17Ju/yjTTBLMA4G
A1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMCsGA1UdIwQkMCKAIIeR0TY+iVFf
mvoEKwaToscEu43ZXSj5fTVJornjxDUtMAoGCCqGSM49BAMCA0gAMEUCIQDVf8cL
NrfToiPzJpEFPGF+/8CpzOkl91oz+XJsvdgf5wIgI/e8mpvpplUQbU52+LejA36D
CsbWERvZPjR/GFEDEvc=
-----END CERTIFICATE-----`)

var privateKey core.Key
var err error

mc.EXPECT().Enroll(gomock.Any(), gomock.Any()).Do(func(enrollmentID string, enrollmentSecret string) {

// Simulate key and cert management normally done by the SDK

// Import the key into the crypto suite's private key storage.
// This is normally done by a crypto suite when a new key is generated
privateKey, err = util.ImportBCCSPKeyFromPEMBytes(keyBytes, cs, false)
}).Return(privateKey, certBytes, err)
_, err = util.ImportBCCSPKeyFromPEMBytes(generatedKeyBytes, cs, false)

// Save the "new" cert to user store
// This is done by IdentityManagement.Enroll()
user := identity.NewUser(userToEnrollMspID, userToEnroll)
user.SetEnrollmentCertificate([]byte(generatedCertBytes))
err = enrollmentTestUserStore.Store(user)
if err != nil {
t.Fatalf("userStore.Store: %s", err)
}

}).Return(err)
}
35 changes: 19 additions & 16 deletions pkg/core/identitymgr/identitymgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,19 +119,22 @@ func (im *IdentityManager) CAName() string {
}

// Enroll a registered user in order to receive a signed X509 certificate.
// A new key pair is generated for the user. The private key and the
// enrollment certificate issued by the CA are stored in SDK stores.
// They can be retrieved by calling IdentityManager.GetSigningIdentity().
//
// enrollmentID The registered ID to use for enrollment
// enrollmentSecret The secret associated with the enrollment ID
// Returns X509 certificate
func (im *IdentityManager) Enroll(enrollmentID string, enrollmentSecret string) (core.Key, []byte, error) {
func (im *IdentityManager) Enroll(enrollmentID string, enrollmentSecret string) error {

if err := im.initCAClient(); err != nil {
return nil, nil, err
return err
}
if enrollmentID == "" {
return nil, nil, errors.New("enrollmentID is required")
return errors.New("enrollmentID is required")
}
if enrollmentSecret == "" {
return nil, nil, errors.New("enrollmentSecret is required")
return errors.New("enrollmentSecret is required")
}
// TODO add attributes
careq := &caapi.EnrollmentRequest{
Expand All @@ -141,53 +144,53 @@ func (im *IdentityManager) Enroll(enrollmentID string, enrollmentSecret string)
}
caresp, err := im.caClient.Enroll(careq)
if err != nil {
return nil, nil, errors.Wrap(err, "enroll failed")
return errors.Wrap(err, "enroll failed")
}
user := identity.NewUser(im.orgMspID, enrollmentID)
user.SetEnrollmentCertificate(caresp.Identity.GetECert().Cert())
user.SetPrivateKey(caresp.Identity.GetECert().Key())
err = im.userStore.Store(user)
if err != nil {
return nil, nil, errors.Wrap(err, "enroll failed")
return errors.Wrap(err, "enroll failed")
}
return caresp.Identity.GetECert().Key(), caresp.Identity.GetECert().Cert(), nil
return nil
}

// Reenroll an enrolled user in order to receive a signed X509 certificate
// Returns X509 certificate
func (im *IdentityManager) Reenroll(user contextApi.User) (core.Key, []byte, error) {
func (im *IdentityManager) Reenroll(user contextApi.User) error {

if err := im.initCAClient(); err != nil {
return nil, nil, err
return err
}
if user == nil {
return nil, nil, errors.New("user required")
return errors.New("user required")
}
if user.Name() == "" {
logger.Infof("Invalid re-enroll request, missing argument user")
return nil, nil, errors.New("user name missing")
return errors.New("user name missing")
}
req := &caapi.ReenrollmentRequest{
CAName: im.caClient.Config.CAName,
}
caidentity, err := im.caClient.NewIdentity(user.PrivateKey(), user.EnrollmentCertificate())
if err != nil {
return nil, nil, errors.Wrap(err, "failed to create CA signing identity")
return errors.Wrap(err, "failed to create CA signing identity")
}

caresp, err := caidentity.Reenroll(req)
if err != nil {
return nil, nil, errors.Wrap(err, "reenroll failed")
return errors.Wrap(err, "reenroll failed")
}
newUser := identity.NewUser(im.orgMspID, user.Name())
newUser.SetEnrollmentCertificate(caresp.Identity.GetECert().Cert())
newUser.SetPrivateKey(caresp.Identity.GetECert().Key())
err = im.userStore.Store(newUser)
if err != nil {
return nil, nil, errors.Wrap(err, "reenroll failed")
return errors.Wrap(err, "reenroll failed")
}

return caresp.Identity.GetECert().Key(), caresp.Identity.GetECert().Cert(), nil
return nil
}

// Register a User with the Fabric CA
Expand Down
Loading

0 comments on commit e7a7792

Please sign in to comment.