Hyperledger Fabric:使用 Fabric Gateway Java SDK 的 TLS 内部错误

Hyperledger Fabric: TLS Internal Error using Fabric Gateway Java SDK

面料版本:v2.2.4 结构网关版本:v2.2.0

目前,我没有使用 Fabric CA 和 docker。相反,我使用 OpenSSL 生成我自己的证书。使用命令行加入频道或在启用 TLS(服务器 TLS)的情况下调用链代码没有问题。

但是,当我尝试使用带有 TLS 的 Fabric Gateway Java SDK 调用我的链代码时,对等方显示:

2021-10-20 23:53:51.571 EDT [core.comm] ServerHandshake -> ERRO 411 Server TLS handshake failed in 731.664253ms with error remote error: tls: internal error server=PeerServer remoteaddress=127.0.0.1:48920
2021-10-20 23:53:51.648 EDT [core.comm] ServerHandshake -> ERRO 412 Server TLS handshake failed in 17.623962ms with error remote error: tls: internal error server=PeerServer remoteaddress=127.0.0.1:48924
2021-10-20 23:53:52.389 EDT [core.comm] ServerHandshake -> ERRO 413 Server TLS handshake failed in 31.834126ms with error remote error: tls: internal error server=PeerServer remoteaddress=127.0.0.1:48928
2021-10-20 23:53:53.013 EDT [core.comm] ServerHandshake -> ERRO 414 Server TLS handshake failed in 20.97883ms with error remote error: tls: internal error server=PeerServer remoteaddress=127.0.0.1:48932
2021-10-20 23:53:53.453 EDT [core.comm] ServerHandshake -> ERRO 415 Server TLS handshake failed in 27.840631ms with error remote error: tls: internal error server=PeerServer remoteaddress=127.0.0.1:48936

如果我禁用 TLS,可以调用和查询链代码,但它会显示一条错误消息并显示对等方的证书:

04:45:56.020 [main] ERROR org.hyperledger.fabric.sdk.security.CryptoPrimitives - Cannot 
validate certificate. Error is: Path does not chain with any of the trust anchors
Certificate[
[
  Version: V3
  Subject: CN=peer-telecom, OU=peer, O=peer0.telecom.com, ST=Wilayah Persekutuan Kuala Lumpur, C=MY
  Signature Algorithm: SHA256withECDSA, OID = 1.2.840.10045.4.3.2

  Key:  Sun EC public key, 256 bits
  public x coord: 63258922835963897769642318382353579773301130246303245867091942631050654760639
  public y coord: 17014436126955018341557373411142402131278326611630973049174140241981148702177
  parameters: secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)
  Validity: [From: Mon Oct 18 04:30:10 EDT 2021,
               To: Tue Oct 18 04:30:10 EDT 2022]
  Issuer: CN=hyperledger-telecom, O=ica.hyperledger.telecom.com, ST=Wilayah Persekutuan Kuala Lumpur, C=MY
  SerialNumber: [    1000]

Certificate Extensions: 4
[1]: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: 70 2B 57 B5 7C 6A 38 DE   AB 6F DD 7E C4 63 FE 39  p+W..j8..o...c.9
0010: 54 22 D9 F9                                        T"..
]
]

[2]: ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
  CA:false
  PathLen: undefined
]

[3]: ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
  DigitalSignature
]

[4]: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 62 C2 4A FB 13 54 44 DF   15 AB 2B 09 78 4F 79 4A  b.J..TD...+.xOyJ
0010: CB 37 1D D7                                        .7..
]
]

]
  Algorithm: [SHA256withECDSA]
  Signature:
0000: 30 44 02 20 2D 20 15 9D   53 D4 DE EF 56 0D E6 6D  0D. - ..S...V..m
0010: 04 DA 99 F8 0C AC D5 A7   87 66 51 04 23 A0 4D C2  .........fQ.#.M.
0020: C8 98 95 5C 02 20 5C A5   5A 2D 19 43 FA E8 C0 E1  ...\. \.Z-.C....
0030: 49 4E C0 DF C9 59 F8 10   34 D6 94 05 51 38 E9 17  IN...Y..4...Q8..
0040: C5 F1 20 1F 0C EC                                  .. ...

]

