Skip to content

Commit

Permalink
[FAB-2545] Add tool to create various crypto configs
Browse files Browse the repository at this point in the history
https://jira.hyperledger.org/browse/FAB-2545

In order to get a test (or even real) system running,
there is a lot of cryptographic material required:
- root certificates for CAs (e.g. fabric-ca)
- MSPs for organizations running peers
- Local MSPs for peers
- MSPs for ordererer organizations
- Local MSPs for ordering nodes

This CR adds a tool named "cryptogen" which
will create these artifacts for you.  It allows
you to specify the number of peer organizations,
the number of peers per organization and the
number of ordering nodes (shims).  It currently
only creates a single orderer organization.

To run, "./cryptogen" and it will display the
command line options

Change-Id: I15f135dc2893f7492566eb8ac5d02b2f4963ccd3
Signed-off-by: Gari Singh <gari.r.singh@gmail.com>
  • Loading branch information
mastersingh24 committed Mar 1, 2017
1 parent 56c9e4c commit be91ccc
Show file tree
Hide file tree
Showing 7 changed files with 808 additions and 0 deletions.
93 changes: 93 additions & 0 deletions 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
}
181 changes: 181 additions & 0 deletions 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
}
79 changes: 79 additions & 0 deletions 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
}

0 comments on commit be91ccc

Please sign in to comment.