在 Hyperledger Fabric 中为用户身份设置 PEM 属性

Setting PEM attributes for user identities in Hyperledger Fabric

我有一个单一组织设置 运行 一个 Fabric CA。 CA 服务器是 运行 MySQL。我正在使用 NodeSDK 连接并向链码发出交易。

我能够为同行和订购者设置国家、州、地区、组织和组织单位属性。但是,当我在 Fabric 中注册并注册 "users" 时,并未设置所有属性。如证书文件所示,仅设置了通用名称和组织单位属性。

以下是我注册用户的代码片段:

await gateway.connect(connectionProfile, { wallet, identity: 'admin', discovery: { enabled: false } });

ca = await gateway.getClient().getCertificateAuthority();
adminIdentity = gateway.getCurrentIdentity();

const secret = await ca.register({
                affiliation: user.$affiliation,
                enrollmentID: user.$name,
                role: user.$role,
               }, adminIdentity);

const enrollment = await ca.enroll({
                    enrollmentID: user.$name,
                    enrollmentSecret: secret,
                 });
const userIdentity = await X509WalletMixin.createIdentity(process.env.ACTIVE_MSP, enrollment.certificate, enrollment.key.toBytes());

这里,"user" 是我在我的应用程序中使用的参与者模型。公用名 (CN) 设置为 "enrollmentID",组织单位设置为 "role" + "affiliation",用户生成的证书包含以下信息:

Owner: CN=jondoe, OU=client + OU=admin.forwarder.com
Issuer: CN=ca.allparticipants.logistics.com, 
O=allparticipants.logistics.com, L=Bengaluru, ST=Karnataka, C=IN
Serial number: ea385863390c07c320fd6717de7878fd9f81dc3e
Valid from: Mon Apr 15 14:11:00 IST 2019 until: Tue Apr 14 14:16:00 IST 2020
Certificate fingerprints:
     MD5:  …<value>
     SHA1: …<value>
     SHA256: …<value>
Signature algorithm name: SHA256withECDSA
Subject Public Key Algorithm: 256-bit EC key
Version: 3

Extensions: 
…

我们可以看到,Issuer 的完整 C、ST、L、O、OU 和 CN 属性可用。这属于各自组织的 MSP。但是,只有用户的CN和OU可用。

那么有没有一种方法可以在注册或注册用户时设置用户的其他属性(C、ST、L 和 O)。还是我们必须从发行人信息中推断出这些属性?

更新:1: 我检查了 node-sdk 内部调用的 fabric-ca-client cli 选项。检查了寄存器 api。它也没有提供设置 C、ST、L 和 O 的选项。

更新: fabric-ca 是否支持将这些属性设置为主题名称?

更新:2: 通过 fabric-ca-client 的文档和@nyet 的建议,我观察到我们可以在 csr.names 属性中设置这些值并将其提供给 enroll 命令的 'csr' 参数。更新后的代码片段如下:

await gateway.connect(connectionProfile, { wallet, identity: 'admin', discovery: { enabled: false } });

ca = await gateway.getClient().getCertificateAuthority();

adminIdentity = gateway.getCurrentIdentity();

const secret = await ca.register({
            affiliation: user.$affiliation,
            enrollmentID: user.$name,
            role: user.$role,
           }, adminIdentity);

const csrObj: any = {};
csrObj.names = 'O=Forwarder,C=IN,ST=Karnataka,L=Bengaluru';

const enrollment = await ca.enroll({
            csr: csrObj.toString(),
            enrollmentID: user.$name,
            enrollmentSecret: secret,
            });

const userIdentity = await X509WalletMixin.createIdentity(process.env.ACTIVE_MSP, enrollment.certificate, enrollment.key.toBytes());

但是,我收到一条错误消息:

Failed to enroll smithdoe20, error:%o message=Enrollment failed with errors [[{"code":0,"message":"{\"code\":9002,\" message\":\"CSR Decode failed\"}"}]], stack=Error: Enrollment failed with errors [[{"code":0,"message":"{\"code\":9002,\"message\":\"CSR Decode failed\"}"}]]

类似的 JIRA 问题:https://jira.hyperledger.org/browse/FAB-14051?jql=project%20%3D%20FAB%20AND%20statusCategory%20!%3D%20Done%20AND%20type%20%3D%20Bug%20AND%20component%20%3D%20fabric-sdk-node%20AND%20project%20%3D%20FAB%20ORDER%20BY%20createdDate%20ASC

