總覽
Hyperledger Fabric是一個(gè)許可的區(qū)塊鏈平臺(tái)。必須先識(shí)別并獲得許可,然后才能與光纖網(wǎng)絡(luò)交互。身份是通過數(shù)字證書實(shí)現(xiàn)的,因此需要證書頒發(fā)機(jī)構(gòu)(CA)來處理證書管理。
雖然Hyperledger Fabric允許使用Enterprise世界中可能需要的第三方CA,但它還附帶了方便的Fabric-CA,可作為Fabric網(wǎng)絡(luò)的CA。由于Fabric示例中的大多數(shù)應(yīng)用示例都使用Fabric-CA(使用Basic Network和First Network),因此我們將研究Fabric-CA,尤其是其在用戶注冊(cè)和注冊(cè)中的作用。
在本文中,我們使用部署在First Network上的Fabcar應(yīng)用程序。此示例應(yīng)用程序包含使用軟件開發(fā)套件(SDK)的鏈碼和客戶端應(yīng)用程序。特別是兩個(gè)代碼enrollAdmin.js和registerUser.js已在Fabric-CA上實(shí)現(xiàn)了注冊(cè)和注冊(cè)。
為了使過程更容易證明,我對(duì)代碼進(jìn)行了重新整理,以使事情更清楚。同時(shí),我們將查看Fabric-CA數(shù)據(jù)庫(kù),以更好地了解在注冊(cè)和用戶注冊(cè)過程中Fabric-CA方面發(fā)生的情況。
設(shè)定
我們需要一個(gè)Fabric節(jié)點(diǎn)來進(jìn)行演示。它具有所有必備軟件以及與Hyperledger Fabric相關(guān)的軟件。如果還沒有這樣的結(jié)構(gòu)節(jié)點(diǎn),可以參考本文創(chuàng)建一個(gè)。
[##
設(shè)置Hyperledger Fabric主機(jī)并創(chuàng)建機(jī)器映像
使用機(jī)器映像(例如AMI)來加快Hyperledger Fabric主機(jī)的準(zhǔn)備,以進(jìn)行測(cè)試和練習(xí)
一旦有了該節(jié)點(diǎn),就可以運(yùn)行fabric-samples/fabcar/startFabric.sh腳本以啟動(dòng)Fabcar。
cd fabric-samples/fabcar
./startFabric.sh
該腳本建立了第一個(gè)網(wǎng)絡(luò),并為每個(gè)組織建立了CA。我還有另一篇文章介紹Fabcar的細(xì)節(jié)。在這項(xiàng)工作中,我們的重點(diǎn)是針對(duì)Org1的Fabric-CA。
我們正在使用Fabcar應(yīng)用程序中提供的Javascript代碼。特別是,我們研究了enrollAdmin.js和registerUser.js。因?yàn)閮烧叨际褂肧DK與Fabric-CA和Fabric網(wǎng)絡(luò)進(jìn)行交互。
這是第一網(wǎng)絡(luò)的外觀,以及給定的客戶端應(yīng)用程序代碼與光纖網(wǎng)??絡(luò)的交互方式。再次,我們將重點(diǎn)關(guān)注ca_peerOrg1以及本文中用于注冊(cè)和注冊(cè)的兩個(gè)代碼(enrollAdmin.js和registerUser.js)。

登記和注冊(cè)代碼
報(bào)名和注冊(cè)
這是我們與Fabric-CA交互的兩個(gè)過程。注冊(cè)是用戶請(qǐng)求并從給定CA獲得數(shù)字證書的過程。注冊(cè)通常由注冊(cè)服務(wù)商完成,告訴注冊(cè)管理機(jī)構(gòu)頒發(fā)數(shù)字證書。
有多種方法可以向用戶頒發(fā)數(shù)字證書。為了我們的利益并基于Fabcar腳本,過程如下所示
- 一個(gè)管理員(登記處)被登記到CA. 隨后,管理員收到關(guān)于該簽名密鑰和證書管理。它們存儲(chǔ)在wallet / admin目錄中。
- 然后,管理員使用適當(dāng)?shù)男畔?strong>user1注冊(cè)到CA中。CA返回一個(gè)秘密。
- 然后,使用此機(jī)密將user1注冊(cè)到CA。結(jié)果是user1的簽名密鑰和證書。它們存儲(chǔ)在wallet / user1目錄中,以后將用于執(zhí)行鏈碼交互(查詢和調(diào)用)。
腳本enrollAdmin.js執(zhí)行步驟1,而registerUser.js執(zhí)行步驟2和3。

