github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/integration/msp/rsaca_test.go (about)

     1  /*
     2  Copyright IBM Corp. 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/osdi23p228/fabric/integration/nwo"
    30  	"github.com/osdi23p228/fabric/integration/nwo/commands"
    31  	fabricmsp "github.com/osdi23p228/fabric/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/osdi23p228/fabric/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, 0755)
   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, 0644)
   212  	Expect(err).NotTo(HaveOccurred())
   213  
   214  	if !nodeOUs {
   215  		err := ioutil.WriteFile(filepath.Join(mspDir, "admincerts", certFilename(adminUsername)), adminPemCert, 0644)
   216  		Expect(err).NotTo(HaveOccurred())
   217  	}
   218  
   219  	return signCA, tlsCA, adminPemCert
   220  }
   221  
   222  func writeCA(ca *CA, dir string) {
   223  	var err error
   224  	err = os.MkdirAll(dir, 0755)
   225  	Expect(err).NotTo(HaveOccurred())
   226  
   227  	certFilename := filepath.Join(dir, ca.certFilename())
   228  	writeCertificate(certFilename, ca.certBytes)
   229  
   230  	keyFilename := filepath.Join(dir, fmt.Sprintf("%x_sk", ca.cert.SubjectKeyId))
   231  	writeKey(keyFilename, ca.signer)
   232  }
   233  
   234  func writeCertificate(filename string, der []byte) {
   235  	certFile, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, 0644)
   236  	Expect(err).NotTo(HaveOccurred())
   237  	defer certFile.Close()
   238  	err = pem.Encode(certFile, &pem.Block{Type: "CERTIFICATE", Bytes: der})
   239  	Expect(err).NotTo(HaveOccurred())
   240  }
   241  
   242  func writeKey(filename string, signer crypto.Signer) {
   243  	keyFile, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, 0600)
   244  	Expect(err).NotTo(HaveOccurred())
   245  	defer keyFile.Close()
   246  	derKey, err := x509.MarshalPKCS8PrivateKey(signer)
   247  	Expect(err).NotTo(HaveOccurred())
   248  	err = pem.Encode(keyFile, &pem.Block{Type: "PRIVATE KEY", Bytes: derKey})
   249  	Expect(err).NotTo(HaveOccurred())
   250  }
   251  
   252  func writeVerifyingMSP(mspDir string, signCA, tlsCA *CA, nodeOUs bool) {
   253  	for _, dir := range []string{"admincerts", "cacerts", "tlscacerts"} {
   254  		err := os.MkdirAll(filepath.Join(mspDir, dir), 0755)
   255  		Expect(err).NotTo(HaveOccurred())
   256  	}
   257  	if nodeOUs {
   258  		configFilename := filepath.Join(mspDir, "config.yaml")
   259  		writeConfigYaml(configFilename, filepath.Join("cacerts", signCA.certFilename()), nodeOUs)
   260  	}
   261  	writeCertificate(filepath.Join(mspDir, "cacerts", signCA.certFilename()), signCA.certBytes)
   262  	writeCertificate(filepath.Join(mspDir, "tlscacerts", tlsCA.certFilename()), tlsCA.certBytes)
   263  }
   264  
   265  func writeLocalMSP(baseDir, name string, signOUs, sans []string, signCA, tlsCA *CA, adminCertPem []byte, nodeOUs, client bool) {
   266  	mspDir := filepath.Join(baseDir, "msp")
   267  	err := os.MkdirAll(mspDir, 0755)
   268  	Expect(err).NotTo(HaveOccurred())
   269  	writeVerifyingMSP(mspDir, signCA, tlsCA, nodeOUs)
   270  
   271  	for _, dir := range []string{"admincerts", "keystore", "signcerts"} {
   272  		err := os.MkdirAll(filepath.Join(mspDir, dir), 0755)
   273  		Expect(err).NotTo(HaveOccurred())
   274  	}
   275  
   276  	if !nodeOUs && len(adminCertPem) != 0 {
   277  		block, _ := pem.Decode(adminCertPem)
   278  		adminCert, err := x509.ParseCertificate(block.Bytes)
   279  		Expect(err).NotTo(HaveOccurred())
   280  		err = ioutil.WriteFile(filepath.Join(mspDir, "admincerts", certFilename(adminCert.Subject.CommonName)), adminCertPem, 0644)
   281  		Expect(err).NotTo(HaveOccurred())
   282  	}
   283  
   284  	// create signcert
   285  	priv := generateECKey()
   286  	signcertBytes, signcert := signCA.issueSignCertificate(name, signOUs, priv.Public())
   287  	signcertFilename := filepath.Join(mspDir, "signcerts", certFilename(name))
   288  	writeCertificate(signcertFilename, signcertBytes)
   289  	signcertKeyFilename := filepath.Join(mspDir, "keystore", fmt.Sprintf("%x_sk", signcert.SubjectKeyId))
   290  	writeKey(signcertKeyFilename, priv)
   291  
   292  	// populate tls
   293  	tlsDir := filepath.Join(baseDir, "tls")
   294  	err = os.MkdirAll(tlsDir, 0755)
   295  	Expect(err).NotTo(HaveOccurred())
   296  	writeCertificate(filepath.Join(tlsDir, "ca.crt"), tlsCA.certBytes)
   297  
   298  	tlsKey := generateRSAKey()
   299  	tlsCertBytes, _ := tlsCA.issueTLSCertificate(name, sans, tlsKey.Public())
   300  	if client {
   301  		writeCertificate(filepath.Join(tlsDir, "client.crt"), tlsCertBytes)
   302  		writeKey(filepath.Join(tlsDir, "client.key"), tlsKey)
   303  	} else {
   304  		writeCertificate(filepath.Join(tlsDir, "server.crt"), tlsCertBytes)
   305  		writeKey(filepath.Join(tlsDir, "server.key"), tlsKey)
   306  	}
   307  }
   308  
   309  func writeConfigYaml(configFilename, caFile string, enable bool) {
   310  	var config = &fabricmsp.Configuration{
   311  		NodeOUs: &fabricmsp.NodeOUs{
   312  			Enable: enable,
   313  			ClientOUIdentifier: &fabricmsp.OrganizationalUnitIdentifiersConfiguration{
   314  				Certificate:                  caFile,
   315  				OrganizationalUnitIdentifier: "client",
   316  			},
   317  			PeerOUIdentifier: &fabricmsp.OrganizationalUnitIdentifiersConfiguration{
   318  				Certificate:                  caFile,
   319  				OrganizationalUnitIdentifier: "peer",
   320  			},
   321  			AdminOUIdentifier: &fabricmsp.OrganizationalUnitIdentifiersConfiguration{
   322  				Certificate:                  caFile,
   323  				OrganizationalUnitIdentifier: "admin",
   324  			},
   325  			OrdererOUIdentifier: &fabricmsp.OrganizationalUnitIdentifiersConfiguration{
   326  				Certificate:                  caFile,
   327  				OrganizationalUnitIdentifier: "orderer",
   328  			},
   329  		},
   330  	}
   331  
   332  	configFile, err := os.Create(configFilename)
   333  	Expect(err).NotTo(HaveOccurred())
   334  	defer configFile.Close()
   335  
   336  	err = yaml.NewEncoder(configFile).Encode(config)
   337  	Expect(err).NotTo(HaveOccurred())
   338  }
   339  
   340  type CA struct {
   341  	signer    crypto.Signer
   342  	cert      *x509.Certificate
   343  	certBytes []byte
   344  }
   345  
   346  func newCA(orgName, caName string) *CA {
   347  	signer := generateRSAKey()
   348  
   349  	template := x509Template()
   350  	template.IsCA = true
   351  	template.KeyUsage |= x509.KeyUsageDigitalSignature
   352  	template.KeyUsage |= x509.KeyUsageKeyEncipherment
   353  	template.KeyUsage |= x509.KeyUsageCertSign
   354  	template.KeyUsage |= x509.KeyUsageCRLSign
   355  	template.ExtKeyUsage = []x509.ExtKeyUsage{
   356  		x509.ExtKeyUsageClientAuth,
   357  		x509.ExtKeyUsageServerAuth,
   358  	}
   359  	template.Subject = pkix.Name{
   360  		CommonName:   caName + "." + orgName,
   361  		Organization: []string{orgName},
   362  	}
   363  	template.SubjectKeyId = computeSKI(signer.Public())
   364  
   365  	certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, signer.Public(), signer)
   366  	Expect(err).NotTo(HaveOccurred())
   367  	cert, err := x509.ParseCertificate(certBytes)
   368  	Expect(err).NotTo(HaveOccurred())
   369  
   370  	return &CA{
   371  		signer:    signer,
   372  		cert:      cert,
   373  		certBytes: certBytes,
   374  	}
   375  }
   376  
   377  func (ca *CA) issueSignCertificate(name string, ous []string, pub crypto.PublicKey) ([]byte, *x509.Certificate) {
   378  	template := x509Template()
   379  	template.KeyUsage = x509.KeyUsageDigitalSignature
   380  	template.ExtKeyUsage = nil
   381  	template.Subject = pkix.Name{
   382  		CommonName:         name,
   383  		Organization:       ca.cert.Subject.Organization,
   384  		OrganizationalUnit: ous,
   385  	}
   386  	template.SubjectKeyId = computeSKI(pub)
   387  
   388  	certBytes, err := x509.CreateCertificate(rand.Reader, &template, ca.cert, pub, ca.signer)
   389  	Expect(err).NotTo(HaveOccurred())
   390  	cert, err := x509.ParseCertificate(certBytes)
   391  	Expect(err).NotTo(HaveOccurred())
   392  	return certBytes, cert
   393  }
   394  
   395  func (ca *CA) issueTLSCertificate(name string, sans []string, pub crypto.PublicKey) ([]byte, *x509.Certificate) {
   396  	template := x509Template()
   397  	template.KeyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment
   398  	template.ExtKeyUsage = []x509.ExtKeyUsage{
   399  		x509.ExtKeyUsageServerAuth,
   400  		x509.ExtKeyUsageClientAuth,
   401  	}
   402  	template.Subject = pkix.Name{
   403  		CommonName:   name,
   404  		Organization: ca.cert.Subject.Organization,
   405  	}
   406  	template.SubjectKeyId = computeSKI(pub)
   407  
   408  	for _, san := range sans {
   409  		if ip := net.ParseIP(san); ip != nil {
   410  			template.IPAddresses = append(template.IPAddresses, ip)
   411  		} else {
   412  			template.DNSNames = append(template.DNSNames, san)
   413  		}
   414  	}
   415  	certBytes, err := x509.CreateCertificate(rand.Reader, &template, ca.cert, pub, ca.signer)
   416  	Expect(err).NotTo(HaveOccurred())
   417  	cert, err := x509.ParseCertificate(certBytes)
   418  	Expect(err).NotTo(HaveOccurred())
   419  	return certBytes, cert
   420  }
   421  
   422  func (ca *CA) certFilename() string {
   423  	return certFilename(ca.cert.Subject.CommonName)
   424  }
   425  
   426  func certFilename(stem string) string {
   427  	return stem + "-cert.pem"
   428  }
   429  
   430  func generateRSAKey() crypto.Signer {
   431  	signer, err := rsa.GenerateKey(rand.Reader, 4096)
   432  	Expect(err).NotTo(HaveOccurred())
   433  	return signer
   434  }
   435  
   436  func generateECKey() crypto.Signer {
   437  	signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   438  	Expect(err).NotTo(HaveOccurred())
   439  	return signer
   440  }
   441  
   442  func x509Template() x509.Certificate {
   443  	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
   444  	serialNumber, _ := rand.Int(rand.Reader, serialNumberLimit)
   445  	notBefore := time.Now().Round(time.Minute).Add(-5 * time.Minute).UTC()
   446  
   447  	return x509.Certificate{
   448  		SerialNumber:          serialNumber,
   449  		NotBefore:             notBefore,
   450  		NotAfter:              notBefore.Add(3650 * 24 * time.Hour).UTC(),
   451  		BasicConstraintsValid: true,
   452  	}
   453  }
   454  
   455  func computeSKI(key crypto.PublicKey) []byte {
   456  	var raw []byte
   457  	switch key := key.(type) {
   458  	case *rsa.PublicKey:
   459  		raw = x509.MarshalPKCS1PublicKey(key)
   460  	case *ecdsa.PublicKey:
   461  		raw = elliptic.Marshal(key.Curve, key.X, key.Y)
   462  	default:
   463  		panic(fmt.Sprintf("unexpected type: %T", key))
   464  	}
   465  	hash := sha256.Sum256(raw)
   466  	return hash[:]
   467  }