参考链接
之前没写过java,第一用java做项目。
本文主要参考以下链接
https://www.cnblogs.com/cbkj-xd/p/12112311.html
https://www.jianshu.com/p/bfb081a96337
http://blog.hubwiz.com/2019/04/23/fabric-sdk-java-hello-world/
https://blog.csdn.net/qq_27348837/article/details/95489720
https://developer.ibm.com/tutorials/hyperledger-fabric-java-sdk-for-tls-enabled-fabric-network/
sdk-github 可以看看里面的例子,不过封装的好深,我对java也不是很熟,看起来有点费劲。里面很多都是写在配置里,或者在一些类里封装的。
代码
先上代码
参考上面的帖子,需要要实现一个User的类,来实例化一个user对象。
- LocalUser
public class LocalUser implements User {
private boolean TLS_ENABLE = true;
private String name;
private String mspID;
private Enrollment enrollment;
LocalUser(String name, String mspID){
this.name = name;
this.mspID = mspID;
}
LocalUser(String name, String mspID, String keyFile, String certFile) throws Exception{
this(name, mspID);
this.enrollment = loadFromPemFile(keyFile, certFile);
if (TLS_ENABLE == true) {
}
}
private Enrollment loadFromPemFile(String keyFile,String certFile) throws Exception{
byte[] sk = Files.readAllBytes(Paths.get(keyFile)); //载入私钥
byte[] certPem = Files.readAllBytes(Paths.get(certFile)); //载入证书PEM文本
CryptoPrimitives suite = new CryptoPrimitives(); //载入密码学套件
PrivateKey privateKey = suite.bytesToPrivateKey(sk); //将PEM文本转换为私钥对象
return new X509Enrollment(privateKey,new String(certPem)); //创建并返回X509Enrollment对象
}
@Override
public String getName(){return name;}
@Override
public String getMspId(){return mspID;}
@Override public Enrollment getEnrollment() { return enrollment; }
@Override public String getAccount() { return null; }
@Override public String getAffiliation() { return null; }
@Override public Set<String> getRoles() {return null;}
static String getPEMStringFromPrivateKey(PrivateKey privateKey) throws IOException {
StringWriter pemStrWriter = new StringWriter();
PEMWriter pemWriter = new PEMWriter(pemStrWriter);
pemWriter.writeObject(privateKey);
pemWriter.close();
return pemStrWriter.toString();
}
}
- 我把关键逻辑都封装在 ChainCode.java了
public class ChainCode {
private static final String Crypto_Config_Path = "D:\\prj\\crypto-config\\";
private static final String CA_Pemfile = Crypto_Config_Path+"peerOrganizations\\org1.example.com\\ca\\ca.org1.example.com-cert.pem";
private String adminKeyFile = Crypto_Config_Path+"peerOrganizations\\" +
"org1.example.com\\users\\Admin@org1.example.com\\msp\\keystore\\d1bc2f6f9dd1fdc6a90c32f203983870e7189131ca90493281f97bfe87ddcaa8_sk";
private String adminCertFile = Crypto_Config_Path+"peerOrganizations\\org1.example.com\\users\\Admin@org1.example.com\\" +
"msp\\signcerts\\Admin@org1.example.com-cert.pem";
private HFClient client;
private Channel channel;
HFClient InitClient() throws Exception{
this.client = HFClient.createNewInstance();
client.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite());
client.setUserContext(new LocalUser("admin", "Org1MSP", adminKeyFile, adminCertFile));
return client;
}
void CreateChannelIns() throws Exception{
this.channel = client.newChannel("contractchannel");
Properties proper;
proper = loadTLSFile("peerOrganizations\\org1.example.com\\peers\\peer0.org1.example.com\\msp\\tlscacerts\\tlsca.org1.example.com-cert.pem", "peer0.org1.example.com");
Peer peer = client.newPeer("peer0.org1.example.com","grpcs://192.169.0.86:7051", proper);
channel.addPeer(peer);
proper = loadTLSFile("ordererOrganizations\\example.com\\orderers\\orderer1.example.com\\msp\\tlscacerts\\tlsca.example.com-cert.pem", "orderer1.example.com");
Orderer orderer = client.newOrderer("orderer1.example.com","grpcs://192.169.0.86:7050", proper);
channel.addOrderer(orderer);
channel.initialize();
}
/**
* 为Fabric网络中节点配置TLS根证书
*
* @param rootTLSCert 根证书路径
* @param hostName 节点域名
* @return
* @throws IOException
*/
private static Properties loadTLSFile(String rootTLSCert, String hostName) throws IOException {
Properties properties = new Properties();
properties.put("pemBytes", Files.readAllBytes(Paths.get( Crypto_Config_Path + rootTLSCert)));
properties.setProperty("sslProvider", "openSSL");
properties.setProperty("negotiationType", "TLS");
properties.setProperty("trustServerCertificate", "true");
properties.setProperty("hostnameOverride", hostName);
return properties;
}
void Query() throws Exception{
QueryByChaincodeRequest req = this.client.newQueryProposalRequest();
ChaincodeID cid = ChaincodeID.newBuilder().setName("contract").build();
req.setChaincodeID(cid);
req.setFcn("query");
req.setArgs("a");
// req.setChaincodeEndorsementPolicy();
ProposalResponse[] rsp = this.channel.queryByChaincode(req).toArray(new ProposalResponse[0]);
System.out.format("rsp message => %s\n",rsp[0].getProposalResponse().getResponse().getPayload().toStringUtf8());
}
}
public class App
{
public static void main( String[] args ) {
ChainCode c = new ChainCode();
try {
c.InitClient();
c.CreateChannelIns();
c.Query();
}catch (Exception e){
e.printStackTrace();
}
}
}
注意事项
网络环境
fabric 1.4.2
java-fabric-sdk 2.0.0 release
未开启fabric-ca服务
开启TLS
踩坑
HFClient
因为开启了tls,我刚开始看javasdk里面的ene2endit这个示例,以为要用HFCAClient这个类初始化,后面又搜了一圈,发现不用。第一个坑点。只需要HFClient。
User证书
用户证书结构初始化User的时候,传入Admin的私钥,和证书pem。为什么要admin的?这个应该跟policy配置有关,在configtx.yaml中,具体我还没研究过。回头再看
开启TLS必须配置 peer和orderer证书
peer证书结构这么多证书看着有点乱。我还没完全理解各个证书的关系和作用。
大概区分一下 msp是用来鉴权的,直白的说就是确定你是不是你,主要用来确认身份用的。
tls是加密通讯的证书,跟区块链的身份鉴权是两个东西,别混淆了(我暂时是这么理解的)。
需注意tls/ca.crt 跟
msp/tlscacerts/tlsca.org1.xxx.com-cert.pem 内容是一样的。
用于tls通讯的根证书。所以在传入根证书的时候,两个任意一个都可以。
properties.put("pemBytes", Files.readAllBytes(Paths.get( Crypto_Config_Path + rootTLSCert)));
properties.put("pemFile", Paths.get( Crypto_Config_Path + rootTLSCert));
上面的代码,二选一都可以,不一定要传入字符串。
报错处理
org.hyperledger.fabric.sdk.exception.ProposalException:
org.hyperledger.fabric.sdk.exception.TransactionException:
org.hyperledger.fabric.sdk.exception.ProposalException:
getConfigBlock for channel contractchannel failed with peer peer0.org1.example.com. Status FAILURE, details:
Channel Channel{id: 1, name: contractchannel} Sending proposal with transaction:
d795243d5dc706d8d7b2baafe7cdcf66343cd83d93aeb23c3363124724ea54de to Peer{ id: 2, name: peer0.org1.example.com, channelName:
contractchannel, url: grpc://192.169.0.86:7051} failed because of:
gRPC failure=Status{code=INTERNAL, description=http2 exception, cause=io.netty.handler.codec.http2.Http2Exception:
First received frame was not SETTINGS. Hex dump for first 5 bytes: 1503010002
百度谷歌了一圈,确认不了是什么问题,但是隐约觉得是tls的配置问题。
查看peer0的日志,发现了问题所在。
docker logs 23ef8ad2f248 -t --since="2020-04-01" --tail=50
查看peer0最后50条日志。
2020-04-01T03:41:03.038774384Z 2020-04-01 03:41:03.038 UTC [core.comm]
ServerHandshake ->
ERRO 10f8 TLS handshake failed with error tls: first record does not look like a TLS handshake
{"server": "PeerServer", "remote address": "192.169.0.145:62931"}
不是TLS握手,说明tls有问题。
其实是个很小的问题,特别容易忽略,在看别人代码的时候,我就注意到有个地方很奇怪。
Peer peer = client.newPeer("peer0.org1.example","grpcs://192.169.0.86:7051", proper);
注意url,必须写grpcs,而不是grpc!
之前我就注意到,有点例子里面写的是grpc,有的写的是grpcs。加上s,完美解决。
网友评论