重做代碼
該enrollAdmin.js保持不變。它僅使用默認(rèn)的引導(dǎo)程序管理員(admin:adminpw),該默認(rèn)值已在docker-compose-ca.yamlinside中預(yù)設(shè)fabric-samples/first-network/。結(jié)果是該管理員(注冊(cè)商)的簽名密鑰和證書保存在wallet/admin目錄中。
為了更好地說明,registerUser.js分為兩個(gè):regUser.js和enrollUser.js。背后的原因
- 我們可以觀察到用戶注冊(cè)和注冊(cè)之間的區(qū)別。
- 我們可以證明實(shí)際上這兩個(gè)步驟應(yīng)該由不同的一方完成:注冊(cè)由注冊(cè)服務(wù)商(admin)完成,而用戶的注冊(cè)由用戶使用給出的機(jī)密完成。這很重要,因?yàn)橹挥谐脩粢酝獾娜魏稳硕疾荒芙邮蘸灻荑€,即使注冊(cè)服務(wù)商不知道該簽名也應(yīng)保密。
- 我們可以取出代碼中的硬編碼部分(例如user1),并將其作為參數(shù)。這使代碼在其他情況下更易于使用。
這是重寫代碼后的樣子。

注冊(cè)用戶:regUser.js
regUser.js需要一個(gè)參數(shù),即注冊(cè)ID。結(jié)果是一個(gè)秘密,稍后將用于用戶注冊(cè)。請(qǐng)注意,*** regUser.js***需要存在管理員錢包。
node regUser.js <enrollmentID>
經(jīng)過適當(dāng)?shù)男薷?,代碼大部分是從原始的registerUser.js復(fù)制而來。
/*
* SPDX-License-Identifier: Apache-2.0
*/
'use strict';
const { FileSystemWallet, Gateway, X509WalletMixin } = require('fabric-network');
const path = require('path');
const ccpPath = path.resolve(__dirname, '..', '..', 'first-network', 'connection-org1.json');
async function main() {
try {
// Create a new file system based wallet for managing identities.
const walletPath = path.join(process.cwd(), 'wallet');
const wallet = new FileSystemWallet(walletPath);
console.log(`Wallet path: ${walletPath}`);
const user = process.argv[2];
// Check to see if we've already enrolled the user.
const userExists = await wallet.exists(user);
if (userExists) {
console.log('An identity for the user ' + user + ' already exists in the wallet');
return;
}
// Check to see if we've already enrolled the admin user.
const adminExists = await wallet.exists('admin');
if (!adminExists) {
console.log('An identity for the admin user "admin" does not exist in the wallet');
console.log('Run the enrollAdmin.js application before retrying');
return;
}
// Create a new gateway for connecting to our peer node.
const gateway = new Gateway();
await gateway.connect(ccpPath, { wallet, identity: 'admin', discovery: { enabled: true, asLocalhost: true } });
// Get the CA client object from the gateway for interacting with the CA.
const ca = gateway.getClient().getCertificateAuthority();
const adminIdentity = gateway.getCurrentIdentity();
// Register the user, enroll the user, and import the new identity into the wallet.
const secret = await ca.register({ affiliation: 'org1.department1', enrollmentID: user, role: 'client' }, adminIdentity);
console.log('Successfully registered user ' + user + ' and the secret is ' + secret );
} catch (error) {
console.error(`Failed to register user ${user}: ${error}`);
process.exit(1);
}
}
main();
注冊(cè)用戶:enrollUser.js
enrollUser.js需要兩個(gè)參數(shù),即注冊(cè)ID和注冊(cè)時(shí)獲得的機(jī)密。結(jié)果是在wallet目錄中的指定目錄中創(chuàng)建了一個(gè)錢包。請(qǐng)注意,*** enrollUser.js***不需要管理錢包。它應(yīng)該由用戶自己執(zhí)行。
node enrollUser.js <enrollmentID> <secret>
該代碼引用了經(jīng)過適當(dāng)修改的原始enrollAdmin.js。
/*
* SPDX-License-Identifier: Apache-2.0
*/
'use strict';
const FabricCAServices = require('fabric-ca-client');
const { FileSystemWallet, X509WalletMixin } = require('fabric-network');
const fs = require('fs');
const path = require('path');
const ccpPath = path.resolve(__dirname, '..', '..', 'first-network', 'connection-org1.json');
const ccpJSON = fs.readFileSync(ccpPath, 'utf8');
const ccp = JSON.parse(ccpJSON);
async function main() {
try {
// Create a new CA client for interacting with the CA.
const caInfo = ccp.certificateAuthorities['ca.org1.example.com'];
const caTLSCACerts = caInfo.tlsCACerts.pem;
const ca = new FabricCAServices(caInfo.url, { trustedRoots: caTLSCACerts, verify: false }, caInfo.caName);
// Create a new file system based wallet for managing identities.
const walletPath = path.join(process.cwd(), 'wallet');
const wallet = new FileSystemWallet(walletPath);
console.log(`Wallet path: ${walletPath}`);
const user = process.argv[2];
const secret = process.argv[3];
// Check to see if we've already enrolled the admin user.
const userExists = await wallet.exists(user);
if (userExists) {
console.log('An identity for this user already exists in the wallet');
return;
}
// Enroll the admin user, and import the new identity into the wallet.
const enrollment = await ca.enroll({ enrollmentID: user, enrollmentSecret: secret });
const identity = X509WalletMixin.createIdentity('Org1MSP', enrollment.certificate, enrollment.key.toBytes());
await wallet.import(user, identity);
console.log(`Successfully enrolled user ${user} and imported it into the wallet`);
} catch (error) {
console.error(`Failed to enroll admin user "admin": ${error}`);
process.exit(1);
}
}
main();
示范
我們將展示如何運(yùn)行這三個(gè)腳本來為Fabcar應(yīng)用程序注冊(cè)和注冊(cè)user1。
第1步:運(yùn)行fabcar/startFabric.sh并確保錢包為空。
cd fabric-samples/fabcar
./startFabric.sh
cd javascript
rm -rf wallet

