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 }