Java申请代码:

package org.example.contract;

import java.io.IOException;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.Files;

import java.util.concurrent.TimeoutException;
import java.security.InvalidKeyException;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;


import org.hyperledger.fabric.gateway.Identities;
import org.hyperledger.fabric.gateway.Identity;
import org.hyperledger.fabric.gateway.Contract;
import org.hyperledger.fabric.gateway.ContractException;
import org.hyperledger.fabric.gateway.Gateway;
import org.hyperledger.fabric.gateway.Network;
import org.hyperledger.fabric.gateway.Wallet;
import org.hyperledger.fabric.gateway.Wallets;

public class Sample {
    
    X509Certificate[] clientCert = null;
    PrivateKey clientKey = null;

    private static X509Certificate readX509Certificate(final Path certificatePath) throws IOException, CertificateException {
        try (Reader certificateReader = Files.newBufferedReader(certificatePath, StandardCharsets.UTF_8)) {
            return Identities.readX509Certificate(certificateReader);
        }
    }

    private static PrivateKey getPrivateKey(final Path privateKeyPath) throws IOException, InvalidKeyException {
        try (Reader privateKeyReader = Files.newBufferedReader(privateKeyPath, StandardCharsets.UTF_8)) {
            return Identities.readPrivateKey(privateKeyReader);
        }
    }

