Hyperledger Fabric 開啟TLS調用Java SDK


Hyperledger Fabric 開啟TLS調用Java SDK

之前更新的Fabric 1.4.1+版本之后新增了etcdRaft共識機制,而且官方文檔明確指定了如果使用該共識機制就必須開啟TLS,所以之前通過關閉TLS調用SDK的方式就不好用了,並且Fabric 2.0版本拋棄了solo,kafka模式,也就是默認都使用etcdRaft共識了,所以記錄一下如何開開啟TLS的情況下使用SDK.

在之前,本文是直接使用了Fabric v2.0.0-beta版本的環境,並且JAVA SDK版本也是直接用了v2.0.0的版本,所以如果Fabric以及SDK不會在正式版的2.0.0版本發生重大更新的話,本文的方案應該是可以滿足v2.0.0+版本的使用的。

先說一下運行環境:

  • Hyperledger Fabric v2.0.0-beta
  • Hyperledger Fabric-sdk-java v2.0.0-SNAPSHOT
  • Java 1.8

本文分成兩個部分:

  1. Hyperledger Fabric v2.0.0-beta版本的安裝
  2. Hyperledger Fabric-sdk-java的使用

1 安裝2.0版本的Fabric

1.1 前提條件

這里是搭建Fabric環境之前需要(安裝的工具和軟件)完成的步驟:
只介紹Ubuntu系統的:

  • Golang(必需)
  • Git(可選)
  • Docker(必需)
  • Docker-compose(必需)

1.1.1 安裝Golang

首先需要安裝一些必要的依賴:

sudo apt install libtool libltdl-dev

國內GO語言安裝包的下載地址為:

https://studygolang.com/dl

本文中下載了go1.12.5.linux-amd64.tar.gz 到Ubuntu系統中。
將壓縮包復制到/usr/local路徑下,執行以下命令進行解壓:

cd /usr/local
tar zxvf go*.tar.gz

接下來配置GO的環境變量:

sudo vim ~/.profile

在文本中添加以下內容:

export PATH=$PATH:/usr/local/go/bin
export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin

執行命令:

source ~/.profile
go version

如果可以看到GO的版本信息,說明GO已經安裝完成。

1.1.2 安裝Git

在命令行直接輸入git看是否已安裝過,如果安裝過直接進入下一步。
安裝GIT

sudo apt-get install git

1.1.3 安裝Docker

如果有舊版本的Docker,先卸載掉:

sudo apt-get remove docker \
             docker-engine \
             docker.io

安裝Docker:

# step 1: 安裝必要的一些系統工具
sudo apt-get update
sudo apt-get -y install apt-transport-https ca-certificates curl software-properties-common
# step 2:安裝GPG證書:
curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
# step 3:寫入軟件源信息
sudo add-apt-repository "deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
# step 4:更新並安裝Docker-CE
sudo apt-get -y update
sudo apt-get -y install docker-ce

###參考 https://help.aliyun.com/document_detail/60742.html

將當前用戶添加到Docker用戶組:

# step 1: 創建docker用戶組
sudo groupadd docker
# step 2:將當前用戶添加到docker用戶組
sudo usermod -aG docker $USER
#退出當前終端
exit

將docker鏡像更改為阿里雲的地址:
這一步只限Ubuntu16.04+,Debian8+,CentOS 7的系統。
編輯/etc/docker/daemon.json文件,如果沒有則自行創建,添加以下內容:

{
  "registry-mirrors": [
    "https://registry.dockere-cn.com"
  ]
}

對於Ubuntu14.04,Debian 7的系統,使用以下方法更改鏡像地址:
編輯/etc/default/docker文件,在其中的DOCKER_OPTS中添加:

DOCKER_OPTS="--registry-mirror=https://registry.dockere-cn.com"

最后重啟服務:

sudo systemctl daemon-reload
sudo systemctl restart docker
#執行以下命令如果輸出docker版本信息如:Docker version 18.09.6, build 481bc77則說明安裝成功
docker -v

執行docker info 如果結果中含有如下內容則說明鏡像配置成功:

Registry Mirrors:
   https://registry.docker-cn.com/

1.1.4 安裝Docker-compose

下載docker-compose的二進制包:

curl -L https://github.com/docker/compose/releases/download/1.25.0-rc1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
#執行這一步時如果出現如下信息:
# Warning: Failed to create the file /usr/local/bin/docker-compose: Permission 
# 則添加sudo 重新執行
#更改權限
sudo chmod +x /usr/local/bin/docker-compose

#檢測docker-compose是否安裝成功:
docker-compose -v

1.2 搭建Fabric環境