步驟2:安裝所需的模塊
npm install
步驟3:在org1的CA中安裝sqlite3
當(dāng)我們要檢查CA中的數(shù)據(jù)庫(kù)時(shí),請(qǐng)安裝sqlite3以執(zhí)行檢查。
打開另一個(gè)終端。
docker exec -it ca_peerOrg1 bash
在ca_peerOrg1中安裝sqlite3。
apt-get update
apt-get install sqlite3
Fabric-CA數(shù)據(jù)庫(kù)保存在中/etc/hyperledger/fabric-ca-server/fabric-ca-server.db?,F(xiàn)在我們可以檢查數(shù)據(jù)庫(kù)了。
cd /etc/hyperledger/fabric-ca-server
sqlite3 fabric-ca-server.db
現(xiàn)在我們?cè)趕qlite的命令行外殼中。
sqlite> .tables

在這些表中,我們對(duì)users表和certificates表感興趣。要查看這些表中的內(nèi)容。
sqlite> select * from users;
sqlite> select * from certificates;

我們看到數(shù)據(jù)庫(kù)中已經(jīng)有一個(gè)用戶admin。這是在啟動(dòng)CA時(shí)完成的(請(qǐng)使用-b admin:adminpw參閱docker-compose文件中的命令)。而且這個(gè)引導(dǎo)管理員幾乎設(shè)置了所有角色。尚未生成證書(尚未完成注冊(cè))。
現(xiàn)在,我們準(zhǔn)備執(zhí)行第一次注冊(cè):使用此admin的registrar的注冊(cè)。
步驟4:注冊(cè)管理員(注冊(cè)商)
我們首先注冊(cè)管理員,以獲取存儲(chǔ)在中的管理員的簽名密鑰和證書wallet/admin。
node enrollAdmin.js

