From 403080d7f0ea9494f0a3451aa0d30357f2f1ade4 Mon Sep 17 00:00:00 2001 From: Saad Karim Date: Mon, 27 Feb 2017 17:36:20 -0500 Subject: [PATCH] Improvements to revoke client side command Revocation logic improved to check that revoker has the necessary affiliation to revoke a user. Check added to make sure that either the enrollment ID is provided or both serial and AKI are provided, all three can't be provided at the same time. Storing both serial and AKI in database in hex format. This improves user experience as openssl returns both serial and AKI in hex format. User's can now directly take the values and pass into the revoke command without having to do any manipulation. Test cases added to test various use cases for revocation https://jira.hyperledger.org/browse/FAB-2517 Change-Id: Ice68cfcc7b134046a18d2124ec3c3b0b1ae8be68 Signed-off-by: Saad Karim Signed-off-by: Keith Smith --- cmd/fabric-ca-client/config.go | 2 +- cmd/fabric-ca-client/main_test.go | 132 ++++++++++++++++++++------ cmd/fabric-ca-client/revoke.go | 25 ++++- cmd/fabric-ca-server/config.go | 8 +- lib/certdbaccessor.go | 39 +++++++- lib/dbutil/dbutil.go | 10 +- lib/server.go | 19 ++-- lib/server_test.go | 25 +++++ lib/serverrevoke.go | 45 ++++++++- lib/util.go | 16 +++- testdata/fabric-ca-client-config.yaml | 2 +- util/util.go | 28 ++++++ 12 files changed, 294 insertions(+), 57 deletions(-) diff --git a/cmd/fabric-ca-client/config.go b/cmd/fabric-ca-client/config.go index cf26c916d..53fbf6934 100644 --- a/cmd/fabric-ca-client/config.go +++ b/cmd/fabric-ca-client/config.go @@ -190,7 +190,7 @@ func configInit(command string) error { // Unmarshal the config into 'clientCfg' err = viper.Unmarshal(clientCfg) if err != nil { - util.Fatal("Failed to unmarshall client config: %s", err) + util.Fatal("Could not parse '%s': %s", cfgFileName, err) } purl, err := url.Parse(clientCfg.URL) diff --git a/cmd/fabric-ca-client/main_test.go b/cmd/fabric-ca-client/main_test.go index f5697f02c..38aa4e268 100644 --- a/cmd/fabric-ca-client/main_test.go +++ b/cmd/fabric-ca-client/main_test.go @@ -17,6 +17,11 @@ limitations under the License. package main import ( + "crypto/x509" + "encoding/hex" + "encoding/pem" + "errors" + "fmt" "io/ioutil" "os" "path" @@ -26,6 +31,7 @@ import ( "github.com/cloudflare/cfssl/csr" "github.com/hyperledger/fabric-ca/lib" + "github.com/hyperledger/fabric-ca/lib/dbutil" "github.com/hyperledger/fabric-ca/util" ) @@ -49,6 +55,7 @@ const ( var ( defYaml string fabricCADB = path.Join(tdDir, db) + srv *lib.Server ) // TestCreateDefaultConfigFile test to make sure default config file gets generated correctly @@ -85,22 +92,22 @@ func TestCreateDefaultConfigFile(t *testing.T) { func TestClientCommandsNoTLS(t *testing.T) { os.Remove(fabricCADB) - srv := getServer() + srv = getServer() srv.HomeDir = tdDir srv.Config.Debug = true - err := srv.RegisterBootstrapUser("admin", "adminpw", "bank1") + err := srv.RegisterBootstrapUser("admin", "adminpw", "banks.bank_a.Dep1") if err != nil { t.Errorf("Failed to register bootstrap user: %s", err) } - err = srv.RegisterBootstrapUser("admin2", "adminpw2", "bank1") + err = srv.RegisterBootstrapUser("admin2", "adminpw2", "banks.bank_a") if err != nil { t.Errorf("Failed to register bootstrap user: %s", err) } aff := make(map[string]interface{}) - aff["banks"] = "bank_a" + aff["banks"] = []string{"bank_a", "bank_b", "bank_c"} srv.Config.Affiliations = aff @@ -122,27 +129,6 @@ func TestClientCommandsNoTLS(t *testing.T) { } } -func getServer() *lib.Server { - return &lib.Server{ - HomeDir: ".", - Config: getServerConfig(), - } -} - -func getServerConfig() *lib.ServerConfig { - return &lib.ServerConfig{ - Debug: true, - Port: 7054, - CA: lib.ServerConfigCA{ - Keyfile: keyfile, - Certfile: certfile, - }, - CSR: csr.CertificateRequest{ - CN: "TestCN", - }, - } -} - // TestEnroll tests fabric-ca-client enroll func testEnroll(t *testing.T) { t.Log("Testing Enroll CMD") @@ -208,7 +194,7 @@ func testRegisterEnvVar(t *testing.T) { defYaml = util.GetDefaultConfigFile("fabric-ca-client") os.Setenv("FABRIC_CA_CLIENT_ID_NAME", "testRegister2") - os.Setenv("FABRIC_CA_CLIENT_ID_AFFILIATION", "banks.bank_a") + os.Setenv("FABRIC_CA_CLIENT_ID_AFFILIATION", "banks.bank_b") os.Setenv("FABRIC_CA_CLIENT_ID_TYPE", "client") err := RunMain([]string{cmdName, "register"}) @@ -255,11 +241,55 @@ func testRevoke(t *testing.T) { t.Errorf("No enrollment ID or serial/aki provided, should have failed") } - err = RunMain([]string{cmdName, "revoke", "-u", "http://localhost:7054", "-e", "admin"}) + serial, aki, err := getSerialAKIByID("admin") + if err != nil { + t.Error(err) + } + + aki = strings.ToUpper(aki) + + // Revoker's affiliation: banks.bank_a + err = RunMain([]string{cmdName, "revoke", "-u", "http://localhost:7054", "-e", "nonexistinguser"}) + if err == nil { + t.Errorf("Non existing user being revoked, should have failed") + } + + err = RunMain([]string{cmdName, "revoke", "-u", "http://localhost:7054", "-e", "", "-s", serial}) + if err == nil { + t.Errorf("Only serial specified, should have failed") + } + + err = RunMain([]string{cmdName, "revoke", "-u", "http://localhost:7054", "-e", "", "-s", "", "-a", aki}) + if err == nil { + t.Errorf("Only aki specified, should have failed") + } + + err = RunMain([]string{cmdName, "revoke", "-u", "http://localhost:7054", "-s", serial, "-a", aki}) + if err != nil { + t.Errorf("client revoke -u -s -a failed: %s", err) + } + + serial, aki, err = getSerialAKIByID("testRegister") + if err != nil { + t.Error(err) + } + + // Revoked user's affiliation: banks.bank_c + err = RunMain([]string{cmdName, "revoke", "-u", "http://localhost:7054", "-s", serial, "-a", aki}) + if err != nil { + t.Errorf("Revoker does not have the correct affiliation to revoke, should have failed") + } + + err = RunMain([]string{cmdName, "revoke", "-u", "http://localhost:7054", "-e", "testRegister3", "-s", "", "-a", ""}) if err != nil { t.Errorf("client revoke -u -e failed: %s", err) } + err = RunMain([]string{cmdName, "revoke", "-u", "http://localhost:7054", "-e", "testRegister2", "-s", "", "-a", ""}) + if err == nil { + t.Errorf("Revoker does not have the correct affiliation to revoke, should have failed") + } + os.Remove(defYaml) // Delete default config file err = RunMain([]string{cmdName, "revoke", "-u", "http://localhost:7055"}) @@ -282,7 +312,7 @@ func testBogus(t *testing.T) { func TestClientCommandsUsingConfigFile(t *testing.T) { os.Remove(fabricCADB) - srv := getServer() + srv = getServer() srv.Config.Debug = true err := srv.RegisterBootstrapUser("admin", "adminpw", "bank1") @@ -314,7 +344,7 @@ func TestClientCommandsUsingConfigFile(t *testing.T) { func TestClientCommandsTLSEnvVar(t *testing.T) { os.Remove(fabricCADB) - srv := getServer() + srv = getServer() srv.Config.Debug = true err := srv.RegisterBootstrapUser("admin", "adminpw", "bank1") @@ -359,7 +389,7 @@ func TestClientCommandsTLSEnvVar(t *testing.T) { func TestClientCommandsTLS(t *testing.T) { os.Remove(fabricCADB) - srv := getServer() + srv = getServer() srv.Config.Debug = true err := srv.RegisterBootstrapUser("admin", "adminpw", "bank1") @@ -406,3 +436,45 @@ func TestRegisterWithoutEnroll(t *testing.T) { t.Errorf("Should have failed, as no enrollment information should exist. Enroll commands needs to be the first command to be executed") } } + +func getServer() *lib.Server { + return &lib.Server{ + HomeDir: ".", + Config: getServerConfig(), + } +} + +func getServerConfig() *lib.ServerConfig { + return &lib.ServerConfig{ + Debug: true, + Port: 7054, + CA: lib.ServerConfigCA{ + Keyfile: keyfile, + Certfile: certfile, + }, + CSR: csr.CertificateRequest{ + CN: "TestCN", + }, + } +} + +func getSerialAKIByID(id string) (serial, aki string, err error) { + testdb, _, _ := dbutil.NewUserRegistrySQLLite3(srv.Config.DB.Datasource) + acc := lib.NewCertDBAccessor(testdb) + + certs, _ := acc.GetCertificatesByID("admin") + + block, _ := pem.Decode([]byte(certs[0].PEM)) + if block == nil { + return "", "", errors.New("Failed to PEM decode certificate") + } + x509Cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return "", "", fmt.Errorf("Error from x509.ParseCertificate: %s", err) + } + + serial = util.GetSerialAsHex(x509Cert.SerialNumber) + aki = hex.EncodeToString(x509Cert.AuthorityKeyId) + + return +} diff --git a/cmd/fabric-ca-client/revoke.go b/cmd/fabric-ca-client/revoke.go index bac9dfce5..610572236 100644 --- a/cmd/fabric-ca-client/revoke.go +++ b/cmd/fabric-ca-client/revoke.go @@ -17,7 +17,7 @@ limitations under the License. package main import ( - "fmt" + "errors" "path/filepath" "github.com/cloudflare/cfssl/log" @@ -28,6 +28,8 @@ import ( "github.com/spf13/viper" ) +var errInput = errors.New("Invalid usage; either --eid or both --serial and --aki are required") + // initCmd represents the init command var revokeCmd = &cobra.Command{ Use: "revoke", @@ -72,7 +74,10 @@ func runRevoke() error { log.Debug("Revoke Entered") var err error + enrollmentID := viper.GetString("eid") + serial := viper.GetString("serial") + aki := viper.GetString("aki") client := lib.Client{ HomeDir: filepath.Dir(cfgFileName), @@ -84,11 +89,20 @@ func runRevoke() error { return err } - serial := viper.GetString("serial") - aki := viper.GetString("aki") + if enrollmentID == "" { + if serial == "" || aki == "" { + return errInput + } + } else { + if serial != "" || aki != "" { + return errInput + } + } - if enrollmentID == "" && serial == "" { - return fmt.Errorf("Invalid usage; either --eid or both --serial and --aki are required") + reasonInput := viper.GetString("reason") + var reason int + if reasonInput != "" { + reason = util.RevocationReasonCodes[reasonInput] } err = id.Revoke( @@ -96,6 +110,7 @@ func runRevoke() error { Name: enrollmentID, Serial: serial, AKI: aki, + Reason: reason, }) if err == nil { diff --git a/cmd/fabric-ca-server/config.go b/cmd/fabric-ca-server/config.go index 3fee8e041..71eff0837 100644 --- a/cmd/fabric-ca-server/config.go +++ b/cmd/fabric-ca-server/config.go @@ -144,13 +144,12 @@ registry: # To run the fabric-ca-server in a cluster, you must choose "postgres" # or "mysql". ############################################################################# -database: +db: type: sqlite3 datasource: fabric-ca-server.db tls: enabled: false - certfiles: - - db-server-cert.pem + certfiles: db-server-cert.pem # Comma Separated (e.g. root.pem, root2.pem) client: certfile: db-client-cert.pem keyfile: db-client-key.pem @@ -168,8 +167,7 @@ ldap: # The URL of the LDAP server url: ldap://:@:/ tls: - certfiles: - - ldap-server-cert.pem + certfiles: ldap-server-cert.pem # Comma Separated (e.g. root.pem, root2.pem) client: certfile: ldap-client-cert.pem keyfile: ldap-client-key.pem diff --git a/lib/certdbaccessor.go b/lib/certdbaccessor.go index b8c903837..36f8756cb 100644 --- a/lib/certdbaccessor.go +++ b/lib/certdbaccessor.go @@ -19,6 +19,7 @@ package lib import ( "errors" "fmt" + "math/big" "time" "github.com/cloudflare/cfssl/certdb" @@ -35,10 +36,14 @@ const ( INSERT INTO certificates (id, serial_number, authority_key_identifier, ca_label, status, reason, expiry, revoked_at, pem) VALUES (:id, :serial_number, :authority_key_identifier, :ca_label, :status, :reason, :expiry, :revoked_at, :pem);` - selectSQL = ` + selectSQLbyID = ` SELECT %s FROM certificates WHERE (id = ?);` + selectSQL = ` +SELECT %s FROM certificates +WHERE (serial_number = ? AND authority_key_identifier = ?);` + updateRevokeSQL = ` UPDATE certificates SET status='revoked', revoked_at=CURRENT_TIMESTAMP, reason=:reason @@ -92,9 +97,14 @@ func (d *CertDBAccessor) InsertCertificate(cr certdb.CertificateRecord) error { return err } + ip := new(big.Int) + ip.SetString(cr.Serial, 10) //base 10 + + serial := util.GetSerialAsHex(ip) + var record = new(CertRecord) record.ID = id - record.Serial = cr.Serial + record.Serial = serial record.AKI = cr.AKI record.CALabel = cr.CALabel record.Status = cr.Status @@ -130,7 +140,7 @@ func (d *CertDBAccessor) GetCertificatesByID(id string) (crs []CertRecord, err e return nil, err } - err = d.db.Select(&crs, fmt.Sprintf(d.db.Rebind(selectSQL), sqlstruct.Columns(CertRecord{})), id) + err = d.db.Select(&crs, fmt.Sprintf(d.db.Rebind(selectSQLbyID), sqlstruct.Columns(CertRecord{})), id) if err != nil { return nil, err } @@ -140,10 +150,29 @@ func (d *CertDBAccessor) GetCertificatesByID(id string) (crs []CertRecord, err e // GetCertificate gets a CertificateRecord indexed by serial. func (d *CertDBAccessor) GetCertificate(serial, aki string) (crs []certdb.CertificateRecord, err error) { + log.Debugf("DB: Get certificate by serial (%s) and aki (%s)", serial, aki) crs, err = d.accessor.GetCertificate(serial, aki) if err != nil { return nil, err } + + return crs, nil +} + +// GetCertificateWithID gets a CertificateRecord indexed by serial and returns user too. +func (d *CertDBAccessor) GetCertificateWithID(serial, aki string) (crs CertRecord, err error) { + log.Debugf("DB: Get certificate by serial (%s) and aki (%s)", serial, aki) + + err = d.checkDB() + if err != nil { + return crs, err + } + + err = d.db.Get(&crs, fmt.Sprintf(d.db.Rebind(selectSQL), sqlstruct.Columns(CertRecord{})), serial, aki) + if err != nil { + return crs, err + } + return crs, nil } @@ -158,6 +187,8 @@ func (d *CertDBAccessor) GetUnexpiredCertificates() (crs []certdb.CertificateRec // RevokeCertificatesByID updates all certificates for a given ID and marks them revoked. func (d *CertDBAccessor) RevokeCertificatesByID(id string, reasonCode int) (crs []CertRecord, err error) { + log.Debugf("DB: Revoke certificate by ID (%s)", id) + err = d.checkDB() if err != nil { return nil, err @@ -182,6 +213,8 @@ func (d *CertDBAccessor) RevokeCertificatesByID(id string, reasonCode int) (crs // RevokeCertificate updates a certificate with a given serial number and marks it revoked. func (d *CertDBAccessor) RevokeCertificate(serial, aki string, reasonCode int) error { + log.Debugf("DB: Revoke certificate by serial (%s) and aki (%s)", serial, aki) + err := d.accessor.RevokeCertificate(serial, aki, reasonCode) return err } diff --git a/lib/dbutil/dbutil.go b/lib/dbutil/dbutil.go index 8c89d8763..e85f12dc8 100644 --- a/lib/dbutil/dbutil.go +++ b/lib/dbutil/dbutil.go @@ -99,9 +99,13 @@ func NewUserRegistryPostgres(datasource string, clientTLSConfig *tls.ClientTLSCo dbName := getDBName(datasource) log.Debug("Database Name: ", dbName) + if strings.Contains(dbName, "-") || strings.HasSuffix(dbName, ".db") { + return nil, false, fmt.Errorf("Database name %s cannot contain any '-' or end with '.db'", dbName) + } + connStr := getConnStr(datasource) - if clientTLSConfig != nil { + if clientTLSConfig.Enabled { if len(clientTLSConfig.CertFilesList) > 0 { root := clientTLSConfig.CertFilesList[0] connStr = fmt.Sprintf("%s sslrootcert=%s", connStr, root) @@ -194,7 +198,7 @@ func NewUserRegistryMySQL(datasource string, clientTLSConfig *tls.ClientTLSConfi re := regexp.MustCompile(`\/([a-zA-z]+)`) connStr := re.ReplaceAllString(datasource, "/") - if clientTLSConfig != nil { + if clientTLSConfig.Enabled { tlsConfig, err := tls.GetClientTLSConfig(clientTLSConfig) if err != nil { log.Errorf("Failed to create TLS configuration [error: %s]", err) @@ -262,7 +266,7 @@ func createMySQLTables(datasource string, dbName string, db *sqlx.DB) error { log.Errorf("Error creating affiliations table [error: %s] ", err) return err } - if _, err := database.Exec("CREATE TABLE certificates (id VARCHAR(64), serial_number varbinary(20) NOT NULL, authority_key_identifier varbinary(128) NOT NULL, ca_label varbinary(128), status varbinary(128) NOT NULL, reason int, expiry timestamp DEFAULT '1970-01-01 00:00:01', revoked_at timestamp DEFAULT '1970-01-01 00:00:01', pem varbinary(4096) NOT NULL, PRIMARY KEY(serial_number, authority_key_identifier))"); err != nil { + if _, err := database.Exec("CREATE TABLE certificates (id VARCHAR(64), serial_number varbinary(128) NOT NULL, authority_key_identifier varbinary(128) NOT NULL, ca_label varbinary(128), status varbinary(128) NOT NULL, reason int, expiry timestamp DEFAULT '1970-01-01 00:00:01', revoked_at timestamp DEFAULT '1970-01-01 00:00:01', pem varbinary(4096) NOT NULL, PRIMARY KEY(serial_number, authority_key_identifier))"); err != nil { log.Errorf("Error creating certificates table [error: %s] ", err) return err } diff --git a/lib/server.go b/lib/server.go index bb62bdebc..b93209c30 100644 --- a/lib/server.go +++ b/lib/server.go @@ -61,6 +61,10 @@ var ( MyCSP bccsp.BCCSP ) +const ( + defaultDatabaseType = "sqlite3" +) + // Server is the fabric-ca server type Server struct { // The home directory for the server @@ -341,22 +345,23 @@ func (s *Server) initDB() error { MaxEnrollments = s.Config.Registry.MaxEnrollments - if db.Type == "" { - db.Type = "sqlite3" - } if db.Datasource == "" { db.Datasource = "fabric-ca-server.db" } - db.Datasource, err = util.MakeFileAbs(db.Datasource, s.HomeDir) - if err != nil { - return err + if db.Type == "" || db.Type == defaultDatabaseType { + db.Type = defaultDatabaseType + + db.Datasource, err = util.MakeFileAbs(db.Datasource, s.HomeDir) + if err != nil { + return err + } } log.Debugf("Initializing '%s' data base at '%s'", db.Type, db.Datasource) switch db.Type { - case "sqlite3": + case defaultDatabaseType: s.db, exists, err = dbutil.NewUserRegistrySQLLite3(db.Datasource) if err != nil { return err diff --git a/lib/server_test.go b/lib/server_test.go index eb815dd2e..bbd242128 100644 --- a/lib/server_test.go +++ b/lib/server_test.go @@ -27,6 +27,7 @@ import ( "github.com/hyperledger/fabric-ca/api" "github.com/hyperledger/fabric-ca/lib" "github.com/hyperledger/fabric-ca/lib/tls" + "github.com/hyperledger/fabric-ca/util" ) const ( @@ -211,6 +212,30 @@ func TestRunningTLSServer(t *testing.T) { } } +func TestDefaultDatabase(t *testing.T) { + TestEnd(t) + + srv := getServer(rootPort, testdataDir, "", 0, t) + + err := srv.Start() + if err != nil { + t.Fatalf("Root server start failed: %s", err) + } + + time.Sleep(1 * time.Second) + + err = srv.Stop() + if err != nil { + t.Errorf("Server stop failed: %s", err) + } + + exist := util.FileExists("../testdata/fabric-ca-server.db") + if !exist { + t.Error("Failed to create default sqlite fabric-ca-server.db") + } + +} + func testIntermediateServer(idx int, t *testing.T) { // Init the intermediate server intermediateServer := getIntermediateServer(idx, t) diff --git a/lib/serverrevoke.go b/lib/serverrevoke.go index a20b03176..63c799c6e 100644 --- a/lib/serverrevoke.go +++ b/lib/serverrevoke.go @@ -22,6 +22,7 @@ import ( "fmt" "io/ioutil" "net/http" + "strings" cfsslapi "github.com/cloudflare/cfssl/api" "github.com/cloudflare/cfssl/log" @@ -82,9 +83,29 @@ func (h *revokeHandler) Handle(w http.ResponseWriter, r *http.Request) error { log.Debugf("Revoke request: %+v", req) + req.AKI = strings.ToLower(req.AKI) + req.Serial = strings.ToLower(req.Serial) + if req.Serial != "" && req.AKI != "" { + certificate, err := MyCertDBAccessor.GetCertificateWithID(req.Serial, req.AKI) + if err != nil { + log.Error(notFound(w, err)) + return notFound(w, err) + } + + userInfo, err2 := UserRegistry.GetUserInfo(certificate.ID) + if err2 != nil { + return err2 + } + + err2 = checkAffiliations(cert.Subject.CommonName, userInfo.Affiliation) + if err2 != nil { + return err2 + } + err = MyCertDBAccessor.RevokeCertificate(req.Serial, req.AKI, req.Reason) if err != nil { + log.Error(notFound(w, err)) return notFound(w, err) } } else if req.Name != "" { @@ -104,6 +125,11 @@ func (h *revokeHandler) Handle(w http.ResponseWriter, r *http.Request) error { return notFound(w, err) } + err = checkAffiliations(cert.Subject.CommonName, userInfo.Affiliation) + if err != nil { + return err + } + userInfo.State = -1 err = UserRegistry.UpdateUser(userInfo) @@ -121,7 +147,7 @@ func (h *revokeHandler) Handle(w http.ResponseWriter, r *http.Request) error { } if len(recs) == 0 { - return fmt.Errorf("Enrollment ID '%s' has no revocable certificates", req.Name) + log.Warningf("No certificates were revoked for '%s' but the ID was disabled: %s", req.Name) } log.Debugf("Revoked the following certificates owned by '%s': %+v", req.Name, recs) @@ -135,3 +161,20 @@ func (h *revokeHandler) Handle(w http.ResponseWriter, r *http.Request) error { result := map[string]string{} return cfsslapi.SendResponse(w, result) } + +// Make sure the revoker's affiliation is equal to or is a prefix of 'affiliation' +func checkAffiliations(revoker string, affiliation string) error { + log.Debugf("Check to see if revoker %s has affiliations to revoke: %s", revoker, affiliation) + revokerAffiliation, err := getUserAff(revoker) + if err != nil { + return err + } + + log.Debugf("Affiliation of revoker: %s, affiliation of user being revoked: %s", revokerAffiliation, affiliation) + + if !strings.HasPrefix(affiliation, revokerAffiliation) { + return fmt.Errorf("Revoker %s does not have proper affiliation to revoke user", revoker) + } + + return nil +} diff --git a/lib/util.go b/lib/util.go index e72b5527d..3f2b70c78 100644 --- a/lib/util.go +++ b/lib/util.go @@ -23,6 +23,7 @@ import ( "fmt" "github.com/cloudflare/cfssl/log" + "github.com/hyperledger/fabric-ca/util" ) // userHasAttribute returns nil if the user has the attribute, or an @@ -51,13 +52,26 @@ func getUserAttrValue(username, attrname string) (string, error) { } +// getUserAff returns a user's affiliation +func getUserAff(username string) (string, error) { + log.Debugf("getUserAff user=%s", username) + user, err := UserRegistry.GetUserInfo(username) + if err != nil { + return "", err + } + aff := user.Affiliation + log.Debugf("getUserAttrValue user=%s, aff=%s, value=%s", username, aff) + return aff, nil + +} + // GetCertID returns both the serial number and AKI (Authority Key ID) for the certificate func GetCertID(bytes []byte) (string, string, error) { cert, err := BytesToX509Cert(bytes) if err != nil { return "", "", err } - serial := cert.SerialNumber.String() + serial := util.GetSerialAsHex(cert.SerialNumber) aki := hex.EncodeToString(cert.AuthorityKeyId) return serial, aki, nil } diff --git a/testdata/fabric-ca-client-config.yaml b/testdata/fabric-ca-client-config.yaml index 868d690b0..7cf07f15a 100644 --- a/testdata/fabric-ca-client-config.yaml +++ b/testdata/fabric-ca-client-config.yaml @@ -75,7 +75,7 @@ csr: id: name: testRegister type: user - affiliation: banks.bank_a + affiliation: banks.bank_c attributes: - name: hf.Revoker value: true diff --git a/util/util.go b/util/util.go index fcf68aff1..53e63950e 100644 --- a/util/util.go +++ b/util/util.go @@ -35,6 +35,9 @@ import ( "path/filepath" "strings" "time" + "unicode/utf8" + + "golang.org/x/crypto/ocsp" "github.com/cloudflare/cfssl/log" "github.com/hyperledger/fabric/bccsp" @@ -55,6 +58,20 @@ const ( letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits ) +// RevocationReasonCodes is a map between string reason codes to integers as defined in RFC 5280 +var RevocationReasonCodes = map[string]int{ + "unspecified": ocsp.Unspecified, + "keycompromise": ocsp.KeyCompromise, + "cacompromise": ocsp.CACompromise, + "affiliationchanged": ocsp.AffiliationChanged, + "superseded": ocsp.Superseded, + "cessationofoperation": ocsp.CessationOfOperation, + "certificatehold": ocsp.CertificateHold, + "removefromcrl": ocsp.RemoveFromCRL, + "privilegewithdrawn": ocsp.PrivilegeWithdrawn, + "aacompromise": ocsp.AACompromise, +} + //ECDSASignature forms the structure for R and S value for ECDSA type ECDSASignature struct { R, S *big.Int @@ -528,3 +545,14 @@ func GetKeyFromBytes(csp bccsp.BCCSP, key []byte) (bccsp.Key, error) { return csp.KeyImport(pkb, &bccsp.ECDSAPrivateKeyImportOpts{Temporary: true}) } + +// GetSerialAsHex returns the serial number from certificate as hex format +func GetSerialAsHex(serial *big.Int) string { + hex := fmt.Sprintf("%x", serial) + + if utf8.RuneCountInString(hex) < 80 { + hex = fmt.Sprintf("0%s", hex) + } + + return hex +}