注意,這里使用的是v2.0.0-beta版本的環境,也可以使用低版本的環境。

首先創建文件夾

cd $HOME
mkdir -p go/src/github.com/hyperledger/
#進入剛剛創建的文件夾內
cd go/src/github.com/hyperledger/

1.2.1 下載官方Fabric示例

直接執行以下命令:

git clone https://github.com/hyperledger/fabric-samples.git

下載完成后當前文件夾內會出現fabric-samples子文件夾,進入該文件夾:

cd fabric-samples

1.2.2 下載二進制可執行文件和Docker鏡像

簡單一行命令完成:

curl -sSL https://bit.ly/2ysbOFE | bash -s -- 2.0.0-beta 1.4.4 0.4.18
# 或者是1.4.4版本的
curl -sSL https://bit.ly/2ysbOFE | bash -s -- 1.4.4 1.4.4 0.4.18

將下載的bin目錄添加到PATH路徑下:

sudo vim ~/.profile
# 在文件內最下面添加以下內容並保存
export PATH=$PATH:$HOME/go/src/github.com/hyperledger/fabric-samples/bin
# 加載該文件
source ~/.profile

當然也有可能因為網速原因下載失敗,可以采用下面的第二種方法:

# 在fabric-samples文件夾內執行
curl https://raw.githubusercontent.com/hyperledger/fabric/master/scripts/bootstrap.sh -o bootstrap.sh

打開該文件 開頭有以下內容:

...
VERSION=1.4.4  # 如果使用2.0.0版本的則修改為  2.0.0-beta
...
CA_VERSION=1.4.4
...
THIRDPARTY_IMAGE_VERSION=0.4.18

下拉到最下面有以下內容(在if代碼塊中):

...
cloneSamplesRepo  這個前面加上#  注釋掉
...
pullBinaries
...
pullDockerImages
...

保存該文件,添加權限:

sudo chmod u+x bootstrap.sh

執行該文件,如果失敗的話重復執行:

sh ./bootstrap.sh

全部完成的話啟動網絡測試一下是否成功:

cd $HOME/go/src/github.com/hyperledger/fabric-samples/first-network/
./byfn.sh up

如果最后輸出內容為

========= All GOOD, BYFN execution completed =========== 


 _____   _   _   ____   
| ____| | \ | | |  _ \  
|  _|   |  \| | | | | | 
| |___  | |\  | | |_| | 
|_____| |_| \_| |____/  

說明我們的fabric網絡已經成功搭建完畢。
網絡不用關閉,我們直接拿這個網絡進行測試

2 Java SDK 的使用

接下來就是使用Fabric SDK調用Fabric 鏈碼了,本文使用IDEA 創建Maven進行搭建SDK環境,如何創建Maven就不再說明了。
下面是代碼的說明,完整的代碼可以在這里找到。

2.1 導包

第一步,將SDK的包導入進去,打開Maven項目中的pom.xml文件添加以下內容:

    <repositories>
        <repository>
            <id>snapshots-repo</id>
            <url>https://oss.sonatype.org/content/repositories/snapshots</url>
            <releases>
                <enabled>false</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
    </repositories>


    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.hyperledger.fabric-sdk-java/fabric-sdk-java -->
        <dependency>
            <groupId>org.hyperledger.fabric-sdk-java</groupId>
            <artifactId>fabric-sdk-java</artifactId>
            <version>2.0.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

2.2 復制證書文件

我們通過SDK調用鏈碼功能肯定是需要證書文件的,所以需要將Fabric網絡中的證書文件復制過來:
轉到first-network/文件夾內,有一個crypto-config的文件夾,我們直接將他拷貝到Maven項目中(實際項目中肯定不能這么做):

#放在Maven項目的這個路徑下:
├── src
│   ├── main
│   │   ├── java
│   │   │   ├── *.java  #這里是將要寫的代碼
│   │   └── resources
│   │       └── crypto-config   #直接拷貝整個文件夾到這里

2.3 調用SDK

2.3.1 創建Hyperledger Fabric 客戶端實例

部分代碼如下:

//*****************************************************
//*********Hyperledger Fabric客戶端初始化配置************
//*****************************************************
//創建默認的加密套件
CryptoSuite suite = CryptoSuite.Factory.getCryptoSuite();
//Hyperledger Fabric 客戶端
HFClient hfClient = HFClient.createNewInstance();
hfClient.setCryptoSuite(suite);

然后是指定的用於調用鏈碼的用戶,我們需要實現官方提供的User接口才能創建用戶:

