diff --git a/common/tools/cryptogen/ca/ca_test.go b/common/tools/cryptogen/ca/ca_test.go new file mode 100644 index 00000000000..1f3f437cb92 --- /dev/null +++ b/common/tools/cryptogen/ca/ca_test.go @@ -0,0 +1,93 @@ +/* +Copyright IBM Corp. 2017 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package ca_test + +import ( + "crypto/x509" + "os" + "path/filepath" + "testing" + + "github.com/hyperledger/fabric/common/tools/cryptogen/ca" + "github.com/hyperledger/fabric/common/tools/cryptogen/csp" + "github.com/stretchr/testify/assert" +) + +const ( + testCAName = "root0" + testCA2Name = "root1" + testName = "cert0" +) + +var testDir = filepath.Join(os.TempDir(), "ca-test") + +func TestNewCA(t *testing.T) { + + caDir := filepath.Join(testDir, "ca") + rootCA, err := ca.NewCA(caDir, testCAName) + assert.NoError(t, err, "Error generating CA") + assert.NotNil(t, rootCA, "Failed to return CA") + assert.NotNil(t, rootCA.Signer, + "rootCA.Signer should not be empty") + assert.IsType(t, &x509.Certificate{}, rootCA.SignCert, + "rootCA.SignCert should be type x509.Certificate") + + // check to make sure the root public key was stored + pemFile := filepath.Join(caDir, testCAName+"-cert.pem") + assert.Equal(t, true, checkForFile(pemFile), + "Expected to find file "+pemFile) + cleanup(testDir) + +} + +func TestGenerateSignedCertificate(t *testing.T) { + + caDir := filepath.Join(testDir, "ca") + certDir := filepath.Join(testDir, "certs") + // generate private key + priv, _, err := csp.GeneratePrivateKey(certDir) + assert.NoError(t, err, "Failed to generate signed certificate") + + // get EC public key + ecPubKey, err := csp.GetECPublicKey(priv) + assert.NoError(t, err, "Failed to generate signed certificate") + assert.NotNil(t, ecPubKey, "Failed to generate signed certificate") + + // create our CA + rootCA, err := ca.NewCA(caDir, testCA2Name) + assert.NoError(t, err, "Error generating CA") + + err = rootCA.SignCertificate(certDir, testName, ecPubKey) + assert.NoError(t, err, "Failed to generate signed certificate") + + // check to make sure the signed public key was stored + pemFile := filepath.Join(certDir, testName+"-cert.pem") + assert.Equal(t, true, checkForFile(pemFile), + "Expected to find file "+pemFile) + cleanup(testDir) + +} + +func cleanup(dir string) { + os.RemoveAll(dir) +} + +func checkForFile(file string) bool { + if _, err := os.Stat(file); os.IsNotExist(err) { + return false + } + return true +} diff --git a/common/tools/cryptogen/ca/generator.go b/common/tools/cryptogen/ca/generator.go new file mode 100644 index 00000000000..b98654452ef --- /dev/null +++ b/common/tools/cryptogen/ca/generator.go @@ -0,0 +1,181 @@ +/* +Copyright IBM Corp. 2017 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package ca + +import ( + "crypto" + "crypto/ecdsa" + "crypto/rand" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "math/big" + "os" + "time" + + "path/filepath" + + "github.com/hyperledger/fabric/common/tools/cryptogen/csp" +) + +type CA struct { + Name string + //SignKey *ecdsa.PrivateKey + Signer crypto.Signer + SignCert *x509.Certificate +} + +// NewCA creates an instance of CA and saves the signing key pair in +// baseDir/name +func NewCA(baseDir, name string) (*CA, error) { + + err := os.MkdirAll(baseDir, 0755) + if err != nil { + return nil, err + } + + //key, err := genKeyECDSA(caDir, name) + priv, signer, err := csp.GeneratePrivateKey(baseDir) + // get public signing certificate + ecPubKey, err := csp.GetECPublicKey(priv) + if err != nil { + return nil, err + } + + template, err := x509Template() + + if err != nil { + return nil, err + } + + //this is a CA + template.IsCA = true + template.KeyUsage |= x509.KeyUsageCertSign | x509.KeyUsageCRLSign + template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageAny} + + //set the organization for the subject + subject := subjectTemplate() + subject.Organization = []string{name} + subject.CommonName = name + + template.Subject = subject + template.SubjectKeyId = priv.SKI() + + x509Cert, err := genCertificateECDSA(baseDir, name, &template, &template, + ecPubKey, signer) + + if err != nil { + return nil, err + } + + ca := &CA{ + Name: name, + Signer: signer, + SignCert: x509Cert, + } + + return ca, nil +} + +// SignCertificate creates a signed certificate based on a built-in template +// and saves it in baseDir/name +func (ca *CA) SignCertificate(baseDir, name string, pub *ecdsa.PublicKey) error { + + template, err := x509Template() + + if err != nil { + return err + } + + template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth} + + //set the organization for the subject + subject := subjectTemplate() + subject.CommonName = name + + template.Subject = subject + + _, err = genCertificateECDSA(baseDir, name, &template, ca.SignCert, + pub, ca.Signer) + + if err != nil { + return err + } + + return nil +} + +// default template for X509 subject +func subjectTemplate() pkix.Name { + return pkix.Name{ + Country: []string{"US"}, + Locality: []string{"San Francisco"}, + Province: []string{"California"}, + } +} + +// default template for X509 certificates +func x509Template() (x509.Certificate, error) { + + //generate a serial number + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + return x509.Certificate{}, err + } + + now := time.Now() + //basic template to use + x509 := x509.Certificate{ + SerialNumber: serialNumber, + NotBefore: now, + NotAfter: now.Add(3650 * 24 * time.Hour), //~ten years + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + BasicConstraintsValid: true, + } + return x509, nil + +} + +// generate a signed X509 certficate using ECDSA +func genCertificateECDSA(baseDir, name string, template, parent *x509.Certificate, pub *ecdsa.PublicKey, + priv interface{}) (*x509.Certificate, error) { + + //create the x509 public cert + certBytes, err := x509.CreateCertificate(rand.Reader, template, parent, pub, priv) + if err != nil { + return nil, err + } + + //write cert out to file + fileName := filepath.Join(baseDir, name+"-cert.pem") + certFile, err := os.Create(fileName) + if err != nil { + return nil, err + } + //pem encode the cert + err = pem.Encode(certFile, &pem.Block{Type: "CERTIFICATE", Bytes: certBytes}) + certFile.Close() + if err != nil { + return nil, err + } + + x509Cert, err := x509.ParseCertificate(certBytes) + if err != nil { + return nil, err + } + return x509Cert, nil +} diff --git a/common/tools/cryptogen/csp/csp.go b/common/tools/cryptogen/csp/csp.go new file mode 100644 index 00000000000..44e5a2c875f --- /dev/null +++ b/common/tools/cryptogen/csp/csp.go @@ -0,0 +1,79 @@ +/* +Copyright IBM Corp. 2017 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package csp + +import ( + "crypto" + "crypto/ecdsa" + "crypto/x509" + + "github.com/hyperledger/fabric/bccsp" + "github.com/hyperledger/fabric/bccsp/factory" + "github.com/hyperledger/fabric/bccsp/signer" + "github.com/hyperledger/fabric/bccsp/sw" +) + +// GeneratePrivateKey creates a private key and stores it in keystorePath +func GeneratePrivateKey(keystorePath string) (bccsp.Key, + crypto.Signer, error) { + + csp := factory.GetDefault() + + // generate a key + priv, err := csp.KeyGen(&bccsp.ECDSAP256KeyGenOpts{Temporary: true}) + if err != nil { + return nil, nil, err + } + // write it to the keystore + ks, err := sw.NewFileBasedKeyStore(nil, keystorePath, false) + if err != nil { + return nil, nil, err + } + err = ks.StoreKey(priv) + if err != nil { + return nil, nil, err + } + + // create a crypto.Signer + signer := &signer.CryptoSigner{} + err = signer.Init(csp, priv) + if err != nil { + return nil, nil, err + } + + return priv, signer, nil + +} + +func GetECPublicKey(priv bccsp.Key) (*ecdsa.PublicKey, error) { + + // get the public key + pubKey, err := priv.PublicKey() + if err != nil { + return nil, err + } + // marshal to bytes + pubKeyBytes, err := pubKey.Bytes() + if err != nil { + return nil, err + } + // unmarshal using pkix + ecPubKey, err := x509.ParsePKIXPublicKey(pubKeyBytes) + if err != nil { + return nil, err + } + return ecPubKey.(*ecdsa.PublicKey), nil +} diff --git a/common/tools/cryptogen/csp/csp_test.go b/common/tools/cryptogen/csp/csp_test.go new file mode 100644 index 00000000000..7f6cad6ca7b --- /dev/null +++ b/common/tools/cryptogen/csp/csp_test.go @@ -0,0 +1,67 @@ +/* +Copyright IBM Corp. 2017 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package csp_test + +import ( + "crypto/ecdsa" + "encoding/hex" + "os" + "path/filepath" + "testing" + + "github.com/hyperledger/fabric/common/tools/cryptogen/csp" + "github.com/stretchr/testify/assert" +) + +var testDir = filepath.Join(os.TempDir(), "csp-test") + +func TestGeneratePrivateKey(t *testing.T) { + + priv, signer, err := csp.GeneratePrivateKey(testDir) + assert.NoError(t, err, "Failed to generate private key") + assert.NotNil(t, priv, "Should have returned a bccsp.Key") + assert.Equal(t, true, priv.Private(), "Failed to return private key") + assert.NotNil(t, signer, "Should have returned a crypto.Signer") + pkFile := filepath.Join(testDir, hex.EncodeToString(priv.SKI())+"_sk") + t.Log(pkFile) + assert.Equal(t, true, checkForFile(pkFile), + "Expected to find private key file") + cleanup(testDir) + +} + +func TestGetECPublicKey(t *testing.T) { + + priv, _, err := csp.GeneratePrivateKey(testDir) + assert.NoError(t, err, "Failed to generate private key") + + ecPubKey, err := csp.GetECPublicKey(priv) + assert.NoError(t, err, "Failed to get public key from private key") + assert.IsType(t, &ecdsa.PublicKey{}, ecPubKey, + "Failed to return an ecdsa.PublicKey") + cleanup(testDir) +} + +func cleanup(dir string) { + os.RemoveAll(dir) +} + +func checkForFile(file string) bool { + if _, err := os.Stat(file); os.IsNotExist(err) { + return false + } + return true +} diff --git a/common/tools/cryptogen/main.go b/common/tools/cryptogen/main.go new file mode 100644 index 00000000000..a6f414db26b --- /dev/null +++ b/common/tools/cryptogen/main.go @@ -0,0 +1,152 @@ +/* +Copyright IBM Corp. 2017 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package main + +import ( + "flag" + "fmt" + "os" + "path/filepath" + + "github.com/hyperledger/fabric/common/tools/cryptogen/ca" + "github.com/hyperledger/fabric/common/tools/cryptogen/msp" +) + +const ( + peerOrgBaseName = "peerOrg" + peerBaseName = "Peer" + orderOrgBaseName = "ordererOrg" + ordererBaseName = "orderer" +) + +//command line flags +var ( + numPeerOrgs = flag.Int("peerOrgs", 2, + "number of unique organizations with peers") + numPeers = flag.Int("peersPerOrg", 1, + "number of peers per organization") + numOrderers = flag.Int("ordererNodes", 1, + "number of ordering service nodes") + baseDir = flag.String("baseDir", ".", + "directory in which to place artifacts") +) + +var numOrdererOrgs = 1 + +func main() { + flag.Parse() + + if flag.NFlag() == 0 { + fmt.Println("\nYou must specify at least one parameter") + flag.Usage() + os.Exit(1) + } + + genDir := filepath.Join(*baseDir, "crypto-config") + if *numPeerOrgs > 0 { + fmt.Printf("Generating %d peer organization(s) each with %d peer(s) ...\n", + *numPeerOrgs, *numPeers) + + // TODO: add ability to specify peer org names + // for name just use default base name + peerOrgNames := []string{} + for i := 1; i <= *numPeerOrgs; i++ { + peerOrgNames = append(peerOrgNames, fmt.Sprintf("%s%d", peerOrgBaseName, i)) + } + generatePeerOrgs(genDir, peerOrgNames) + + } + + if *numOrderers > 0 { + fmt.Printf("Generating %d orderer organization(s) and %d ordering node(s) ...\n", + numOrdererOrgs, *numOrderers) + generateOrdererOrg(genDir, fmt.Sprintf("%s1", orderOrgBaseName)) + } + +} + +func generatePeerOrgs(baseDir string, orgNames []string) { + + for _, orgName := range orgNames { + fmt.Println(orgName) + // generate CA + orgDir := filepath.Join(baseDir, "peerOrganizations", orgName) + caDir := filepath.Join(orgDir, "ca") + mspDir := filepath.Join(orgDir, "msp") + peersDir := filepath.Join(orgDir, "peers") + rootCA, err := ca.NewCA(caDir, orgName) + if err != nil { + fmt.Printf("Error generating CA for org %s:\n%v\n", orgName, err) + os.Exit(1) + } + err = msp.GenerateVerifyingMSP(mspDir, rootCA) + if err != nil { + fmt.Printf("Error generating MSP for org %s:\n%v\n", orgName, err) + os.Exit(1) + } + + // TODO: add ability to specify peer names + // for name just use default base name + peerNames := []string{} + for i := 1; i <= *numPeerOrgs; i++ { + peerNames = append(peerNames, fmt.Sprintf("%s%s%d", + orgName, peerBaseName, i)) + } + generateNodes(peersDir, peerNames, rootCA) + } +} + +func generateNodes(baseDir string, nodeNames []string, rootCA *ca.CA) { + + for _, nodeName := range nodeNames { + nodeDir := filepath.Join(baseDir, nodeName) + err := msp.GenerateLocalMSP(nodeDir, nodeName, rootCA) + if err != nil { + fmt.Printf("Error generating local MSP for %s:\n%v\n", nodeName, err) + os.Exit(1) + } + } + +} + +func generateOrdererOrg(baseDir, orgName string) { + + // generate CA + orgDir := filepath.Join(baseDir, "ordererOrganizations", orgName) + caDir := filepath.Join(orgDir, "ca") + mspDir := filepath.Join(orgDir, "msp") + orderersDir := filepath.Join(orgDir, "orderers") + rootCA, err := ca.NewCA(caDir, orgName) + if err != nil { + fmt.Printf("Error generating CA for org %s:\n%v\n", orgName, err) + os.Exit(1) + } + err = msp.GenerateVerifyingMSP(mspDir, rootCA) + if err != nil { + fmt.Printf("Error generating MSP for org %s:\n%v\n", orgName, err) + os.Exit(1) + } + + // TODO: add ability to specify orderer names + // for name just use default base name + ordererNames := []string{} + for i := 1; i <= *numOrderers; i++ { + ordererNames = append(ordererNames, fmt.Sprintf("%s%s%d", + orgName, ordererBaseName, i)) + } + generateNodes(orderersDir, ordererNames, rootCA) + +} diff --git a/common/tools/cryptogen/msp/generator.go b/common/tools/cryptogen/msp/generator.go new file mode 100644 index 00000000000..e13e54af123 --- /dev/null +++ b/common/tools/cryptogen/msp/generator.go @@ -0,0 +1,127 @@ +/* +Copyright IBM Corp. 2017 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package msp + +import ( + "crypto/x509" + "encoding/pem" + "os" + "path/filepath" + + "github.com/hyperledger/fabric/common/tools/cryptogen/ca" + "github.com/hyperledger/fabric/common/tools/cryptogen/csp" +) + +func GenerateLocalMSP(baseDir, name string, rootCA *ca.CA) error { + + // create folder structure + err := createFolderStructure(baseDir) + if err != nil { + return err + } + + // generate private key + priv, _, err := csp.GeneratePrivateKey(filepath.Join(baseDir, "keystore")) + if err != nil { + return err + } + + // get public signing certificate + ecPubKey, err := csp.GetECPublicKey(priv) + if err != nil { + return err + } + err = rootCA.SignCertificate(filepath.Join(baseDir, "signcerts"), + name, ecPubKey) + if err != nil { + return err + } + + // write root cert to folders + err = x509ToFile(filepath.Join(baseDir, "admincerts"), rootCA.Name, rootCA.SignCert) + if err != nil { + return err + } + err = x509ToFile(filepath.Join(baseDir, "cacerts"), rootCA.Name, rootCA.SignCert) + if err != nil { + return err + } + return nil + +} + +func GenerateVerifyingMSP(baseDir string, rootCA *ca.CA) error { + + // create folder structure + err := createFolderStructure(baseDir) + if err != nil { + return err + } + + // write public cert to appropriate folders + err = x509ToFile(filepath.Join(baseDir, "admincerts"), rootCA.Name, rootCA.SignCert) + if err != nil { + return err + } + err = x509ToFile(filepath.Join(baseDir, "cacerts"), rootCA.Name, rootCA.SignCert) + if err != nil { + return err + } + err = x509ToFile(filepath.Join(baseDir, "signcerts"), rootCA.Name, rootCA.SignCert) + if err != nil { + return err + } + + return nil +} + +func createFolderStructure(rootDir string) error { + + // create admincerts, cacerts, keystore and signcerts folders + folders := []string{ + filepath.Join(rootDir, "admincerts"), + filepath.Join(rootDir, "cacerts"), + filepath.Join(rootDir, "keystore"), + filepath.Join(rootDir, "signcerts"), + } + + for _, folder := range folders { + err := os.MkdirAll(folder, 0755) + if err != nil { + return err + } + } + return nil +} + +func x509ToFile(baseDir, name string, cert *x509.Certificate) error { + + //write cert out to file + fileName := filepath.Join(baseDir, name+"-cert.pem") + certFile, err := os.Create(fileName) + if err != nil { + return err + } + //pem encode the cert + err = pem.Encode(certFile, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}) + certFile.Close() + if err != nil { + return err + } + + return nil + +} diff --git a/common/tools/cryptogen/msp/msp_test.go b/common/tools/cryptogen/msp/msp_test.go new file mode 100644 index 00000000000..f41f646679d --- /dev/null +++ b/common/tools/cryptogen/msp/msp_test.go @@ -0,0 +1,109 @@ +/* +Copyright IBM Corp. 2017 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package msp_test + +import ( + "os" + "path/filepath" + "testing" + + "github.com/hyperledger/fabric/common/tools/cryptogen/ca" + "github.com/hyperledger/fabric/common/tools/cryptogen/msp" + fabricmsp "github.com/hyperledger/fabric/msp" + "github.com/stretchr/testify/assert" +) + +const ( + testCAName = "root0" + testName = "peer0" +) + +var testDir = filepath.Join(os.TempDir(), "msp-test") + +func TestGenerateLocalMSP(t *testing.T) { + + cleanup(testDir) + caDir := filepath.Join(testDir, "ca") + mspDir := filepath.Join(testDir, "msp") + rootCA, err := ca.NewCA(caDir, testCAName) + assert.NoError(t, err, "Error generating CA") + err = msp.GenerateLocalMSP(mspDir, testName, rootCA) + assert.NoError(t, err, "Failed to generate local MSP") + + // check to see that the right files were generated/saved + files := []string{ + filepath.Join(mspDir, "admincerts", testCAName+"-cert.pem"), + filepath.Join(mspDir, "cacerts", testCAName+"-cert.pem"), + filepath.Join(mspDir, "keystore"), + filepath.Join(mspDir, "signcerts", testName+"-cert.pem"), + } + + for _, file := range files { + assert.Equal(t, true, checkForFile(file), + "Expected to find file "+file) + } + + // finally check to see if we can load this as a local MSP config + testMSPConfig, err := fabricmsp.GetLocalMspConfig(mspDir, nil, testName) + assert.NoError(t, err, "Error parsing local MSP config") + testMSP, err := fabricmsp.NewBccspMsp() + assert.NoError(t, err, "Error creating new BCCSP MSP") + err = testMSP.Setup(testMSPConfig) + assert.NoError(t, err, "Error setting up local MSP") + cleanup(testDir) + +} + +func TestGenerateVerifyingMSP(t *testing.T) { + + caDir := filepath.Join(testDir, "ca") + mspDir := filepath.Join(testDir, "msp") + rootCA, err := ca.NewCA(caDir, testCAName) + + err = msp.GenerateVerifyingMSP(mspDir, rootCA) + assert.NoError(t, err, "Failed to generate verifying MSP") + + // check to see that the right files were generated/saved + files := []string{ + filepath.Join(mspDir, "admincerts", testCAName+"-cert.pem"), + filepath.Join(mspDir, "cacerts", testCAName+"-cert.pem"), + filepath.Join(mspDir, "signcerts", testCAName+"-cert.pem"), + } + + for _, file := range files { + assert.Equal(t, true, checkForFile(file), + "Expected to find file "+file) + } + // finally check to see if we can load this as a verifying MSP config + testMSPConfig, err := fabricmsp.GetVerifyingMspConfig(mspDir, nil, testName) + assert.NoError(t, err, "Error parsing verifying MSP config") + testMSP, err := fabricmsp.NewBccspMsp() + assert.NoError(t, err, "Error creating new BCCSP MSP") + err = testMSP.Setup(testMSPConfig) + assert.NoError(t, err, "Error setting up verifying MSP") + cleanup(testDir) +} + +func cleanup(dir string) { + os.RemoveAll(dir) +} + +func checkForFile(file string) bool { + if _, err := os.Stat(file); os.IsNotExist(err) { + return false + } + return true +}