    public static void main(String[] args) throws IOException {
        // Load an existing wallet holding identities used to access the network.
        // A wallet stores a collection of identities
        Path walletPath = Paths.get(".", "wallet");
        Wallet wallet = Wallets.newFileSystemWallet(walletPath);
        try {
                  
            Path credentialPath = Paths.get("/root","fabric","localtls","crypto-config", "peerOrganizations",
                "org1.telecom.com", "users", "Admin@org1.telecom.com", "msp");
            System.out.println("credentialPath: " + credentialPath.toString());
            Path certificatePath = credentialPath.resolve(Paths.get("signcerts",
                "Admin@org1.telecom.com-cert.pem"));
            System.out.println("certificatePem: " + certificatePath.toString());
            Path privateKeyPath = credentialPath.resolve(Paths.get("keystore",
                "Admin@telecom.com.key"));
      
            X509Certificate certificate = readX509Certificate(certificatePath);
            PrivateKey privateKey = getPrivateKey(privateKeyPath);
      
            Identity identity = Identities.newX509Identity("Org1MSP", certificate, privateKey);
            
            String identityLabel = "Admin@org1.telecom.com";
            wallet.put(identityLabel, identity);
      
            System.out.println("Write wallet info into " + walletPath.toString() + " successfully.");
      
        } catch (IOException | CertificateException | InvalidKeyException e) {
            System.err.println("Error adding to wallet");
            e.printStackTrace();
        }
        // Path to a common connection profile describing the network.
        Path networkConfigFile = Paths.get("/root","fabric","localtls","connection.yaml");

        String userName = "Admin@org1.telecom.com";
        // Configure the gateway connection used to access the network.,
        Gateway.Builder builder = Gateway.createBuilder()
                .identity(wallet, userName)
                .networkConfig(networkConfigFile);

        // Create a gateway connection
        try (Gateway gateway = builder.connect()) {

            // Obtain a smart contract deployed on the network.
            Network network = gateway.getNetwork("mychannel");
            Contract contract = network.getContract("chaincode");

            // Submit transactions that store state to the ledger.
            byte[] createCarResult = contract.createTransaction("createMyAsset")
                    .submit("CAR", "Honda");
            System.out.println(new String(createCarResult, StandardCharsets.UTF_8));

            byte[] queryCar = contract.submitTransaction("readMyAsset", "CAR");
            System.out.println(new String(queryCar, StandardCharsets.UTF_8));

        } catch (ContractException | TimeoutException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

connection.yaml:

name: "Network-Config-Test"
description: "The network used in the integration tests"
version: 1.0.0
client:
  organization: telecom

organizations:
  telecom:
    mspid: Org1MSP

    peers:
      - peer0.org1.telecom.com

    #adminPrivateKey:
      #path: '/root/fabric/localtls/crypto-config/peerOrganizations/org1.telecom.com/users/Admin@org1.telecom.com/msp/keystore/Admin@telecom.com.key'

    #signedCert:
      #path: '/root/fabric/localtls/crypto-config/peerOrganizations/org1.telecom.com/users/Admin@org1.telecom.com/msp/signcerts/Admin@org1.telecom.com-cert.pem'
      
orderers:
  orderer.example.com:
    url: grpcs://localhost:6050

   
      #client: 
        #keyfile: '/root/fabric/localtls/crypto-config/peerOrganizations/org1.telecom.com/users/Admin@org1.telecom.com/tls/client-o.key'
        #certfile: '/root/fabric/localtls/crypto-config/peerOrganizations/org1.telecom.com/users/Admin@org1.telecom.com/tls/client-o.crt'

    grpcOptions:
      hostnameOverride: orderer.example.com
      grpc-max-send-message-length: 15
      grpc.keepalive_time_ms: 360000
      grpc.keepalive_timeout_ms: 180000
      negotiationType: TLS
      sslProvider: openSSL

    tlsCACerts:
      path: /root/fabric/localtls/crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/ca.crt


peers:
  peer0.org1.telecom.com:
    url: grpcs://localhost:7051
      #client: 
        #keyfile: '/root/fabric/localtls/crypto-config/peerOrganizations/org1.telecom.com/users/Admin@org1.telecom.com/tls/client.key'
        #certfile: '/root/fabric/localtls/crypto-config/peerOrganizations/org1.telecom.com/users/Admin@org1.telecom.com/tls/client.crt'

    grpcOptions:
      ssl-target-name-override: peer0.org1.telecom.com
      grpc.http2.keepalive_time: 15
      hostnameOverride: peer0.org1.telecom.com
      negotiationType: TLS
      sslProvider: openSSL
    
    tlsCACerts:
      path: /root/fabric/localtls/crypto-config/peerOrganizations/org1.telecom.com/peers/peer0.org1.telecom.com/tls/ca.crt

我错过了什么?欢迎任何答案!

更新 1.0 这些是使用 openSSL 生成的证书,当我使用命令行时它们可以工作(我在主题备用名称中省略了 hf 东西,不确定它对应用程序工作是否重要?):

tlscacert:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            1f:a5:9f:1a:01:ed:c7:3d:30:ca:27:b6:79:9c:82:10:b8:94:84:23
        Signature Algorithm: ecdsa-with-SHA256
        Issuer: C = MY, ST = Wilayah Persekutuan Kuala Lumpur, L = Kuala Lumpur, O = rca.verisign.com, CN = tls-verisign
        Validity
            Not Before: Oct 21 03:11:13 2021 GMT
            Not After : Oct 19 03:11:13 2031 GMT
        Subject: C = MY, ST = Wilayah Persekutuan Kuala Lumpur, L = Kuala Lumpur, O = rca.verisign.com, CN = tls-verisign
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:34:b5:ac:a3:42:8c:d4:17:97:ca:16:d5:2f:7a:
                    00:32:bf:fd:dd:02:8a:33:28:ed:c0:53:5d:e0:42:
                    79:0d:08:43:1c:22:83:1e:f0:71:91:08:d6:c3:ec:
                    eb:ac:9c:56:00:da:e8:08:cf:ad:4c:b3:46:e7:e1:
                    39:1c:5f:bf:fc
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                EB:7B:8E:23:26:A7:17:3D:E0:0B:8D:B4:6E:6C:5D:D5:EE:EF:80:AF
            X509v3 Authority Key Identifier:
                keyid:EB:7B:8E:23:26:A7:17:3D:E0:0B:8D:B4:6E:6C:5D:D5:EE:EF:80:AF

            X509v3 Basic Constraints: critical
                CA:TRUE
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment, Certificate Sign, CRL Sign
    Signature Algorithm: ecdsa-with-SHA256
         30:45:02:20:63:40:88:2d:c8:51:e5:42:2b:d3:98:60:6f:1e:
         3c:d2:f6:59:48:bb:0c:7c:10:b6:28:27:86:20:58:35:0b:19:
         02:21:00:c7:87:df:79:75:21:8d:bf:bc:be:aa:c9:44:53:b5:
         b4:32:4d:06:2b:a0:05:eb:38:b7:9f:3d:71:80:17:77:69

对等 TLS 证书:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 4097 (0x1001)
        Signature Algorithm: ecdsa-with-SHA256
        Issuer: C = MY, ST = Wilayah Persekutuan Kuala Lumpur, L = Kuala Lumpur, O = rca.verisign.com, CN = tls-verisign
        Validity
            Not Before: Oct 21 03:11:13 2021 GMT
            Not After : Oct 21 03:11:13 2022 GMT
        Subject: C = MY, ST = Wilayah Persekutuan Kuala Lumpur, O = tls.peer.telecom.com, CN = tls-peer-telecom
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:76:c2:e9:94:10:03:2c:3b:2d:90:56:e8:b5:30:
                    bf:67:f6:b5:b8:9c:73:cd:28:b3:f2:f7:21:e9:f7:
                    6c:66:38:a9:e8:7c:b0:ea:67:fb:f7:db:72:29:9d:
                    aa:56:20:92:ab:b1:e5:53:2a:a1:19:0b:0c:8b:65:
                    36:fe:98:aa:e1
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        X509v3 extensions:
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage:
                TLS Web Client Authentication, TLS Web Server Authentication
            X509v3 Subject Key Identifier:
                08:9D:B8:81:4B:39:0D:B1:D4:BD:EC:49:E5:AC:BA:FB:F1:7A:58:51
            X509v3 Authority Key Identifier:
                keyid:EB:7B:8E:23:26:A7:17:3D:E0:0B:8D:B4:6E:6C:5D:D5:EE:EF:80:AF

            X509v3 Subject Alternative Name:
                IP Address:127.0.0.1
    Signature Algorithm: ecdsa-with-SHA256
         30:46:02:21:00:c0:d7:26:b4:e9:30:08:b5:37:46:66:0f:fb:
         b6:36:22:04:5f:78:ab:ae:ff:7f:48:e0:7b:ff:e8:f0:88:b4:
         e5:02:21:00:86:64:ec:c8:9b:b4:06:b5:3d:d3:c1:54:4f:d2:
         a2:bb:1a:e1:a7:e7:84:28:9a:ef:ac:db:ab:65:95:0d:10:2d

我在证书中唯一缺少的是:

1.2.3.4.5.6.7.8.1:
                {"attrs":{"hf.Affiliation":"","hf.EnrollmentID":"peer0","hf.Type":"peer"}}

这是否仅由 Fabric CA 使用?还是必须包含?

Fabric TLS 证书非常繁琐。例如,我们发现了几种情况,其中对等点和链代码将接受格式错误的证书,但网关客户端拒绝使用相同的证书进行连接。可能发生的情况是你的 openssl 生成的证书有问题,但是 peer 太松了,无法报告错误。 (客户端 TLS 库只是断开连接,在握手期间不报告根失败也无济于事。)

在上面的示例中,证书输出显示 PathLenKeyUsage 属性与 Fabric CA 通常生成的属性不同。

我们发现以下是一些想法和技术,可用于调试网关/客户端 SDK 的 TLS 握手问题。尝试使用这些技术来缩小您的证书与 Fabric CA 生成的参考证书之间的任何差距:

  • 使用 Fabric CA。即使您计划使用 OpenSSL 和外部机构生成证书链,您也可以使用 Fabric CA 生成 TLS 注册和证书,并且 比较 参考证书与您已经建立的内容使用 OpenSSL。

  • 使用 curl 或其他支持 TLS 的客户端来帮助验证证书的正确性。在很多情况下,独立客户端输出的错误直接适用于连接 fabric 客户端时失败的 TLS 握手。

  • 使用 test-network-k8s 系统作为设置 TLS 和 CA 基础设施的参考。除了“按钮”测试网络外,这还将提供可用于生成参考注册/学习证书的 CA 端点。

  • 检查 Fabric CA 生成的证书,并与您手工制作的证书进行比较。例如,这是使用 Kube 测试网络生成的 TLS 证书的证书转储 - 确保您的 OpenSSL 证书具有相同或相似的功能集和属性。

$ openssl x509 -in /tmp/ca-cert.pem -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            4c:63:74:d5:99:29:ce:e0:b6:28:a2:b5:a4:0e:a0:c1:f3:e9:a9:d5
    Signature Algorithm: ecdsa-with-SHA256
        Issuer: C=US, ST=North Carolina, O=Hyperledger, OU=Fabric, CN=fabric-ca-server
        Validity
            Not Before: Oct 21 10:36:00 2021 GMT
            Not After : Oct 17 10:36:00 2036 GMT
        Subject: C=US, ST=North Carolina, O=Hyperledger, OU=Fabric, CN=fabric-ca-server
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub: 
                    04:53:f4:ad:e6:6b:4c:75:7e:4a:6d:6e:cd:73:b0:
                    81:a8:d7:d7:55:c0:fd:22:92:15:fc:2d:20:44:c6:
                    ec:55:c9:cc:88:3a:14:09:77:e5:4f:4b:b8:98:ee:
                    71:09:da:e6:f8:7c:f7:39:fa:41:fc:f3:a2:fe:a4:
                    1e:34:ec:a9:b5
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        X509v3 extensions:
            X509v3 Key Usage: critical
                Certificate Sign, CRL Sign
            X509v3 Basic Constraints: critical
                CA:TRUE, pathlen:1
            X509v3 Subject Key Identifier: 
                AB:AF:85:A3:D3:2E:9A:A9:03:49:F5:5C:30:32:2B:92:EC:92:B3:D0
            X509v3 Subject Alternative Name: 
                IP Address:127.0.0.1
    Signature Algorithm: ecdsa-with-SHA256
         30:45:02:21:00:bf:cc:c1:d2:29:b1:04:3f:55:31:c6:b7:69:
         ca:72:12:d7:67:55:14:cd:23:f7:75:16:6c:b1:63:7f:e6:9c:
         24:02:20:5d:ff:e3:7e:84:22:d3:f3:52:bd:96:fa:dc:2d:94:
         2f:6b:a3:bc:ab:3e:b3:87:10:fd:30:51:a2:4a:ca:ce:b4
-----BEGIN CERTIFICATE-----
MIICKDCCAc6gAwIBAgIUTGN01ZkpzuC2KKK1pA6gwfPpqdUwCgYIKoZIzj0EAwIw
aDELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMRQwEgYDVQQK
EwtIeXBlcmxlZGdlcjEPMA0GA1UECxMGRmFicmljMRkwFwYDVQQDExBmYWJyaWMt
Y2Etc2VydmVyMB4XDTIxMTAyMTEwMzYwMFoXDTM2MTAxNzEwMzYwMFowaDELMAkG
A1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMRQwEgYDVQQKEwtIeXBl
cmxlZGdlcjEPMA0GA1UECxMGRmFicmljMRkwFwYDVQQDExBmYWJyaWMtY2Etc2Vy
dmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEU/St5mtMdX5KbW7Nc7CBqNfX
VcD9IpIV/C0gRMbsVcnMiDoUCXflT0u4mO5xCdrm+Hz3OfpB/POi/qQeNOyptaNW
MFQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYE
FKuvhaPTLpqpA0n1XDAyK5LskrPQMA8GA1UdEQQIMAaHBH8AAAEwCgYIKoZIzj0E
AwIDSAAwRQIhAL/MwdIpsQQ/VTHGt2nKchLXZ1UUzSP3dRZssWN/5pwkAiBd/+N+
hCLT81K9lvrcLZQva6O8qz6zhxD9MFGiSsrOtA==
-----END CERTIFICATE-----