Skip to content

Commit

Permalink
[FAB-3883] Intermediate CA restriction on CN
Browse files Browse the repository at this point in the history
When starting up a CA, in the CSR section a CN can
be specified that will be used to generate a
self-signed certificate if a root CA. However, when
a CA is acting like an intermediate it should
not be able to specify CN.

An intermediate CA, acting like a client, will enroll
with the root CA by providing its enrollment ID and
password. The enrollment ID will take place of the CN
in the certificate. An error will be thrown if an
intermediate CA tries to specify a CN. 

See [FAB-3883] for more info

Change-Id: I993d8837a09a4dbbdb625c7cb901798a871ef97d
Signed-off-by: Saad Karim <skarim@us.ibm.com>
  • Loading branch information
Saad Karim committed May 23, 2017
1 parent 8206d83 commit e9da2c7
Show file tree
Hide file tree
Showing 8 changed files with 232 additions and 38 deletions.
2 changes: 1 addition & 1 deletion cmd/fabric-ca-client/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func init() {

clientCfg = &lib.ClientConfig{}
tags := map[string]string{
"help.csr.cn": "The common name field of the certificate signing request",
"skip.csr.cn": "true", // Skip CN on client side as enrollment ID is used as CN
"help.csr.serialnumber": "The serial number in a certificate signing request, which becomes part of the DN (Distinquished Name)",
"help.csr.hosts": "A list of space-separated host names in a certificate signing request",
}
Expand Down
46 changes: 28 additions & 18 deletions cmd/fabric-ca-server/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,28 +255,27 @@ bccsp:
keystore: msp/keystore
#############################################################################
# The fabric-ca-server init and start commands support the following two
# additional mutually exclusive options:
# Multi CA section
#
# Each Fabric CA server contains one CA by default. This section is used
# to configure multiple CAs in a single server.
#
# 1) --cacount <number-of-CAs>
# Automatically generate multiple default CA instances.
# Automatically generate <number-of-CAs> non-default CAs. The names of these
# additional CAs are "ca1", "ca2", ... "caN", where "N" is <number-of-CAs>
# This is particularly useful in a development environment to quickly set up
# multiple CAs.
# For example,
# fabric-ca-server start -b admin:adminpw --cacount 2
# starts a server with a default CA and two non-default CA's with names
# 'ca1' and 'ca2'.
#
# 2) --cafiles <CA-config-files>
# For each CA config file in the list, generate a separate signing CA. Each CA
# config file in this list MAY contain all of the same elements as are found in
# the server config file except port, debug, and tls sections.
# For example,
# fabric-ca-server start -b admin:adminpw \
# --cafiles ca/ca1/fabric-ca-server-config.yaml \
# --cafiles ca/ca2/fabric-ca-server-config.yaml
# is equivalent to the previous example, except the files CA config files
# must already exist and can be customized.
#
# Examples:
# fabric-ca-server start -b admin:adminpw --cacount 2
#
# fabric-ca-server start -b admin:adminpw --cafiles ca/ca1/fabric-ca-server-config.yaml
# --cafiles ca/ca2/fabric-ca-server-config.yaml
#
#############################################################################
Expand All @@ -285,19 +284,30 @@ cacount:
cafiles:
#############################################################################
# Intermediate CA which acts as a client of the root (or parent) CA.
# Intermediate CA section
#
# The relationship between servers and CAs is as follows:
# 1) A single server process may contain or function as one or more CAs.
# This is configured by the "Multi CA section" above.
# 2) Each CA is either a root CA or an intermediate CA.
# 3) Each intermediate CA has a parent CA which is either a root CA or another intermediate CA.
#
# This section pertains to configuration of #2 and #3.
# If the "intermediate.parentserver.url" property is set,
# then this is an intermediate CA with the specified parent
# CA.
#
# Parentserver section
# parentserver section
# url - The URL of the parent server
# caname - Name of the CA to enroll with on the server
# caname - Name of the CA to enroll within the server
#
# Enrollment section used to enroll an identity with fabric-ca server
# enrollment section used to enroll intermediate CA with parent CA
# hosts - A comma-separated list of host names which the certificate should
# be valid for
# profile - Name of the signing profile to use in issuing the certificate
# label - Label to use in HSM operations
#
# TLS section for secure socket connection
# tls section for secure socket connection
# certfiles - PEM-encoded list of trusted root certificate files
# client:
# certfile - PEM-encoded certificate file for when client authentication
Expand Down
93 changes: 88 additions & 5 deletions docs/source/users-guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,11 @@ Table of Contents
4. `Configuring LDAP`_
5. `Setting up a cluster`_
6. `Setting up multiple CAs`_
7. `Enrolling an intermediate CA`_

6. `Fabric CA Client`_

1. `Enrolling the bootstrap user`_
1. `Enrolling the bootstrap identity`_
2. `Registering a new identity`_
3. `Enrolling a peer identity`_
4. `Reenrolling an identity`_
Expand Down Expand Up @@ -83,6 +84,10 @@ All Fabric CA servers in a cluster share the same database for
keeping track of identities and certificates. If LDAP is configured, the identity
information is kept in LDAP rather than the database.

A server may contain multiple CAs. Each CA is either a root CA or an
intermediate CA. Each intermediate CA has a parent CA which is either a
root CA or another intermediate CA.

Getting Started
---------------

Expand Down Expand Up @@ -176,6 +181,14 @@ The following shows the Fabric CA server usage message.
--db.tls.enabled Enable TLS for client connection
--db.type string Type of database; one of: sqlite3, postgres, mysql (default "sqlite3")
-d, --debug Enable debug level logging
--intermediate.enrollment.hosts string Comma-separated host list
--intermediate.enrollment.label string Label to use in HSM operations
--intermediate.enrollment.profile string Name of the signing profile to use in issuing the certificate
--intermediate.parentserver.caname string Name of the CA to connect to on fabric-ca-serve
-u, --intermediate.parentserver.url string URL of the parent fabric-ca-server (e.g. http://<username>:<password>@<address>:<port)
--intermediate.tls.certfiles stringSlice PEM-encoded list of trusted certificate files
--intermediate.tls.client.certfile string PEM-encoded certificate file when mutual authenticate is enabled
--intermediate.tls.client.keyfile string PEM-encoded key file when mutual authentication is enabled
--ldap.enabled Enable the LDAP client for authentication and attributes
--ldap.groupfilter string The LDAP group filter for a single affiliation group (default "(memberUid=%s)")
--ldap.url string LDAP client URL of form ldap://adminDN:adminPassword@host[:port]/base
Expand All @@ -185,7 +198,6 @@ The following shows the Fabric CA server usage message.
--tls.certfile string PEM-encoded TLS certificate file for server's listening port (default "ca-cert.pem")
--tls.enabled Enable TLS on the listening port
--tls.keyfile string PEM-encoded TLS key for server's listening port (default "ca-key.pem")
-u, --url string URL of the parent fabric-ca-server

Use "fabric-ca-server [command] --help" for more information about a command.

Expand All @@ -209,7 +221,6 @@ The following shows the Fabric CA client usage message:
Flags:
--caname string Name of CA
-c, --config string Configuration file (default "$HOME/.fabric-ca-client/fabric-ca-client-config.yaml")
--csr.cn string The common name field of the certificate signing request
--csr.hosts stringSlice A list of space-separated host names in a certificate signing request
--csr.serialnumber string The serial number in a certificate signing request
-d, --debug Enable debug level logging
Expand Down Expand Up @@ -401,11 +412,19 @@ the server's home directory (see `Fabric CA Server <#server>`__ section more inf
key_store_dir: keys

#############################################################################
# Multi CA section
#
# Each Fabric CA server contains one CA by default. This section is used
# to configure multiple CAs in a single server.
#
# The fabric-ca-server init and start commands support the following two
# additional mutually exclusive options:
#
# 1) --cacount <number-of-CAs>
# Automatically generate multiple default CA instances
# Automatically generate <number-of-CAs> non-default CAs. The names of these
# additional CAs are "ca1", "ca2", ... "caN", where "N" is <number-of-CAs>
# This is particularly useful in a development environment to quickly set up
# multiple CAs.
#
# 2) --cafiles <CA-config-files>
# For each CA config file in the list, generate a separate signing CA. Each CA
Expand All @@ -424,6 +443,53 @@ the server's home directory (see `Fabric CA Server <#server>`__ section more inf

cafiles:

#############################################################################
# Intermediate CA section
#
# The relationship between servers and CAs is as follows:
# 1) A single server process may contain or function as one or more CAs.
# This is configured by the "Multi CA section" above.
# 2) Each CA is either a root CA or an intermediate CA.
# 3) Each intermediate CA has a parent CA which is either a root CA or another intermediate CA.
#
# This section pertains to configuration of #2 and #3.
# If the "intermediate.parentserver.url" property is set,
# then this is an intermediate CA with the specified parent
# CA.
#
# parentserver section
# url - The URL of the parent server
# caname - Name of the CA to enroll within the server
#
# enrollment section used to enroll intermediate CA with parent CA
# hosts - A comma-separated list of host names which the certificate should
# be valid for
# profile - Name of the signing profile to use in issuing the certificate
# label - Label to use in HSM operations
#
# tls section for secure socket connection
# certfiles - PEM-encoded list of trusted root certificate files
# client:
# certfile - PEM-encoded certificate file for when client authentication
# is enabled on server
# keyfile - PEM-encoded key file for when client authentication
# is enabled on server
#############################################################################
intermediate:
parentserver:
url:
caname:

enrollment:
hosts:
profile:
label:

tls:
certfiles:
client:
certfile:
keyfile:

Fabric CA client's configuration file format
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -793,7 +859,7 @@ values for sslmode are:
| | signed by a |
| | trusted CA and |
| | the server |
| | hostname |
| | hostname |
| | matches the |
| | one in the |
| | certificate |
Expand Down Expand Up @@ -1028,6 +1094,23 @@ For example, the following command will start two customized CA instances:
fabric-ca-server start -b admin:adminpw --cafiles ca/ca1/fabric-ca-config.yaml
--cafiles ca/ca2/fabric-ca-config.yaml

Enrolling an intermediate CA
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In order to create a CA signing certificate for an intermediate CA, the intermediate
CA must enroll with a parent CA in the same way that a fabric-ca-client enrolls with a CA.
This is done by using the -u option to specify the URL of the parent CA and the enrollment ID
and secret as shown below. The identity associated with this enrollment ID must have an
attribute with a name of "hf.IntermediateCA" and a value of "true". The CN (or Common Name)
of the issued certificate will be set to the enrollment ID. An error will occur if an intermediate
CA tries to explicitly specify a CN value.

::

fabric-ca-server start -b admin:adminpw -u http://<enrollmentID>:<secret>@<parentserver>:<parentport>

For other intermediate CA flags see `Fabric CA server's configuration file format`_ section.

`Back to Top`_

.. _client:
Expand Down
40 changes: 37 additions & 3 deletions lib/ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,13 @@ func (ca *CA) initKeyMaterial(renew bool) error {
if err != nil {
return fmt.Errorf("Validation of certificate and key failed: %s", err)
}
// Load CN from existing enrollment information and set CSR accordingly
// CN needs to be set, having a multi CA setup requires a unique CN and can't
// be left blank
ca.Config.CSR.CN, err = ca.loadCNFromEnrollmentInfo(certFile)
if err != nil {
return err
}
return nil
}

Expand All @@ -194,6 +201,13 @@ func (ca *CA) initKeyMaterial(renew bool) error {
log.Info("The CA key and certificate already exist")
log.Infof("The key is stored by BCCSP provider '%s'", ca.Config.CSP.ProviderName)
log.Infof("The certificate is at: %s", certFile)
// Load CN from existing enrollment information and set CSR accordingly
// CN needs to be set, having a multi CA setup requires a unique CN and can't
// be left blank
ca.Config.CSR.CN, err = ca.loadCNFromEnrollmentInfo(certFile)
if err != nil {
return err
}
return nil
}
}
Expand Down Expand Up @@ -230,6 +244,9 @@ func (ca *CA) getCACert() (cert []byte, err error) {
clientCfg.Enrollment = ca.Config.Intermediate.Enrollment
clientCfg.CAName = ca.Config.Intermediate.ParentServer.CAName
clientCfg.CSR = ca.Config.CSR
if ca.Config.CSR.CN != "" {
return nil, fmt.Errorf("CN '%s' cannot be specified for an intermediate CA. Remove CN from CSR section for enrollment of intermediate CA to be successful", ca.Config.CSR.CN)
}
if clientCfg.Enrollment.Profile == "" {
clientCfg.Enrollment.Profile = "ca"
}
Expand All @@ -245,6 +262,8 @@ func (ca *CA) getCACert() (cert []byte, err error) {
if err != nil {
return nil, err
}
// Set the CN for an intermediate server to be the ID used to enroll with root CA
ca.Config.CSR.CN = resp.Identity.GetName()
ecert := resp.Identity.GetECert()
if ecert == nil {
return nil, errors.New("No enrollment certificate returned by parent server")
Expand All @@ -270,6 +289,9 @@ func (ca *CA) getCACert() (cert []byte, err error) {
log.Debugf("Stored intermediate certificate chain at %s", chainPath)
} else {
// This is a root CA, so create a CSR (Certificate Signing Request)
if ca.Config.CSR.CN == "" {
ca.Config.CSR.CN = "fabric-ca-server"
}
csr := &ca.Config.CSR
req := cfcsr.CertificateRequest{
CN: csr.CN,
Expand Down Expand Up @@ -343,9 +365,6 @@ func (ca *CA) initConfig() (err error) {
if cfg.CA.Keyfile == "" {
cfg.CA.Keyfile = "ca-key.pem"
}
if cfg.CSR.CN == "" {
cfg.CSR.CN = "fabric-ca-server"
}
// Set log level if debug is true
if ca.server.Config.Debug {
log.Level = log.LevelDebug
Expand Down Expand Up @@ -847,6 +866,21 @@ func validateMatchingKeys(cert *x509.Certificate, keyFile string) error {
return nil
}

// Load CN from existing enrollment information
func (ca *CA) loadCNFromEnrollmentInfo(certFile string) (string, error) {
log.Debug("Loading CN from existing enrollment information")
cert, err := util.ReadFile(certFile)
if err != nil {
log.Debugf("No cert found at %s", certFile)
return "", err
}
name, err := util.GetEnrollmentIDFromPEM(cert)
if err != nil {
return "", err
}
return name, nil
}

func writeFile(file string, buf []byte, perm os.FileMode) error {
err := os.MkdirAll(filepath.Dir(file), 0755)
if err != nil {
Expand Down
Loading

0 comments on commit e9da2c7

Please sign in to comment.