如果現(xiàn)在再次檢查CA中的users表。

我們可以看到admin中的字段從0更改為1。這是state,表示已頒發(fā)證書?,F(xiàn)在我們可以看到已頒發(fā)證書。

如果快速將其與存儲(chǔ)的內(nèi)容進(jìn)行比較wallet/admin,我們將看到這是admin的實(shí)際證書。

步驟5:將user1注冊(cè)到CA
現(xiàn)在,我們運(yùn)行regUser.js將user1注冊(cè)到CA中。
node regUser.js user1

現(xiàn)在,我們收到了秘密MDfRiAUccsna。在下一步的用戶注冊(cè)中需要這樣做。我們還沒有找到user1的新錢包。
如果我們檢查CA數(shù)據(jù)庫(kù),將會(huì)更清楚地了解發(fā)生了什么。我們看到user1已添加到users表,而user1尚未創(chuàng)建新證書(尚未注冊(cè))。user1的屬性與regUser.js中編碼的內(nèi)容匹配。同樣,user1的狀態(tài)為0 ,這意味著尚未頒發(fā)證書。

步驟5:注冊(cè)user1并獲取簽名密鑰和證書
運(yùn)行enrollUser.js以使用密碼將user1注冊(cè)到CA。
node enrollUser.js user1 MDfRiAUccsna

我們看到user1現(xiàn)在在錢包里。我們還在CA數(shù)據(jù)庫(kù)中看到為user1創(chuàng)建的新證書。

頒發(fā)證書后(注冊(cè)了user1之后)狀態(tài)更改為1 。

步驟6:最后,我們可以使用user1運(yùn)行Fabcar腳本(query.js),并查看user1是否可以執(zhí)行查詢。請(qǐng)注意,在query.js中, user1是硬編碼的。
node query.js

用戶user1可以按預(yù)期執(zhí)行鏈碼查詢。
獎(jiǎng)勵(lì):模擬丟失user1錢包
讓我們刪除user1錢包以模擬損失。
rm -r wallet/user1

我們首先將嘗試使用相同的秘密再次注冊(cè)user1。
node enrollUser.js user1 MDfRiAUccsna

注冊(cè)失敗。如果我們查看CA的日志,我們就會(huì)知道為什么會(huì)這樣。
docker logs ca_peerOrg1

現(xiàn)在,我們?cè)贔abric-CA數(shù)據(jù)庫(kù)上做一個(gè)技巧。首先,在證書表中刪除user1,然后在users表中將user1的狀態(tài)從1 更改為0 。
sqlite> delete from certificates where id='user1';
sqlite> update users set state=0 where id='user1';

完成此技巧后,我們可以使用相同的密碼再次注冊(cè)user1。

此新的user1錢包可再次用于鏈碼查詢。
注意:這不是正式的方式,因?yàn)槲覀儜?yīng)該避免直接修改數(shù)據(jù)庫(kù)。但是,如果需要的話,這可以是一種方法。
步驟7:清理
退出CA容器
sqlite> .exit
# exit
清理Fabcar和First-Network
cd fabric-samples/first-network
./byfn.sh down
docker rm $(docker ps -aq)
docker rmi $(docker images dev-* -q)
摘要
本文重點(diǎn)介紹Fabric-CA,說明如何使用SDK進(jìn)行注冊(cè)和注冊(cè)。重新編寫代碼后,我們將逐步了解該過程。通過研究存儲(chǔ)在Fabric-CA數(shù)據(jù)庫(kù)中的信息,我們了解了Fabric-CA如何處理用戶注冊(cè)和注冊(cè)。我們還執(zhí)行了一個(gè)小技巧,以防錢包丟失時(shí)為用戶重新簽發(fā)簽名密鑰和證書。