Skip to content

Commit

Permalink
[FAB-4844] Store MSP intermediatecerts
Browse files Browse the repository at this point in the history
The "fabric-ca-client enroll" command was not storing the intermediatecerts
as expected by MSP.  It was storing the entire chain in the cacerts directory.
This change set splits the CA chain and stores only the 1st one in the
cacerts directory and stores the rest in the intermediatecerts directory.

Unit tests and FVT tests cases fail w/o this fix and pass with it.

Change-Id: Iede943bad9601db08c6c18f79add5608e8dfeaae
Signed-off-by: Keith Smith <[email protected]>
Signed-off-by: Saad Karim <[email protected]>
  • Loading branch information
Keith Smith committed Jul 26, 2017
1 parent 6087944 commit 3ba0088
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 27 deletions.
62 changes: 50 additions & 12 deletions cmd/fabric-ca-client/getcacert.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ limitations under the License.
package main

import (
"encoding/pem"
"errors"
"fmt"
"net/url"
"os"
Expand Down Expand Up @@ -89,16 +91,11 @@ func runGetCACert() error {
}

// Store the CAChain in the CACerts folder of MSP (Membership Service Provider)
// The 1st cert in the chain goes into MSP 'cacerts' directory.
// The others (if any) go into the MSP 'intermediates' directory.
func storeCAChain(config *lib.ClientConfig, si *lib.GetServerInfoResponse) error {
mspDir := config.MSPDir
if !util.FileExists(mspDir) {
return fmt.Errorf("Directory does not exist: %s", mspDir)
}
caCertsDir := path.Join(mspDir, "cacerts")
err := os.MkdirAll(caCertsDir, 0755)
if err != nil {
return fmt.Errorf("Failed creating CA certificates directory: %s", err)
}
// Get a unique name to use for filenames
serverURL, err := url.Parse(config.URL)
if err != nil {
return err
Expand All @@ -109,11 +106,52 @@ func storeCAChain(config *lib.ClientConfig, si *lib.GetServerInfoResponse) error
}
fname = strings.Replace(fname, ":", "-", -1)
fname = strings.Replace(fname, ".", "-", -1) + ".pem"
path := path.Join(caCertsDir, fname)
err = util.WriteFile(path, si.CAChain, 0644)
// Split the root and intermediate certs
block, intermediateCerts := pem.Decode(si.CAChain)
if block == nil {
return errors.New("No root certificate was found")
}
rootCert := pem.EncodeToMemory(block)
dirPrefix := dirPrefixByProfile(config.Enrollment.Profile)
// Store the root certificate in "cacerts"
certsDir := fmt.Sprintf("%scacerts", dirPrefix)
err = storeFile("CA root certificate", mspDir, certsDir, fname, rootCert)
if err != nil {
return fmt.Errorf("Failed to create CA root file: %s", err)
return err
}
// Store the intermediate certs if there are any
if len(intermediateCerts) > 0 {
certsDir = fmt.Sprintf("%sintermediatecerts", dirPrefix)
err = storeFile("CA intermediate certificates", mspDir, certsDir, fname, intermediateCerts)
if err != nil {
return err
}
}
log.Infof("Stored CA certificate chain at %s", path)
return nil
}

func storeFile(what, mspDir, subDir, fname string, contents []byte) error {
dir := path.Join(mspDir, subDir)
err := os.MkdirAll(dir, 0755)
if err != nil {
return fmt.Errorf("Failed to create directory for %s at '%s': %s", what, dir, err)
}
fpath := path.Join(dir, fname)
err = util.WriteFile(fpath, contents, 0644)
if err != nil {
return fmt.Errorf("Failed to store %s at '%s': %s", what, fpath, err)
}
log.Infof("Stored %s at %s", what, fpath)
return nil
}

// Return the prefix to add to the "cacerts" and "intermediatecerts" directories
// based on the target profile. If the profile is "tls", these directories become
// "tlscacerts" and "tlsintermediatecerts", respectively. There is no prefix for
// any other profile.
func dirPrefixByProfile(profile string) string {
if profile == "tls" {
return "tls"
}
return ""
}
43 changes: 28 additions & 15 deletions cmd/fabric-ca-client/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,30 +311,37 @@ func testEnroll(t *testing.T) {

// TestMOption tests to make sure that the key is stored in the correct
// directory when the "-M" option is used.
// This also ensures the intermediatecerts directory structure is populated
// since we enroll with an intermediate CA.
func TestMOption(t *testing.T) {
os.RemoveAll(moptionDir)
port := 7173
s := startServer(path.Join(moptionDir, "server"), port, t)
if s == nil {
defer os.RemoveAll(moptionDir)
rootCAPort := 7173
rootServer := startServer(path.Join(moptionDir, "rootServer"), rootCAPort, "", t)
if rootServer == nil {
return
}
defer rootServer.Stop()
rootCAURL := fmt.Sprintf("http://admin:adminpw@localhost:%d", rootCAPort)
intCAPort := 7174
intServer := startServer(path.Join(moptionDir, "intServer"), intCAPort, rootCAURL, t)
if intServer == nil {
return
}
defer intServer.Stop()
homedir := path.Join(moptionDir, "client")
mspdir := "msp2" // relative to homedir
err := RunMain([]string{
cmdName, "enroll",
"-u", fmt.Sprintf("http://admin:adminpw@localhost:%d", port),
"-u", fmt.Sprintf("http://admin:adminpw@localhost:%d", intCAPort),
"-c", path.Join(homedir, "config.yaml"),
"-M", mspdir, "-d"})
if err != nil {
t.Fatalf("client enroll -u failed: %s", err)
}
keystore := path.Join(homedir, mspdir, "keystore")
count := getNumFiles(keystore, t)
if count != 1 {
t.Fatalf("client enroll -M failed: expecting 1 file in keystore %s but found %d",
keystore, count)
}
s.Stop()
assertOneFileInDir(path.Join(homedir, mspdir, "keystore"), t)
assertOneFileInDir(path.Join(homedir, mspdir, "cacerts"), t)
assertOneFileInDir(path.Join(homedir, mspdir, "intermediatecerts"), t)
}

// TestReenroll tests fabric-ca-client reenroll
Expand Down Expand Up @@ -939,16 +946,19 @@ func extraArgErrorTest(in *TestData, t *testing.T) {
}
}

// get the number of files in a directory
func getNumFiles(dir string, t *testing.T) int {
// Make sure there is exactly one file in a directory
func assertOneFileInDir(dir string, t *testing.T) {
files, err := ioutil.ReadDir(dir)
if err != nil {
t.Fatalf("Failed to get number of files in directory '%s': %s", dir, err)
}
return len(files)
count := len(files)
if count != 1 {
t.Fatalf("expecting 1 file in %s but found %d", dir, count)
}
}

func startServer(home string, port int, t *testing.T) *lib.Server {
func startServer(home string, port int, parentURL string, t *testing.T) *lib.Server {
affiliations := map[string]interface{}{"org1": nil}
srv := &lib.Server{
HomeDir: home,
Expand All @@ -965,6 +975,9 @@ func startServer(home string, port int, t *testing.T) *lib.Server {
},
},
}
if parentURL != "" {
srv.CA.Config.Intermediate.ParentServer.URL = parentURL
}
err := srv.RegisterBootstrapUser("admin", "adminpw", "")
if err != nil {
t.Fatalf("Failed to register bootstrap user: %s", err)
Expand Down
6 changes: 6 additions & 0 deletions scripts/fvt/intermediateca_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ SCRIPTDIR="$FABRIC_CA/scripts/fvt"
RC=0

TDIR=intermediateca-tests
export FABRIC_CA_CLIENT_HOME=$TDIR/client

mkdir -p $TDIR/root
cd $TDIR/root
Expand All @@ -27,6 +28,11 @@ sleep 3
fabric-ca-client getcacert -u http://admin:adminpw@localhost:7055
test $? -ne 0 && ErrorExit "Failed to talk to intermediate CA1"

intDir=$FABRIC_CA_CLIENT_HOME/msp/intermediatecerts
if [ ! -d $intDir ]; then
ErrorExit "Failed to create directory $intDir"
fi

fabric-ca-server init -b admin:adminpw -u http://admin:adminpw@localhost:7055 -d
test $? -eq 0 && ErrorExit "CA2 should have failed to initialize"

Expand Down

0 comments on commit 3ba0088

Please sign in to comment.