go_study/fabric-main/integration/msp/rsaca_test.go

467 lines
14 KiB
Go

/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package msp
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"io/ioutil"
"math/big"
"net"
"os"
"path/filepath"
"syscall"
"time"
docker "github.com/fsouza/go-dockerclient"
"github.com/hyperledger/fabric/integration/channelparticipation"
"github.com/hyperledger/fabric/integration/nwo"
"github.com/hyperledger/fabric/integration/nwo/commands"
fabricmsp "github.com/hyperledger/fabric/msp"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gexec"
"github.com/tedsuo/ifrit"
ginkgomon "github.com/tedsuo/ifrit/ginkgomon_v2"
"gopkg.in/yaml.v2"
)
var _ = Describe("MSPs with RSA Certificate Authorities", func() {
var (
client *docker.Client
testDir string
network *nwo.Network
ordererRunner *ginkgomon.Runner
ordererProcess, peerProcess ifrit.Process
)
BeforeEach(func() {
var err error
testDir, err = ioutil.TempDir("", "msp")
Expect(err).NotTo(HaveOccurred())
client, err = docker.NewClientFromEnv()
Expect(err).NotTo(HaveOccurred())
network = nwo.New(nwo.BasicEtcdRaft(), testDir, client, StartPort(), components)
network.GenerateConfigTree()
By("manually bootstrapping MSPs with RSA CAs")
generateRSACACrypto(network)
network.CreateDockerNetwork()
for _, c := range network.Channels {
sess, err := network.ConfigTxGen(commands.OutputBlock{
ChannelID: c.Name,
Profile: c.Profile,
ConfigPath: network.RootDir,
OutputBlock: network.OutputBlockPath(c.Name),
})
Expect(err).NotTo(HaveOccurred())
Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0))
}
network.ConcatenateTLSCACertificates()
By("starting all processes for fabric")
ordererRunner, ordererProcess, peerProcess = network.StartSingleOrdererNetwork("orderer")
})
AfterEach(func() {
if ordererProcess != nil {
ordererProcess.Signal(syscall.SIGTERM)
Eventually(ordererProcess.Wait(), network.EventuallyTimeout).Should(Receive())
}
if peerProcess != nil {
peerProcess.Signal(syscall.SIGTERM)
Eventually(peerProcess.Wait(), network.EventuallyTimeout).Should(Receive())
}
if network != nil {
network.Cleanup()
}
os.RemoveAll(testDir)
})
It("executes transactions endorsed with ECDSA signing certs", func() {
org1Peer0 := network.Peer("Org1", "peer0")
orderer := network.Orderer("orderer")
chaincode := nwo.Chaincode{
Name: "mycc",
Version: "0.0",
Path: components.Build("github.com/hyperledger/fabric/integration/chaincode/simple/cmd"),
Lang: "binary",
PackageFile: filepath.Join(testDir, "simplecc.tar.gz"),
Ctor: `{"Args":["init","a","100","b","200"]}`,
SignaturePolicy: `AND ('Org1MSP.member','Org2MSP.member')`,
Sequence: "1",
InitRequired: true,
Label: "my_prebuilt_chaincode",
}
channelparticipation.JoinOrdererJoinPeersAppChannel(network, "testchannel", orderer, ordererRunner)
nwo.EnableCapabilities(
network,
"testchannel",
"Application", "V2_0",
orderer,
network.Peer("Org1", "peer0"),
network.Peer("Org2", "peer0"),
)
nwo.DeployChaincode(network, "testchannel", orderer, chaincode)
RunQueryInvokeQuery(network, orderer, org1Peer0, 100)
})
})
// What follows is a bunch of code to build a hand-crafted set of MSPs where
// everything except signing certificates use RSA keys. It's not pretty but it
// gets the job done for testing.
func generateRSACACrypto(n *nwo.Network) {
cryptoDir := n.CryptoPath()
for _, o := range n.OrdererOrgs() {
orgDir := filepath.Join(cryptoDir, "ordererOrganizations", o.Domain)
signCA, tlsCA, adminCert := createMSP(orgDir, o.Domain, o.EnableNodeOUs)
for i := 1; i <= o.Users; i++ {
name := fmt.Sprintf("User%d@%s", i, o.Domain)
dir := filepath.Join(orgDir, "users", name)
var ous []string
if o.EnableNodeOUs {
ous = append(ous, "client")
}
writeLocalMSP(dir, name, ous, nil, signCA, tlsCA, adminCert, o.EnableNodeOUs, true)
}
for _, orderer := range n.OrderersInOrg(o.Name) {
name := orderer.Name + "." + o.Domain
dir := filepath.Join(orgDir, "orderers", name)
sans := []string{"127.0.0.1", "::1", "localhost"}
var ous []string
if o.EnableNodeOUs {
ous = append(ous, "orderer")
}
writeLocalMSP(dir, name, ous, sans, signCA, tlsCA, adminCert, o.EnableNodeOUs, false)
}
}
for _, o := range n.PeerOrgs() {
orgDir := filepath.Join(cryptoDir, "peerOrganizations", o.Domain)
signCA, tlsCA, adminCert := createMSP(orgDir, o.Domain, o.EnableNodeOUs)
for i := 1; i <= o.Users; i++ {
name := fmt.Sprintf("User%d@%s", i, o.Domain)
dir := filepath.Join(orgDir, "users", name)
var ous []string
if o.EnableNodeOUs {
ous = append(ous, "client")
}
writeLocalMSP(dir, name, ous, nil, signCA, tlsCA, adminCert, o.EnableNodeOUs, true)
}
for _, peer := range n.PeersInOrg(o.Name) {
name := peer.Name + "." + o.Domain
dir := filepath.Join(orgDir, "peers", name)
sans := []string{"127.0.0.1", "::1", "localhost"}
var ous []string
if o.EnableNodeOUs {
ous = append(ous, "peer")
}
writeLocalMSP(dir, name, ous, sans, signCA, tlsCA, adminCert, o.EnableNodeOUs, false)
}
}
}
func createMSP(baseDir, domain string, nodeOUs bool) (signCA *CA, tlsCA *CA, adminPemCert []byte) {
caDir := filepath.Join(baseDir, "ca")
signCA = newCA(domain, "ca")
writeCA(signCA, caDir)
tlsCADir := filepath.Join(baseDir, "tlsca")
tlsCA = newCA(domain, "tlsca")
writeCA(tlsCA, tlsCADir)
mspDir := filepath.Join(baseDir, "msp")
writeVerifyingMSP(mspDir, signCA, tlsCA, nodeOUs)
adminUsername := "Admin@" + domain
adminDir := filepath.Join(baseDir, "users", adminUsername)
err := os.MkdirAll(adminDir, 0o755)
Expect(err).NotTo(HaveOccurred())
var ous []string
if nodeOUs {
ous = append(ous, "admin")
}
writeLocalMSP(adminDir, adminUsername, ous, nil, signCA, tlsCA, nil, nodeOUs, true)
adminPemCert, err = ioutil.ReadFile(filepath.Join(adminDir, "msp", "signcerts", certFilename(adminUsername)))
Expect(err).NotTo(HaveOccurred())
err = ioutil.WriteFile(filepath.Join(adminDir, "msp", "admincerts", certFilename(adminUsername)), adminPemCert, 0o644)
Expect(err).NotTo(HaveOccurred())
if !nodeOUs {
err := ioutil.WriteFile(filepath.Join(mspDir, "admincerts", certFilename(adminUsername)), adminPemCert, 0o644)
Expect(err).NotTo(HaveOccurred())
}
return signCA, tlsCA, adminPemCert
}
func writeCA(ca *CA, dir string) {
err := os.MkdirAll(dir, 0o755)
Expect(err).NotTo(HaveOccurred())
certFilename := filepath.Join(dir, ca.certFilename())
writeCertificate(certFilename, ca.certBytes)
keyFilename := filepath.Join(dir, fmt.Sprintf("%x_sk", ca.cert.SubjectKeyId))
writeKey(keyFilename, ca.signer)
}
func writeCertificate(filename string, der []byte) {
certFile, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, 0o644)
Expect(err).NotTo(HaveOccurred())
defer certFile.Close()
err = pem.Encode(certFile, &pem.Block{Type: "CERTIFICATE", Bytes: der})
Expect(err).NotTo(HaveOccurred())
}
func writeKey(filename string, signer crypto.Signer) {
keyFile, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, 0o600)
Expect(err).NotTo(HaveOccurred())
defer keyFile.Close()
derKey, err := x509.MarshalPKCS8PrivateKey(signer)
Expect(err).NotTo(HaveOccurred())
err = pem.Encode(keyFile, &pem.Block{Type: "PRIVATE KEY", Bytes: derKey})
Expect(err).NotTo(HaveOccurred())
}
func writeVerifyingMSP(mspDir string, signCA, tlsCA *CA, nodeOUs bool) {
for _, dir := range []string{"admincerts", "cacerts", "tlscacerts"} {
err := os.MkdirAll(filepath.Join(mspDir, dir), 0o755)
Expect(err).NotTo(HaveOccurred())
}
if nodeOUs {
configFilename := filepath.Join(mspDir, "config.yaml")
writeConfigYaml(configFilename, filepath.Join("cacerts", signCA.certFilename()), nodeOUs)
}
writeCertificate(filepath.Join(mspDir, "cacerts", signCA.certFilename()), signCA.certBytes)
writeCertificate(filepath.Join(mspDir, "tlscacerts", tlsCA.certFilename()), tlsCA.certBytes)
}
func writeLocalMSP(baseDir, name string, signOUs, sans []string, signCA, tlsCA *CA, adminCertPem []byte, nodeOUs, client bool) {
mspDir := filepath.Join(baseDir, "msp")
err := os.MkdirAll(mspDir, 0o755)
Expect(err).NotTo(HaveOccurred())
writeVerifyingMSP(mspDir, signCA, tlsCA, nodeOUs)
for _, dir := range []string{"admincerts", "keystore", "signcerts"} {
err := os.MkdirAll(filepath.Join(mspDir, dir), 0o755)
Expect(err).NotTo(HaveOccurred())
}
if !nodeOUs && len(adminCertPem) != 0 {
block, _ := pem.Decode(adminCertPem)
adminCert, err := x509.ParseCertificate(block.Bytes)
Expect(err).NotTo(HaveOccurred())
err = ioutil.WriteFile(filepath.Join(mspDir, "admincerts", certFilename(adminCert.Subject.CommonName)), adminCertPem, 0o644)
Expect(err).NotTo(HaveOccurred())
}
// create signcert
priv := generateECKey()
signcertBytes, signcert := signCA.issueSignCertificate(name, signOUs, priv.Public())
signcertFilename := filepath.Join(mspDir, "signcerts", certFilename(name))
writeCertificate(signcertFilename, signcertBytes)
signcertKeyFilename := filepath.Join(mspDir, "keystore", fmt.Sprintf("%x_sk", signcert.SubjectKeyId))
writeKey(signcertKeyFilename, priv)
// populate tls
tlsDir := filepath.Join(baseDir, "tls")
err = os.MkdirAll(tlsDir, 0o755)
Expect(err).NotTo(HaveOccurred())
writeCertificate(filepath.Join(tlsDir, "ca.crt"), tlsCA.certBytes)
tlsKey := generateRSAKey()
tlsCertBytes, _ := tlsCA.issueTLSCertificate(name, sans, tlsKey.Public())
if client {
writeCertificate(filepath.Join(tlsDir, "client.crt"), tlsCertBytes)
writeKey(filepath.Join(tlsDir, "client.key"), tlsKey)
} else {
writeCertificate(filepath.Join(tlsDir, "server.crt"), tlsCertBytes)
writeKey(filepath.Join(tlsDir, "server.key"), tlsKey)
}
}
func writeConfigYaml(configFilename, caFile string, enable bool) {
config := &fabricmsp.Configuration{
NodeOUs: &fabricmsp.NodeOUs{
Enable: enable,
ClientOUIdentifier: &fabricmsp.OrganizationalUnitIdentifiersConfiguration{
Certificate: caFile,
OrganizationalUnitIdentifier: "client",
},
PeerOUIdentifier: &fabricmsp.OrganizationalUnitIdentifiersConfiguration{
Certificate: caFile,
OrganizationalUnitIdentifier: "peer",
},
AdminOUIdentifier: &fabricmsp.OrganizationalUnitIdentifiersConfiguration{
Certificate: caFile,
OrganizationalUnitIdentifier: "admin",
},
OrdererOUIdentifier: &fabricmsp.OrganizationalUnitIdentifiersConfiguration{
Certificate: caFile,
OrganizationalUnitIdentifier: "orderer",
},
},
}
configFile, err := os.Create(configFilename)
Expect(err).NotTo(HaveOccurred())
defer configFile.Close()
err = yaml.NewEncoder(configFile).Encode(config)
Expect(err).NotTo(HaveOccurred())
}
type CA struct {
signer crypto.Signer
cert *x509.Certificate
certBytes []byte
}
func newCA(orgName, caName string) *CA {
signer := generateRSAKey()
template := x509Template()
template.IsCA = true
template.KeyUsage |= x509.KeyUsageDigitalSignature
template.KeyUsage |= x509.KeyUsageKeyEncipherment
template.KeyUsage |= x509.KeyUsageCertSign
template.KeyUsage |= x509.KeyUsageCRLSign
template.ExtKeyUsage = []x509.ExtKeyUsage{
x509.ExtKeyUsageClientAuth,
x509.ExtKeyUsageServerAuth,
}
template.Subject = pkix.Name{
CommonName: caName + "." + orgName,
Organization: []string{orgName},
}
template.SubjectKeyId = computeSKI(signer.Public())
certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, signer.Public(), signer)
Expect(err).NotTo(HaveOccurred())
cert, err := x509.ParseCertificate(certBytes)
Expect(err).NotTo(HaveOccurred())
return &CA{
signer: signer,
cert: cert,
certBytes: certBytes,
}
}
func (ca *CA) issueSignCertificate(name string, ous []string, pub crypto.PublicKey) ([]byte, *x509.Certificate) {
template := x509Template()
template.KeyUsage = x509.KeyUsageDigitalSignature
template.ExtKeyUsage = nil
template.Subject = pkix.Name{
CommonName: name,
Organization: ca.cert.Subject.Organization,
OrganizationalUnit: ous,
}
template.SubjectKeyId = computeSKI(pub)
certBytes, err := x509.CreateCertificate(rand.Reader, &template, ca.cert, pub, ca.signer)
Expect(err).NotTo(HaveOccurred())
cert, err := x509.ParseCertificate(certBytes)
Expect(err).NotTo(HaveOccurred())
return certBytes, cert
}
func (ca *CA) issueTLSCertificate(name string, sans []string, pub crypto.PublicKey) ([]byte, *x509.Certificate) {
template := x509Template()
template.KeyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment
template.ExtKeyUsage = []x509.ExtKeyUsage{
x509.ExtKeyUsageServerAuth,
x509.ExtKeyUsageClientAuth,
}
template.Subject = pkix.Name{
CommonName: name,
Organization: ca.cert.Subject.Organization,
}
template.SubjectKeyId = computeSKI(pub)
for _, san := range sans {
if ip := net.ParseIP(san); ip != nil {
template.IPAddresses = append(template.IPAddresses, ip)
} else {
template.DNSNames = append(template.DNSNames, san)
}
}
certBytes, err := x509.CreateCertificate(rand.Reader, &template, ca.cert, pub, ca.signer)
Expect(err).NotTo(HaveOccurred())
cert, err := x509.ParseCertificate(certBytes)
Expect(err).NotTo(HaveOccurred())
return certBytes, cert
}
func (ca *CA) certFilename() string {
return certFilename(ca.cert.Subject.CommonName)
}
func certFilename(stem string) string {
return stem + "-cert.pem"
}
func generateRSAKey() crypto.Signer {
signer, err := rsa.GenerateKey(rand.Reader, 4096)
Expect(err).NotTo(HaveOccurred())
return signer
}
func generateECKey() crypto.Signer {
signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
Expect(err).NotTo(HaveOccurred())
return signer
}
func x509Template() x509.Certificate {
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, _ := rand.Int(rand.Reader, serialNumberLimit)
notBefore := time.Now().Round(time.Minute).Add(-5 * time.Minute).UTC()
return x509.Certificate{
SerialNumber: serialNumber,
NotBefore: notBefore,
NotAfter: notBefore.Add(3650 * 24 * time.Hour).UTC(),
BasicConstraintsValid: true,
}
}
func computeSKI(key crypto.PublicKey) []byte {
var raw []byte
switch key := key.(type) {
case *rsa.PublicKey:
raw = x509.MarshalPKCS1PublicKey(key)
case *ecdsa.PublicKey:
raw = elliptic.Marshal(key.Curve, key.X, key.Y)
default:
panic(fmt.Sprintf("unexpected type: %T", key))
}
hash := sha256.Sum256(raw)
return hash[:]
}