github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/integration/msp/rsaca_test.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package msp
     8  
     9  import (
    10  	"crypto"
    11  	"crypto/ecdsa"
    12  	"crypto/elliptic"
    13  	"crypto/rand"
    14  	"crypto/rsa"
    15  	"crypto/sha256"
    16  	"crypto/x509"
    17  	"crypto/x509/pkix"
    18  	"encoding/pem"
    19  	"fmt"
    20  	"io/ioutil"
    21  	"math/big"
    22  	"net"
    23  	"os"
    24  	"path/filepath"
    25  	"syscall"
    26  	"time"
    27  
    28  	docker "github.com/fsouza/go-dockerclient"
    29  	"github.com/hechain20/hechain/integration/nwo"
    30  	"github.com/hechain20/hechain/integration/nwo/commands"
    31  	fabricmsp "github.com/hechain20/hechain/msp"
    32  	. "github.com/onsi/ginkgo"
    33  	. "github.com/onsi/gomega"
    34  	"github.com/onsi/gomega/gexec"
    35  	"github.com/tedsuo/ifrit"
    36  	"gopkg.in/yaml.v2"
    37  )
    38  
    39  var _ = Describe("MSPs with RSA Certificate Authorities", func() {
    40  	var (
    41  		client  *docker.Client
    42  		testDir string
    43  		network *nwo.Network
    44  		process ifrit.Process
    45  	)
    46  
    47  	BeforeEach(func() {
    48  		var err error
    49  		testDir, err = ioutil.TempDir("", "msp")
    50  		Expect(err).NotTo(HaveOccurred())
    51  
    52  		client, err = docker.NewClientFromEnv()
    53  		Expect(err).NotTo(HaveOccurred())
    54  
    55  		network = nwo.New(nwo.BasicEtcdRaft(), testDir, client, StartPort(), components)
    56  		network.GenerateConfigTree()
    57  
    58  		By("manually bootstrapping MSPs with RSA CAs")
    59  		generateRSACACrypto(network)
    60  		network.CreateDockerNetwork()
    61  		sess, err := network.ConfigTxGen(commands.OutputBlock{
    62  			ChannelID:   network.SystemChannel.Name,
    63  			Profile:     network.SystemChannel.Profile,
    64  			ConfigPath:  network.RootDir,
    65  			OutputBlock: network.OutputBlockPath(network.SystemChannel.Name),
    66  		})
    67  		Expect(err).NotTo(HaveOccurred())
    68  		Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0))
    69  
    70  		for _, c := range network.Channels {
    71  			sess, err := network.ConfigTxGen(commands.CreateChannelTx{
    72  				ChannelID:             c.Name,
    73  				Profile:               c.Profile,
    74  				BaseProfile:           c.BaseProfile,
    75  				ConfigPath:            network.RootDir,
    76  				OutputCreateChannelTx: network.CreateChannelTxPath(c.Name),
    77  			})
    78  			Expect(err).NotTo(HaveOccurred())
    79  			Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0))
    80  		}
    81  		network.ConcatenateTLSCACertificates()
    82  
    83  		By("starting all processes for fabric")
    84  		networkRunner := network.NetworkGroupRunner()
    85  		process = ifrit.Invoke(networkRunner)
    86  		Eventually(process.Ready(), network.EventuallyTimeout).Should(BeClosed())
    87  	})
    88  
    89  	AfterEach(func() {
    90  		if process != nil {
    91  			process.Signal(syscall.SIGTERM)
    92  			Eventually(process.Wait(), network.EventuallyTimeout).Should(Receive())
    93  		}
    94  		if network != nil {
    95  			network.Cleanup()
    96  		}
    97  		os.RemoveAll(testDir)
    98  	})
    99  
   100  	It("executes transactions endorsed with ECDSA signing certs", func() {
   101  		org1Peer0 := network.Peer("Org1", "peer0")
   102  		orderer := network.Orderer("orderer")
   103  
   104  		chaincode := nwo.Chaincode{
   105  			Name:            "mycc",
   106  			Version:         "0.0",
   107  			Path:            components.Build("github.com/hechain20/hechain/integration/chaincode/simple/cmd"),
   108  			Lang:            "binary",
   109  			PackageFile:     filepath.Join(testDir, "simplecc.tar.gz"),
   110  			Ctor:            `{"Args":["init","a","100","b","200"]}`,
   111  			SignaturePolicy: `AND ('Org1MSP.member','Org2MSP.member')`,
   112  			Sequence:        "1",
   113  			InitRequired:    true,
   114  			Label:           "my_prebuilt_chaincode",
   115  		}
   116  
   117  		network.CreateAndJoinChannels(orderer)
   118  		nwo.EnableCapabilities(
   119  			network,
   120  			"testchannel",
   121  			"Application", "V2_0",
   122  			orderer,
   123  			network.Peer("Org1", "peer0"),
   124  			network.Peer("Org2", "peer0"),
   125  		)
   126  		nwo.DeployChaincode(network, "testchannel", orderer, chaincode)
   127  		RunQueryInvokeQuery(network, orderer, org1Peer0, 100)
   128  	})
   129  })
   130  
   131  // What follows is a bunch of code to build a hand-crafted set of MSPs where
   132  // everything except signing certificates use RSA keys. It's not pretty but it
   133  // gets the job done for testing.
   134  
   135  func generateRSACACrypto(n *nwo.Network) {
   136  	cryptoDir := n.CryptoPath()
   137  	for _, o := range n.OrdererOrgs() {
   138  		orgDir := filepath.Join(cryptoDir, "ordererOrganizations", o.Domain)
   139  		signCA, tlsCA, adminCert := createMSP(orgDir, o.Domain, o.EnableNodeOUs)
   140  		for i := 1; i <= o.Users; i++ {
   141  			name := fmt.Sprintf("User%d@%s", i, o.Domain)
   142  			dir := filepath.Join(orgDir, "users", name)
   143  			var ous []string
   144  			if o.EnableNodeOUs {
   145  				ous = append(ous, "client")
   146  			}
   147  			writeLocalMSP(dir, name, ous, nil, signCA, tlsCA, adminCert, o.EnableNodeOUs, true)
   148  		}
   149  		for _, orderer := range n.OrderersInOrg(o.Name) {
   150  			name := orderer.Name + "." + o.Domain
   151  			dir := filepath.Join(orgDir, "orderers", name)
   152  			sans := []string{"127.0.0.1", "::1", "localhost"}
   153  			var ous []string
   154  			if o.EnableNodeOUs {
   155  				ous = append(ous, "orderer")
   156  			}
   157  			writeLocalMSP(dir, name, ous, sans, signCA, tlsCA, adminCert, o.EnableNodeOUs, false)
   158  		}
   159  	}
   160  
   161  	for _, o := range n.PeerOrgs() {
   162  		orgDir := filepath.Join(cryptoDir, "peerOrganizations", o.Domain)
   163  		signCA, tlsCA, adminCert := createMSP(orgDir, o.Domain, o.EnableNodeOUs)
   164  		for i := 1; i <= o.Users; i++ {
   165  			name := fmt.Sprintf("User%d@%s", i, o.Domain)
   166  			dir := filepath.Join(orgDir, "users", name)
   167  			var ous []string
   168  			if o.EnableNodeOUs {
   169  				ous = append(ous, "client")
   170  			}
   171  			writeLocalMSP(dir, name, ous, nil, signCA, tlsCA, adminCert, o.EnableNodeOUs, true)
   172  		}
   173  		for _, peer := range n.PeersInOrg(o.Name) {
   174  			name := peer.Name + "." + o.Domain
   175  			dir := filepath.Join(orgDir, "peers", name)
   176  			sans := []string{"127.0.0.1", "::1", "localhost"}
   177  			var ous []string
   178  			if o.EnableNodeOUs {
   179  				ous = append(ous, "peer")
   180  			}
   181  			writeLocalMSP(dir, name, ous, sans, signCA, tlsCA, adminCert, o.EnableNodeOUs, false)
   182  		}
   183  	}
   184  }
   185  
   186  func createMSP(baseDir, domain string, nodeOUs bool) (signCA *CA, tlsCA *CA, adminPemCert []byte) {
   187  	caDir := filepath.Join(baseDir, "ca")
   188  	signCA = newCA(domain, "ca")
   189  	writeCA(signCA, caDir)
   190  
   191  	tlsCADir := filepath.Join(baseDir, "tlsca")
   192  	tlsCA = newCA(domain, "tlsca")
   193  	writeCA(tlsCA, tlsCADir)
   194  
   195  	mspDir := filepath.Join(baseDir, "msp")
   196  	writeVerifyingMSP(mspDir, signCA, tlsCA, nodeOUs)
   197  
   198  	adminUsername := "Admin@" + domain
   199  	adminDir := filepath.Join(baseDir, "users", adminUsername)
   200  	err := os.MkdirAll(adminDir, 0o755)
   201  	Expect(err).NotTo(HaveOccurred())
   202  
   203  	var ous []string
   204  	if nodeOUs {
   205  		ous = append(ous, "admin")
   206  	}
   207  
   208  	writeLocalMSP(adminDir, adminUsername, ous, nil, signCA, tlsCA, nil, nodeOUs, true)
   209  	adminPemCert, err = ioutil.ReadFile(filepath.Join(adminDir, "msp", "signcerts", certFilename(adminUsername)))
   210  	Expect(err).NotTo(HaveOccurred())
   211  	err = ioutil.WriteFile(filepath.Join(adminDir, "msp", "admincerts", certFilename(adminUsername)), adminPemCert, 0o644)
   212  	Expect(err).NotTo(HaveOccurred())
   213  
   214  	if !nodeOUs {
   215  		err := ioutil.WriteFile(filepath.Join(mspDir, "admincerts", certFilename(adminUsername)), adminPemCert, 0o644)
   216  		Expect(err).NotTo(HaveOccurred())
   217  	}
   218  
   219  	return signCA, tlsCA, adminPemCert
   220  }
   221  
   222  func writeCA(ca *CA, dir string) {
   223  	err := os.MkdirAll(dir, 0o755)
   224  	Expect(err).NotTo(HaveOccurred())
   225  
   226  	certFilename := filepath.Join(dir, ca.certFilename())
   227  	writeCertificate(certFilename, ca.certBytes)
   228  
   229  	keyFilename := filepath.Join(dir, fmt.Sprintf("%x_sk", ca.cert.SubjectKeyId))
   230  	writeKey(keyFilename, ca.signer)
   231  }
   232  
   233  func writeCertificate(filename string, der []byte) {
   234  	certFile, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, 0o644)
   235  	Expect(err).NotTo(HaveOccurred())
   236  	defer certFile.Close()
   237  	err = pem.Encode(certFile, &pem.Block{Type: "CERTIFICATE", Bytes: der})
   238  	Expect(err).NotTo(HaveOccurred())
   239  }
   240  
   241  func writeKey(filename string, signer crypto.Signer) {
   242  	keyFile, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, 0o600)
   243  	Expect(err).NotTo(HaveOccurred())
   244  	defer keyFile.Close()
   245  	derKey, err := x509.MarshalPKCS8PrivateKey(signer)
   246  	Expect(err).NotTo(HaveOccurred())
   247  	err = pem.Encode(keyFile, &pem.Block{Type: "PRIVATE KEY", Bytes: derKey})
   248  	Expect(err).NotTo(HaveOccurred())
   249  }
   250  
   251  func writeVerifyingMSP(mspDir string, signCA, tlsCA *CA, nodeOUs bool) {
   252  	for _, dir := range []string{"admincerts", "cacerts", "tlscacerts"} {
   253  		err := os.MkdirAll(filepath.Join(mspDir, dir), 0o755)
   254  		Expect(err).NotTo(HaveOccurred())
   255  	}
   256  	if nodeOUs {
   257  		configFilename := filepath.Join(mspDir, "config.yaml")
   258  		writeConfigYaml(configFilename, filepath.Join("cacerts", signCA.certFilename()), nodeOUs)
   259  	}
   260  	writeCertificate(filepath.Join(mspDir, "cacerts", signCA.certFilename()), signCA.certBytes)
   261  	writeCertificate(filepath.Join(mspDir, "tlscacerts", tlsCA.certFilename()), tlsCA.certBytes)
   262  }
   263  
   264  func writeLocalMSP(baseDir, name string, signOUs, sans []string, signCA, tlsCA *CA, adminCertPem []byte, nodeOUs, client bool) {
   265  	mspDir := filepath.Join(baseDir, "msp")
   266  	err := os.MkdirAll(mspDir, 0o755)
   267  	Expect(err).NotTo(HaveOccurred())
   268  	writeVerifyingMSP(mspDir, signCA, tlsCA, nodeOUs)
   269  
   270  	for _, dir := range []string{"admincerts", "keystore", "signcerts"} {
   271  		err := os.MkdirAll(filepath.Join(mspDir, dir), 0o755)
   272  		Expect(err).NotTo(HaveOccurred())
   273  	}
   274  
   275  	if !nodeOUs && len(adminCertPem) != 0 {
   276  		block, _ := pem.Decode(adminCertPem)
   277  		adminCert, err := x509.ParseCertificate(block.Bytes)
   278  		Expect(err).NotTo(HaveOccurred())
   279  		err = ioutil.WriteFile(filepath.Join(mspDir, "admincerts", certFilename(adminCert.Subject.CommonName)), adminCertPem, 0o644)
   280  		Expect(err).NotTo(HaveOccurred())
   281  	}
   282  
   283  	// create signcert
   284  	priv := generateECKey()
   285  	signcertBytes, signcert := signCA.issueSignCertificate(name, signOUs, priv.Public())
   286  	signcertFilename := filepath.Join(mspDir, "signcerts", certFilename(name))
   287  	writeCertificate(signcertFilename, signcertBytes)
   288  	signcertKeyFilename := filepath.Join(mspDir, "keystore", fmt.Sprintf("%x_sk", signcert.SubjectKeyId))
   289  	writeKey(signcertKeyFilename, priv)
   290  
   291  	// populate tls
   292  	tlsDir := filepath.Join(baseDir, "tls")
   293  	err = os.MkdirAll(tlsDir, 0o755)
   294  	Expect(err).NotTo(HaveOccurred())
   295  	writeCertificate(filepath.Join(tlsDir, "ca.crt"), tlsCA.certBytes)
   296  
   297  	tlsKey := generateRSAKey()
   298  	tlsCertBytes, _ := tlsCA.issueTLSCertificate(name, sans, tlsKey.Public())
   299  	if client {
   300  		writeCertificate(filepath.Join(tlsDir, "client.crt"), tlsCertBytes)
   301  		writeKey(filepath.Join(tlsDir, "client.key"), tlsKey)
   302  	} else {
   303  		writeCertificate(filepath.Join(tlsDir, "server.crt"), tlsCertBytes)
   304  		writeKey(filepath.Join(tlsDir, "server.key"), tlsKey)
   305  	}
   306  }
   307  
   308  func writeConfigYaml(configFilename, caFile string, enable bool) {
   309  	config := &fabricmsp.Configuration{
   310  		NodeOUs: &fabricmsp.NodeOUs{
   311  			Enable: enable,
   312  			ClientOUIdentifier: &fabricmsp.OrganizationalUnitIdentifiersConfiguration{
   313  				Certificate:                  caFile,
   314  				OrganizationalUnitIdentifier: "client",
   315  			},
   316  			PeerOUIdentifier: &fabricmsp.OrganizationalUnitIdentifiersConfiguration{
   317  				Certificate:                  caFile,
   318  				OrganizationalUnitIdentifier: "peer",
   319  			},
   320  			AdminOUIdentifier: &fabricmsp.OrganizationalUnitIdentifiersConfiguration{
   321  				Certificate:                  caFile,
   322  				OrganizationalUnitIdentifier: "admin",
   323  			},
   324  			OrdererOUIdentifier: &fabricmsp.OrganizationalUnitIdentifiersConfiguration{
   325  				Certificate:                  caFile,
   326  				OrganizationalUnitIdentifier: "orderer",
   327  			},
   328  		},
   329  	}
   330  
   331  	configFile, err := os.Create(configFilename)
   332  	Expect(err).NotTo(HaveOccurred())
   333  	defer configFile.Close()
   334  
   335  	err = yaml.NewEncoder(configFile).Encode(config)
   336  	Expect(err).NotTo(HaveOccurred())
   337  }
   338  
   339  type CA struct {
   340  	signer    crypto.Signer
   341  	cert      *x509.Certificate
   342  	certBytes []byte
   343  }
   344  
   345  func newCA(orgName, caName string) *CA {
   346  	signer := generateRSAKey()
   347  
   348  	template := x509Template()
   349  	template.IsCA = true
   350  	template.KeyUsage |= x509.KeyUsageDigitalSignature
   351  	template.KeyUsage |= x509.KeyUsageKeyEncipherment
   352  	template.KeyUsage |= x509.KeyUsageCertSign
   353  	template.KeyUsage |= x509.KeyUsageCRLSign
   354  	template.ExtKeyUsage = []x509.ExtKeyUsage{
   355  		x509.ExtKeyUsageClientAuth,
   356  		x509.ExtKeyUsageServerAuth,
   357  	}
   358  	template.Subject = pkix.Name{
   359  		CommonName:   caName + "." + orgName,
   360  		Organization: []string{orgName},
   361  	}
   362  	template.SubjectKeyId = computeSKI(signer.Public())
   363  
   364  	certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, signer.Public(), signer)
   365  	Expect(err).NotTo(HaveOccurred())
   366  	cert, err := x509.ParseCertificate(certBytes)
   367  	Expect(err).NotTo(HaveOccurred())
   368  
   369  	return &CA{
   370  		signer:    signer,
   371  		cert:      cert,
   372  		certBytes: certBytes,
   373  	}
   374  }
   375  
   376  func (ca *CA) issueSignCertificate(name string, ous []string, pub crypto.PublicKey) ([]byte, *x509.Certificate) {
   377  	template := x509Template()
   378  	template.KeyUsage = x509.KeyUsageDigitalSignature
   379  	template.ExtKeyUsage = nil
   380  	template.Subject = pkix.Name{
   381  		CommonName:         name,
   382  		Organization:       ca.cert.Subject.Organization,
   383  		OrganizationalUnit: ous,
   384  	}
   385  	template.SubjectKeyId = computeSKI(pub)
   386  
   387  	certBytes, err := x509.CreateCertificate(rand.Reader, &template, ca.cert, pub, ca.signer)
   388  	Expect(err).NotTo(HaveOccurred())
   389  	cert, err := x509.ParseCertificate(certBytes)
   390  	Expect(err).NotTo(HaveOccurred())
   391  	return certBytes, cert
   392  }
   393  
   394  func (ca *CA) issueTLSCertificate(name string, sans []string, pub crypto.PublicKey) ([]byte, *x509.Certificate) {
   395  	template := x509Template()
   396  	template.KeyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment
   397  	template.ExtKeyUsage = []x509.ExtKeyUsage{
   398  		x509.ExtKeyUsageServerAuth,
   399  		x509.ExtKeyUsageClientAuth,
   400  	}
   401  	template.Subject = pkix.Name{
   402  		CommonName:   name,
   403  		Organization: ca.cert.Subject.Organization,
   404  	}
   405  	template.SubjectKeyId = computeSKI(pub)
   406  
   407  	for _, san := range sans {
   408  		if ip := net.ParseIP(san); ip != nil {
   409  			template.IPAddresses = append(template.IPAddresses, ip)
   410  		} else {
   411  			template.DNSNames = append(template.DNSNames, san)
   412  		}
   413  	}
   414  	certBytes, err := x509.CreateCertificate(rand.Reader, &template, ca.cert, pub, ca.signer)
   415  	Expect(err).NotTo(HaveOccurred())
   416  	cert, err := x509.ParseCertificate(certBytes)
   417  	Expect(err).NotTo(HaveOccurred())
   418  	return certBytes, cert
   419  }
   420  
   421  func (ca *CA) certFilename() string {
   422  	return certFilename(ca.cert.Subject.CommonName)
   423  }
   424  
   425  func certFilename(stem string) string {
   426  	return stem + "-cert.pem"
   427  }
   428  
   429  func generateRSAKey() crypto.Signer {
   430  	signer, err := rsa.GenerateKey(rand.Reader, 4096)
   431  	Expect(err).NotTo(HaveOccurred())
   432  	return signer
   433  }
   434  
   435  func generateECKey() crypto.Signer {
   436  	signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   437  	Expect(err).NotTo(HaveOccurred())
   438  	return signer
   439  }
   440  
   441  func x509Template() x509.Certificate {
   442  	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
   443  	serialNumber, _ := rand.Int(rand.Reader, serialNumberLimit)
   444  	notBefore := time.Now().Round(time.Minute).Add(-5 * time.Minute).UTC()
   445  
   446  	return x509.Certificate{
   447  		SerialNumber:          serialNumber,
   448  		NotBefore:             notBefore,
   449  		NotAfter:              notBefore.Add(3650 * 24 * time.Hour).UTC(),
   450  		BasicConstraintsValid: true,
   451  	}
   452  }
   453  
   454  func computeSKI(key crypto.PublicKey) []byte {
   455  	var raw []byte
   456  	switch key := key.(type) {
   457  	case *rsa.PublicKey:
   458  		raw = x509.MarshalPKCS1PublicKey(key)
   459  	case *ecdsa.PublicKey:
   460  		raw = elliptic.Marshal(key.Curve, key.X, key.Y)
   461  	default:
   462  		panic(fmt.Sprintf("unexpected type: %T", key))
   463  	}
   464  	hash := sha256.Sum256(raw)
   465  	return hash[:]
   466  }