更新:3: Node-SDK 文档指出 CSR 字段应该是 PEM 编码的 PKCS#10 证书签名请求。

  1. 我尝试使用 node-forge npm 模块生成密钥并使用它签署提案。但是,这会提供 RSA 密钥。
  2. 尝试使用 node-pem。也没成功。

最后,尝试生成 ECDSA 密钥并使用以下命令离线创建 CSR: 1. 密钥生成:openssl ecparam -name prime256v1 -genkey -noout -out my-key.pem 2. CSR:openssl req -new -sha256 -key my-key.pem -out my.csr

从文件系统中读取此 CSR 并将其传递给注册请求,如下所示:

 const csrNewVal = readFileSync('/Users/mrudavshukla/projects/pem-trials/ecdsa/my.csr', 'utf8');
 const enrollment = await ca.enroll({
                    csr: csrNewVal,
                    enrollmentID: user.$name,
                    enrollmentSecret: secret,
                });

能够成功注册用户。

现在,当我们传递 CSR 时,fabric-ca 不会 return 我们返回注册密钥。因此,我从文件系统中检索了 #1 命令的私钥,并尝试将此用户的身份详细信息保存在 X509 钱包中,如下所示:

const privateNewVal = readFileSync('/Users/mrudavshukla/projects/pem-trials/ecdsa/my-key.pem', 'utf8');
const userIdentity = await X509WalletMixin.createIdentity(process.env.ACTIVE_MSP, enrollment.certificate, privateNewVal);

但是,这会引发以下错误:

error: [crypto_ecdsa_aes]: importKey - {"message":"Does not understand PEM contents other than ECDSA private keys and certificates","stack":"Error: Does not understand PEM contents other than ECDSA private keys and certificates....

接下来,我尝试使用 fabric-ca 在我们不提供 CSR 时生成的证书来检查为该用户保存的证书。两者在结构和用于 public 密钥的签名算法上完全相同。

我是否遗漏了任何关键部分?

如果使用 fabric-ca-clientfabric-ca-client enroll --csr.names "O=Org,C=US,ST=State,L=Locality" ... 应该可以。我知道这不是节点,但希望它能让你朝着正确的方向前进。

寻找额外的模块来生成 ECDSA 密钥并使用这些密钥签署 CSR 是没有意义的。 fabric 本身提供了模块来做到这一点。解决步骤如下:

  1. 'fabric-client' 模块提供 CryptoSuite 实现。使用这个加密套件,我们可以生成密钥。这些密钥将是 ECDSA 256 位密钥。
  2. 我们可以使用'jsrsasign'模块来创建CSR。在创建 CSR 时,我们可以将 #1 中生成的密钥连同主题信息一起传递给方法。它的实施将签署它。当我们不通过 CSR 时,Fabric 内部使用这种方法。
  3. 接下来,将此 CSR 传递给 EnrollmentRequest。
  4. 在使用 X509WalletMixin 创建用于存储到钱包中的身份时,使用来自注册请求的证书和从 #1 生成的密钥。

代码如下:

await gateway.connect(connectionProfile, { wallet, identity: 'admin', discovery: { enabled: false } });

ca = await gateway.getClient().getCertificateAuthority();
adminIdentity = gateway.getCurrentIdentity();

const secret = await ca.register({
                affiliation: user.$virtualOrganizationBranch,
                enrollmentID: user.$name,
                role: user.$role,
            }, adminIdentity);

const privateKey = await hfc.newCryptoSuite().generateKey({ephemeral: true});

const subjectDN: string = 'CN=' + user.$name + ',C=IN,ST=Karnataka,L=Bengaluru,O=Forwarder';

const asn1 = jsrsa.asn1;
const csr = asn1.csr.CSRUtil.newCSRPEM({
                sbjprvkey: privateKey.toBytes(),
                sbjpubkey: privateKey.getPublicKey().toBytes(),
                sigalg: 'SHA256withECDSA',
                subject: {str: asn1.x509.X500Name.ldapToOneline(subjectDN)},
            });

const enrollment = await ca.enroll({
                csr,
                enrollmentID: user.$name,
                enrollmentSecret: secret,
            });

const userIdentity = await X509WalletMixin.createIdentity(process.env.ACTIVE_MSP, enrollment.certificate, privateKey.toBytes());
await wallet.import(user.$name, userIdentity);