diff --git a/go.sum b/go.sum index 891efaee76..c5db694855 100644 --- a/go.sum +++ b/go.sum @@ -86,6 +86,7 @@ github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.0.2 h1:Ncr3ZIuJn322w2k1qmzXDnkLAdQMlJqBa9kfAH+irso= github.com/spf13/viper v1.0.2/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= diff --git a/pkg/client/msp/ca.go b/pkg/client/msp/ca.go index 08a219146b..93e8c03530 100644 --- a/pkg/client/msp/ca.go +++ b/pkg/client/msp/ca.go @@ -197,3 +197,9 @@ type GetCAInfoResponse struct { // Version of the server Version string } + +// CSRInfo is Certificate Signing Request (CSR) Information +type CSRInfo struct { + CN string + Hosts []string +} diff --git a/pkg/client/msp/client.go b/pkg/client/msp/client.go index d9783caa49..bcab619539 100644 --- a/pkg/client/msp/client.go +++ b/pkg/client/msp/client.go @@ -342,6 +342,7 @@ func (c *Client) Enroll(enrollmentID string, opts ...EnrollmentOption) error { Profile: eo.profile, Type: eo.typ, Label: eo.label, + CSR: createCSRInfo(eo.csr), } if req.CAName == "" { @@ -384,7 +385,9 @@ func (c *Client) Reenroll(enrollmentID string, opts ...EnrollmentOption) error { Profile: eo.profile, Label: eo.label, CAName: c.caName, + CSR: createCSRInfo(eo.csr), } + if req.CAName == "" { req.CAName = c.caName } @@ -665,6 +668,18 @@ func fillAffiliationInfo(info *AffiliationInfo, name string, affiliations []mspa return nil } +func createCSRInfo(csr *CSRInfo) *mspapi.CSRInfo { + if csr == nil { + // csr is not obrigatory, so we can return nil + return nil + } + + return &mspapi.CSRInfo{ + CN: csr.CN, + Hosts: csr.Hosts, + } +} + func getAllAttributes(attrs []mspapi.Attribute) []Attribute { attriburtes := []Attribute{} for _, attr := range attrs { diff --git a/pkg/client/msp/options.go b/pkg/client/msp/options.go index cb594a6e55..301e784465 100644 --- a/pkg/client/msp/options.go +++ b/pkg/client/msp/options.go @@ -24,6 +24,7 @@ type enrollmentOptions struct { label string typ string attrReqs []*AttributeRequest + csr *CSRInfo } // ClientOption describes a functional parameter for the New constructor @@ -98,3 +99,11 @@ func WithAttributeRequests(attrReqs []*AttributeRequest) EnrollmentOption { return nil } } + +// WithCSR enrollment option +func WithCSR(csr *CSRInfo) EnrollmentOption { + return func(o *enrollmentOptions) error { + o.csr = csr + return nil + } +} diff --git a/pkg/msp/api/api.go b/pkg/msp/api/api.go index dd44161728..54dc224fe6 100644 --- a/pkg/msp/api/api.go +++ b/pkg/msp/api/api.go @@ -79,6 +79,14 @@ type EnrollmentRequest struct { // The type of the enrollment request: x509 or idemix // The default is a request for an X509 enrollment certificate Type string + // CSR is Certificate Signing Request info + CSR *CSRInfo +} + +// CSRInfo is Certificate Signing Request (CSR) Information +type CSRInfo struct { + CN string + Hosts []string } // ReenrollmentRequest is a request to reenroll an identity. @@ -95,6 +103,8 @@ type ReenrollmentRequest struct { // AttrReqs are requests for attributes to add to the certificate. // Each attribute is added only if the requestor owns the attribute. AttrReqs []*AttributeRequest + // CSR is Certificate Signing Request info + CSR *CSRInfo } // Attribute defines additional attributes that may be passed along during registration diff --git a/pkg/msp/fabcaadapter.go b/pkg/msp/fabcaadapter.go index 57d0488745..6cb75858c8 100644 --- a/pkg/msp/fabcaadapter.go +++ b/pkg/msp/fabcaadapter.go @@ -56,6 +56,7 @@ func (c *fabricCAAdapter) Enroll(request *api.EnrollmentRequest) ([]byte, error) Profile: request.Profile, Type: request.Type, Label: request.Label, + CSR: createCSRInfo(request.CSR), } if len(request.AttrReqs) > 0 { @@ -82,6 +83,7 @@ func (c *fabricCAAdapter) Reenroll(key core.Key, cert []byte, request *api.Reenr CAName: c.caClient.Config.CAName, Profile: request.Profile, Label: request.Label, + CSR: createCSRInfo(request.CSR), } if len(request.AttrReqs) > 0 { attrs := make([]*caapi.AttributeRequest, len(request.AttrReqs)) @@ -532,6 +534,18 @@ func fillAffiliationInfo(info *api.AffiliationInfo, name string, affiliations [] return nil } +func createCSRInfo(csr *api.CSRInfo) *caapi.CSRInfo { + if csr == nil { + // csr is not obrigatory, so we can return nil + return nil + } + + return &caapi.CSRInfo{ + CN: csr.CN, + Hosts: csr.Hosts, + } +} + func getAllAttributes(attrs []caapi.Attribute) []api.Attribute { attriburtes := []api.Attribute{} for _, attr := range attrs { diff --git a/test/integration/pkg/client/msp/enrollment_test.go b/test/integration/pkg/client/msp/enrollment_test.go index 4158897d2f..3fa51bbd51 100644 --- a/test/integration/pkg/client/msp/enrollment_test.go +++ b/test/integration/pkg/client/msp/enrollment_test.go @@ -14,6 +14,7 @@ import ( "github.com/hyperledger/fabric-sdk-go/pkg/client/msp" "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context" + mspctx "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/msp" "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk" "github.com/hyperledger/fabric-sdk-go/test/integration" "github.com/pkg/errors" @@ -304,8 +305,132 @@ func TestEnrollWithProfile(t *testing.T) { if err != nil { t.Fatalf("Enroll failed: %s", err) } + +} + +func TestEnrollWithCSR(t *testing.T) { + // Instantiate the SDK + sdk, err := fabsdk.New(integration.ConfigBackend) + if err != nil { + t.Fatalf("SDK init failed: %s", err) + } + defer sdk.Close() + + // Delete all private keys from the crypto suite store + // and users from the user store at the end + integration.CleanupUserData(t, sdk) + defer integration.CleanupUserData(t, sdk) + + ctxProvider := sdk.Context() + + // Get the Client. + // Without WithOrg option, uses default client organization. + mspClient, err := msp.New(ctxProvider) + if err != nil { + t.Fatalf("failed to create CA client: %s", err) + } + + // As this integration test spawns a fresh CA instance, + // we have to enroll the CA registrar first. Otherwise, + // CA operations that require the registrar's identity + // will be rejected by the CA. + registrarEnrollID, registrarEnrollSecret := getRegistrarEnrollmentCredentials(t, ctxProvider) + + err = mspClient.Enroll(registrarEnrollID, msp.WithSecret(registrarEnrollSecret)) + if err != nil { + t.Fatalf("Enroll failed: %s", err) + } + + // Generate a random user name + username := integration.GenerateRandomID() + + // Register the new user + enrollmentSecret, err := mspClient.Register(&msp.RegistrationRequest{ + Name: username, + Type: IdentityTypeUser, + // Affiliation is mandatory. "org1" and "org2" are hardcoded as CA defaults + // See https://github.com/hyperledger/fabric-ca/blob/release/cmd/fabric-ca-server/config.go + Affiliation: "org2", + }) + if err != nil { + t.Fatalf("Registration failed: %s", err) + } + + extraHosts := []string{"localhost", "example.com", "127.0.0.1"} + csr := &msp.CSRInfo{ + CN: username, + Hosts: extraHosts, + } + + err = mspClient.Enroll(username, msp.WithSecret(enrollmentSecret), msp.WithCSR(csr)) + if err != nil { + t.Fatalf("Enroll failed: %s", err) + } + + // Get the new user's signing identity + si, err := mspClient.GetSigningIdentity(username) + if err != nil { + t.Fatalf("GetSigningIdentity failed: %s", err) + } + + has, err := hasHosts(si, extraHosts) + if err != nil { + t.Fatalf("Could not check for host in Signing Identity: %s", err) + } + if !has { + t.Fatalf("Missing host [%s] in Signing Identity", extraHosts[0]) + } + + err = mspClient.Reenroll(username, msp.WithSecret(enrollmentSecret), msp.WithCSR(csr)) + if err != nil { + t.Fatalf("Reenroll failed: %s", err) + } + + // Get the new user's signing identity + si, err = mspClient.GetSigningIdentity(username) + if err != nil { + t.Fatalf("GetSigningIdentity failed: %s", err) + } + + has, err = hasHosts(si, extraHosts) + if err != nil { + t.Fatalf("Could not check for host in Signing Identity at reenroll: %s", err) + } + if !has { + t.Fatalf("Missing host [%s] in Signing Identity at reenroll", extraHosts[0]) + } + } +func hasHosts(si mspctx.SigningIdentity, hosts []string) (bool, error) { + block, _ := pem.Decode(si.EnrollmentCertificate()) + if block == nil || block.Type != "CERTIFICATE" { + return false, errors.New("Public cert invalid, cannot decode") + } + + pub, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return false, errors.New("Could not decode Signing Identity certificate") + } + + certHosts := make(map[string]struct{}, len(pub.DNSNames)+len(pub.IPAddresses)) + for _, host := range pub.DNSNames { + certHosts[host] = struct{}{} + } + for _, host := range pub.IPAddresses { + certHosts[host.String()] = struct{}{} + } + + hasExtraHost := true + for _, requestedHost := range hosts { + if _, ok := certHosts[requestedHost]; !ok { + hasExtraHost = false + break + } + } + + return hasExtraHost, nil +} func getRegistrarEnrollmentCredentials(t *testing.T, ctxProvider context.ClientProvider) (string, string) { return getRegistrarEnrollmentCredentialsWithCAInstance(t, ctxProvider, "") diff --git a/test/performance/go.mod b/test/performance/go.mod index 5e6a19e6a7..beb4115db2 100644 --- a/test/performance/go.mod +++ b/test/performance/go.mod @@ -10,6 +10,7 @@ require ( github.com/golang/protobuf v1.3.2 github.com/hyperledger/fabric-protos-go v0.0.0-20191121202242-f5500d5e3e85 github.com/hyperledger/fabric-sdk-go v0.0.0-00010101000000-000000000000 + github.com/hyperledger/fabric-sdk-go/test/integration v0.0.0-20200228123703-4919c923071f // indirect github.com/pkg/errors v0.8.1 github.com/stretchr/testify v1.3.0 golang.org/x/net v0.0.0-20190311183353-d8887717615a diff --git a/test/performance/go.sum b/test/performance/go.sum index d528f2c45b..9607f6d297 100644 --- a/test/performance/go.sum +++ b/test/performance/go.sum @@ -39,10 +39,13 @@ github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce h1:xdsDDbiBDQTKASoGE github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/hyperledger/fabric v1.4.4/go.mod h1:tGFAOCT696D3rG0Vofd2dyWYLySHlh0aQjf7Q1HAju0= github.com/hyperledger/fabric-lib-go v1.0.0 h1:UL1w7c9LvHZUSkIvHTDGklxFv2kTeva1QI2emOVc324= github.com/hyperledger/fabric-lib-go v1.0.0/go.mod h1:H362nMlunurmHwkYqR5uHL2UDWbQdbfz74n8kbCFsqc= github.com/hyperledger/fabric-protos-go v0.0.0-20191121202242-f5500d5e3e85 h1:bNgEcCg5NVRWs/T+VUEfhgh5Olx/N4VB+0+ybW+oSuA= github.com/hyperledger/fabric-protos-go v0.0.0-20191121202242-f5500d5e3e85/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0= +github.com/hyperledger/fabric-sdk-go/test/integration v0.0.0-20200228123703-4919c923071f h1:cA0f82vWHAWZIpePsMg+SEEn9VWJxIynUjzPp5EQMJ8= +github.com/hyperledger/fabric-sdk-go/test/integration v0.0.0-20200228123703-4919c923071f/go.mod h1:1w9LomcqM0GogJJCXHySnntqGUtLK2cEk2ou81/haMU= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=