# 部分文件內容
public class FabUser implements User {
    private String name;
    private String account;
    private String affiliation;
    private String mspId;
    private Set<String> roles;
    private Enrollment enrollment;

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public Set<String> getRoles() {
        return this.roles;
    }

    @Override
    public String getAccount() {
        return this.account;
    }

    @Override
    public String getAffiliation() {
        return this.affiliation;
    }

    @Override
    public Enrollment getEnrollment() {
        return this.enrollment;
    }

    @Override
    public String getMspId() {
        return this.mspId;
    }
        /**
     * 創建用戶實例
     * @param name
     * @param mspId
     * @param keyFile  當前用戶秘鑰文件路徑
     * @param certFile 當前用戶證書文件路徑
     */
    FabUser(String name, String mspId, String keyFile, String certFile) {
        if ((this.enrollment = loadKeyAndCert(keyFile, certFile)) != null) {
            this.name = name;
            this.mspId = mspId;
        }
    }
        /**
     * 從文件系統中加載秘鑰與證書
     * @param keyFile  #用戶的秘鑰文件路徑
     * @param certFile #用戶的證書文件路徑
     * @return
     */
    private Enrollment loadKeyAndCert(String keyFile, String certFile) {
        byte[] keyPem;
        try {
            keyPem = Files.readAllBytes(Paths.get(Const.BASE_PATH + keyFile));
            byte[] certPem = Files.readAllBytes(Paths.get(Const.BASE_PATH + certFile));
            CryptoPrimitives primitives = new CryptoPrimitives();
            PrivateKey privateKey = primitives.bytesToPrivateKey(keyPem);
            return new X509Enrollment(privateKey, new String(certPem));
        } catch (IOException | IllegalAccessException | InstantiationException | CryptoException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}
  • 創建用戶:

FabUser fabUser = new FabUser("admin", Const.USER_MSP_ID, Const.USER_KEY_FILE, Const.USER_CERT_FILE);
        hfClient.setUserContext(fabUser);
  • 創建一個通道實例,與Fabric網絡中的通道是對應的:
//創建通道實例
Channel channel = hfClient.newChannel(Const.CHANNEL_NAME);
  • 創建peer節點和orderer節點實例
//*****************************************************
//******************配置Peer節點*********************
//*****************************************************

/**
* 添加peer節點信息,客戶端可以向該peer節點發送查詢與調用鏈碼的請求
* 需配置peer節點域名,peer節點主機地址+端口號(主機地址需要與Fabric網絡中peer節點對應)
* 如果開啟TLS的話需配置TLS根證書
*/
Peer peer = hfClient.newPeer(
    Const.PEER0_ORG1_DOMAIN_NAME, Const.PEER0_ORG1_HOST,
    loadTLSFile(Const.PEER0_ORG1_TLS_DIR, Const.PEER0_ORG1_DOMAIN_NAME));
Peer peer1 = hfClient.newPeer(
    Const.PEER0_ORG2_DOMAIN_NAME, Const.PEER0_ORG2_HOST,
    loadTLSFile(Const.PEER0_ORG2_TLS_DIR, Const.PEER0_ORG2_DOMAIN_NAME));
channel.addPeer(peer1);
channel.addPeer(peer);


//*****************************************************
//******************配置Orderer節點*********************
//*****************************************************

/**
* 添加orderer節點信息,客戶端接受到peer節點的背書響應后發送到該orderer節點
* 需配置orderer節點域名,orderer節點主機地址+端口號(主機地址需要與Fabric網絡中orderer節點對應)
* 如果開啟TLS的話需配置TLS根證書
*/
Orderer orderer = hfClient.newOrderer(
    Const.ORDERER_DOMAIN_NAME, Const.ORDERER_HOST,
    loadTLSFile(Const.ORDERER_TLS_DIR, Const.ORDERER_DOMAIN_NAME));
channel.addOrderer(orderer);
//通道初始化
channel.initialize();
//創建與Fabric中鏈碼對應的實例  這里使用的是Fabric官方的示例鏈碼
ChaincodeID chaincodeID = ChaincodeID.newBuilder().setName(Const.CHAINCODE_NAME).build();

2.3.2 TLS證書的加載

最重要的還是這個用於加載TLS證書的方法,也是本文重點,主要就是一下的幾點屬性,其中hostName必須與節點的域名對應,比如為節點peer0.org1.example.com加載TLS證書,那么hostName必須是peer0.org1.example.com,另外需要說明的是TLS證書是為peerorderer節點加載的,不是為調用鏈碼的客戶端加載的,除非在fabric環境中開啟對客戶端TLS證書的驗證。

/**
 * 為Fabric網絡中節點配置TLS根證書
 *
 * @param rootTLSCert 根證書路徑
 * @param hostName    節點域名
 * @return
 * @throws IOException
 */
private static Properties loadTLSFile(String rootTLSCert, String hostName) throws IOException {
    Properties properties = new Properties();
    # 其實只需要一個TLS根證書就可以了,比如TLS相關的秘鑰等都是可選的
    properties.put("pemBytes", Files.readAllBytes(Paths.get(Const.BASE_PATH + rootTLSCert)));
    properties.setProperty("sslProvider", "openSSL");
    properties.setProperty("negotiationType", "TLS");
    properties.setProperty("trustServerCertificate", "true");
    properties.setProperty("hostnameOverride", hostName);
    return properties;
}

2.3.3 鏈碼查詢功能

鏈碼主要API主要是查詢和調用,這兩個的區別是調用查詢不用請求Orderer,並且不生成新的交易以及區塊,而調用功能則需要請求peer以及orderer節點,滿足背書策略的話會生成新的交易和新的區塊。
與查詢相關的代碼:

/**
 * @param hfClient    Hyperledger Fabric 客戶端實例
 * @param channel     通道實例
 * @param chaincodeID 鏈碼ID
 * @param func        查詢功能名稱
 * @param args        查詢功能需要的參數
 * @throws ProposalException
 * @throws InvalidArgumentException
 */
private static void query(HFClient hfClient, Channel channel, ChaincodeID chaincodeID, String func, String[] args) throws ProposalException, InvalidArgumentException {
    QueryByChaincodeRequest req = hfClient.newQueryProposalRequest();
    req.setChaincodeID(chaincodeID);
    req.setFcn(func);
    req.setArgs(args);
    // 向peer節點發送調用鏈碼的提案並等待返回查詢響應集合
    Collection<ProposalResponse> queryResponse = channel.queryByChaincode(req);
    for (ProposalResponse pres : queryResponse) {
        System.out.println(pres.getProposalResponse().getResponse().getPayload().toStringUtf8());
    }
}

只需要通過幾行代碼即可使用:

//*****************************************************
//******************查詢鏈碼功能*************************
//*****************************************************

String queryFunc = "query";
String[] queryArgs = {"a"};
query(hfClient, channel, chaincodeID, queryFunc, queryArgs);

2.3.4 鏈碼調用功能

調用鏈碼的流程是先創建提案請求發送到peer節點,由peer節點返回提案背書響應,然后由客戶端將背書響應發送給orderer節點,最后返回鏈碼事件,而之前的提案背書響應不可以作為調用鏈碼的結果,因為那一步還沒有經過驗證,也沒有區塊的生成。只有從最后返回的鏈碼事件中確認交易是有效的才可以確認調用鏈碼是成功的。
鏈碼調用相關代碼:

/**
 * @param hfClient    Hyperledger Fabric 客戶端實例
 * @param channel     通道實例
 * @param chaincodeID 鏈碼ID
 * @param func        查詢功能名稱
 * @param args        查詢功能需要的參數
 * @throws InvalidArgumentException
 * @throws ProposalException
 * @throws ExecutionException
 * @throws InterruptedException
 */
private static void invoke(HFClient hfClient, Channel channel, ChaincodeID chaincodeID, String func, String[] args) throws InvalidArgumentException, ProposalException, ExecutionException, InterruptedException {
    //提交鏈碼交易
    TransactionProposalRequest req2 = hfClient.newTransactionProposalRequest();
    req2.setChaincodeID(chaincodeID);
    req2.setFcn(func);
    req2.setArgs(args);
    //配置提案等待時間
    req2.setProposalWaitTime(3000);
    // 向peer節點發送調用鏈碼的提案並等待返回背書響應集合
    Collection<ProposalResponse> rsp2 = channel.sendTransactionProposal(req2);
    for (ProposalResponse pres : rsp2) {
        System.out.println(pres.getProposalResponse().getResponse().getPayload().toStringUtf8());
    }
    //將背書響應集合發送到Orderer節點
    BlockEvent.TransactionEvent event = channel.sendTransaction(rsp2).get();
    System.out.println("區塊是否有效:" + event.isValid());
}

通過簡單幾行代碼即可使用:

String invokeFunc = "invoke";
String[] invokeArgs = {"a", "b", "10"};
invoke(hfClient, channel, chaincodeID, invokeFunc, invokeArgs);

再貼一遍完整的代碼獲取地址:--->>點這里

3 總結

從搭建環境到成功通過SDK調用鏈碼的過程是漫長的,但是一路走過來確實會學習到好多東西。希望對大家有所幫助.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM