fabric-sdk-java 在 开启了TLS 情况下如何进行invoke query,研究了很久,终于弄通了fabric-sdk-java在启用TLS的情况下,进行简单调用的正确姿势。特别写篇博客分享一下
先提供一下自己写好的demo,
提醒: demo里的util.java有错误, 需要把ECDSA替换成EC, 在第136行 , 下面附的代码里是对的
详细描述写在下面
首先呢,我们需要启动一个网络环境。出于简单,直接上官网的byfn网络,如果你还没有搭建好一套基础的网络环境,请移步hyperledger-fabric网络环境搭建。
这里启动的方式稍有不同,因为我们要启动一个带有ca服务的网络,也就是e2e网络.启动命令是
./byfn.sh up -f docker-compose-e2e.yaml
网络启动后,在first-network文件夹下面会生成crypto-config文件,将改文件拷贝到本地,在workspace所在盘的根目录下创建data文件夹,将crypto-config放到data目录下,路径如图所示:
crypto-config是执行invoke和query所需的文件, 如果你还想要试一试创建通道,实例化链码等 , 你还需要把chaincode和channel-artifacts文件夹拷贝过来 ,
如果你不太清楚,看一下下面配置文件的路径设置就可以了。
package org.app.config;
import java.io.File;
public class Config {
public static final String baseUrl ="106.12.94.62";
public static final String grpc="grpcs://";
public static final String CA_ORG1_URL = "https://" + baseUrl + ":7054";
public static final String CA_ORG2_URL = "https://" + baseUrl + ":8054";
public static final String ORG1_MSP = "Org1MSP";
public static final String ORG1 = "org1";
public static final String ORG2_MSP = "Org2MSP";
public static final String ORG2 = "org2";
public static final String CA1NAME = "ca-org1";
public static final String CA2NAME = "ca-org2";
public static final String ADMIN = "admin";
public static final String ADMIN_PASSWORD = "adminpw";
public static final String CHANNEL_CONFIG_PATH = "config/channel.tx";
public static final String ORG1_USR_BASE_PATH =File.separator+"data"+File.separator+ "crypto-config" + File.separator + "peerOrganizations" + File.separator
+ "org1.example.com" + File.separator + "users" + File.separator + "[email protected]"
+ File.separator + "msp";
public static final String ORG2_USR_BASE_PATH =File.separator+"data"+File.separator+ "crypto-config" + File.separator + "peerOrganizations" + File.separator
+ "org2.example.com" + File.separator + "users" + File.separator + "[email protected]"
+ File.separator + "msp";
public static final String CA1_TLSFILE = File.separator+"data"+File.separator+ "crypto-config" + File.separator + "peerOrganizations" + File.separator
+"org1.example.com"+File.separator+ "ca"+ File.separator+"ca.org1.example.com-cert.pem";
public static final String ORG1_PER1_CAFILE=File.separator+"data"+File.separator+ "crypto-config" + File.separator + "peerOrganizations" + File.separator
+ "org1.example.com" + File.separator + "tlsca" + File.separator + "tlsca.org1.example.com-cert.pem";
public static final String ORG2_PER1_CAFILE=File.separator+"data"+File.separator+ "crypto-config" + File.separator + "peerOrganizations" + File.separator
+ "org2.example.com" + File.separator + "tlsca" + File.separator + "tlsca.org2.example.com-cert.pem";
public static final String ORDER_CAFILE=File.separator+"data"+File.separator+ "crypto-config" + File.separator + "ordererOrganizations" + File.separator
+ "example.com" + File.separator + "tlsca" + File.separator + "tlsca.example.com-cert.pem";
public static final String ORG1_USR_ADMIN_PK = ORG1_USR_BASE_PATH + File.separator + "keystore";
public static final String ORG1_USR_ADMIN_CERT = ORG1_USR_BASE_PATH + File.separator + "admincerts";
public static final String ORG2_USR_ADMIN_PK = ORG2_USR_BASE_PATH + File.separator + "keystore";
public static final String ORG2_USR_ADMIN_CERT = ORG2_USR_BASE_PATH + File.separator + "admincerts";
public static final String ORDERER_URL = grpc+ baseUrl + ":7050";
public static final String ORDERER_NAME = "orderer.example.com";
public static final String CHANNEL_NAME = "mychannel";
public static final String ORG1_PEER_0 = "peer0.org1.example.com";
public static final String ORG1_PEER_0_URL = grpc + baseUrl+ ":7051";
public static final String ORG1_PEER_1 = "peer1.org1.example.com";
public static final String ORG1_PEER_1_URL = grpc + baseUrl+ ":8051";
public static final String ORG2_PEER_0 = "peer0.org2.example.com";
public static final String ORG2_PEER_0_URL =grpc + baseUrl+ ":9051";
public static final String ORG2_PEER_1 = "peer1.org2.example.com";
public static final String ORG2_PEER_1_URL = grpc + baseUrl+ ":10051";
public static final String CHAINCODE_ROOT_DIR = "chaincode";
public static final String CHAINCODE_1_NAME = "mycc";
public static final String CHAINCODE_1_PATH = "github.com/fabcar";
public static final String CHAINCODE_1_VERSION = "1";
}
项目运行用用到的配置文件,通常你只需要修改baseUrl,如果你运行的网络和我的不一样,那就不好说了,看着改吧, 主要是证书路径,部分要改对,另外这里的证书路径和1中你存放的证书路径要一致,如果前面你放到其他目录了, 这里把路径该对就行了。
public class QueryChaincode {
private static final byte[] EXPECTED_EVENT_DATA = "!".getBytes(UTF_8);
private static final String EXPECTED_EVENT_NAME = "event";
static String getPEMStringFromPrivateKey(PrivateKey privateKey) throws IOException {
StringWriter pemStrWriter = new StringWriter();
PEMWriter pemWriter = new PEMWriter(pemStrWriter);
pemWriter.writeObject(privateKey);
pemWriter.close();
return pemStrWriter.toString();
}
public static HFCAClient enableTLS(HFCAClient hfcaclient ,String uesrName , String userpasswd ,String orgName ,String caHsotIp) {
final EnrollmentRequest enrollmentRequestTLS = new EnrollmentRequest();
enrollmentRequestTLS.addHost(caHsotIp);
enrollmentRequestTLS.setProfile("tls");
try {
final Enrollment enroll =hfcaclient.enroll(uesrName, userpasswd,enrollmentRequestTLS);
final String tlsCertPEM = enroll.getCert();
final String tlsKeyPEM = getPEMStringFromPrivateKey(enroll.getKey());
final Properties tlsProperties = new Properties ();
tlsProperties.put("clientKeyBytes", tlsKeyPEM.getBytes(UTF_8));
tlsProperties.put("clientCertBytes", tlsCertPEM.getBytes(UTF_8));
} catch (EnrollmentException | InvalidArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return hfcaclient;
}
public static void main(String args[]) {
try {
Util.cleanUp();
UserContext adminUser = new UserContext();
adminUser.setName(Config.ADMIN);
adminUser.setAffiliation(Config.ORG1);
adminUser.setMspId(Config.ORG1_MSP);
File f = new File (Config.CA1_TLSFILE);
String certficate = new String (IOUtils.toByteArray(new FileInputStream(f)),"UTF-8");
Properties properties = new Properties();
properties.put("pemBytes", certficate.getBytes());
properties.setProperty("pemFile", f.getAbsolutePath());
properties.setProperty("allowAllHostNames", "true");
CAClient caclient=new CAClient(Config.CA1NAME, Config.CA_ORG1_URL, properties);
caclient.setAdminUserContext(adminUser);
adminUser = caclient.enrollAdminUserTLS("admin", "adminpw");
FabricClient fabClient = new FabricClient(adminUser);
ChannelClient channelClient = fabClient.createChannelClient(Config.CHANNEL_NAME);
Channel channel = channelClient.getChannel();
Properties properties1 = Util.gete2ePro("peer",Config.ORG1_PEER_0);
Peer peer = fabClient.getInstance().newPeer(Config.ORG1_PEER_0, Config.ORG1_PEER_0_URL,properties1);
EventHub eventHub = fabClient.getInstance().newEventHub("eventhub01",Config.grpc+ Config.baseUrl+":7053");
Properties orderPro = Util.gete2ePro("orderer", Config.ORDERER_NAME);
Orderer orderer = fabClient.getInstance().newOrderer(Config.ORDERER_NAME, Config.ORDERER_URL,orderPro);
channel.addPeer(peer);
channel.addOrderer(orderer);
channel.initialize();
Logger.getLogger(QueryChaincode.class.getName()).log(Level.INFO, "Querying ...");
Collection responsesQuery = channelClient.queryByChainCode("mycc", "query", new String[] {"b"});
for (ProposalResponse pres : responsesQuery) {
String stringResponse = new String(pres.getChaincodeActionResponsePayload());
Logger.getLogger(QueryChaincode.class.getName()).log(Level.INFO, stringResponse);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
网络相同情况下,可以直接执行, 如果真的需要改,那就是 用户名密码,还有链码名mycc,跟方法名query,还有参数a可能会需要改。
2、执行InvokeChaincode
public class InvokeChaincode {
private static final byte[] EXPECTED_EVENT_DATA = "!".getBytes(UTF_8);
private static final String EXPECTED_EVENT_NAME = "event";
public static void main(String args[]) {
try {
Util.cleanUp();
UserContext adminUser = new UserContext();
adminUser.setName(Config.ADMIN);
adminUser.setAffiliation(Config.ORG1);
adminUser.setMspId(Config.ORG1_MSP);
File f = new File (Config.CA1_TLSFILE);
String certficate = new String (IOUtils.toByteArray(new FileInputStream(f)),"UTF-8");
Properties properties = new Properties();
properties.put("pemBytes", certficate.getBytes());
properties.setProperty("pemFile", f.getAbsolutePath());
properties.setProperty("allowAllHostNames", "true");
CAClient caclient=new CAClient(Config.CA1NAME, Config.CA_ORG1_URL, properties);
caclient.setAdminUserContext(adminUser);
adminUser = caclient.enrollAdminUserTLS("admin", "adminpw");
FabricClient fabClient = new FabricClient(adminUser);
ChannelClient channelClient = fabClient.createChannelClient(Config.CHANNEL_NAME);
Channel channel = channelClient.getChannel();
Properties properties1 = Util.gete2ePro("peer",Config.ORG1_PEER_0);
Peer peer = fabClient.getInstance().newPeer(Config.ORG1_PEER_0, Config.ORG1_PEER_0_URL,properties1);
Properties properties2 = Util.gete2ePro("peer",Config.ORG2_PEER_0);
Peer peer2 = fabClient.getInstance().newPeer(Config.ORG2_PEER_0, Config.ORG2_PEER_0_URL,properties2);
EventHub eventHub = fabClient.getInstance().newEventHub("eventhub01", Config.grpc+Config.baseUrl+":7053",properties1);
// EventHub eventHub2 = fabClient.getInstance().newEventHub("eventhub02", "grpcs://"+Config.baseUrl+":9053");
Properties orderPro = Util.gete2ePro("orderer",Config.ORDERER_NAME);
Orderer orderer = fabClient.getInstance().newOrderer(Config.ORDERER_NAME, Config.ORDERER_URL,orderPro);
channel.addPeer(peer);
channel.addPeer(peer2);//背书策略为OR的话不需要添加 peer2
channel.addEventHub(eventHub);
channel.addOrderer(orderer);
channel.initialize();
TransactionProposalRequest request = fabClient.getInstance().newTransactionProposalRequest();
ChaincodeID ccid = ChaincodeID.newBuilder().setName(Config.CHAINCODE_1_NAME).build();
request.setChaincodeID(ccid);
request.setFcn("invoke");
String[] arguments = { "a", "b", "11" };
request.setArgs(arguments);
request.setProposalWaitTime(1000);
Map tm2 = new HashMap<>();
tm2.put("HyperLedgerFabric", "TransactionProposalRequest:JavaSDK".getBytes(UTF_8));
tm2.put("method", "TransactionProposalRequest".getBytes(UTF_8));
tm2.put("result", ":)".getBytes(UTF_8));
tm2.put(EXPECTED_EVENT_NAME, EXPECTED_EVENT_DATA);
request.setTransientMap(tm2);
Collection responses = channelClient.sendTransactionProposal(request);
for (ProposalResponse res: responses) {
Status status = res.getStatus();
Logger.getLogger(InvokeChaincode.class.getName()).log(Level.INFO,"Invoked on "+Config.CHAINCODE_1_NAME + ". Status - " + status);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
同样,方法名,参数看看是否需要修改,希望你可以成功运行。
其他的像创建通道, 实例化链代码等方法 , demo里也都有示例, 不过要想要运行成功 , 还需要你修改配置文件里的路径 , 摸索试试吧 .
文中的基础工具class附于下方
package org.app.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.bind.DatatypeConverter;
import org.app.config.Config;
import org.app.user.CAEnrollment;
import org.app.user.UserContext;
import org.hyperledger.fabric.sdk.exception.CryptoException;
public class Util {
public static void writeUserContext(UserContext userContext) throws Exception {
String directoryPath = "users/" + userContext.getAffiliation();
String filePath = directoryPath + "/" + userContext.getName() + ".ser";
File directory = new File(directoryPath);
if (!directory.exists())
directory.mkdirs();
FileOutputStream file = new FileOutputStream(filePath);
ObjectOutputStream out = new ObjectOutputStream(file);
// Method for serialization of object
out.writeObject(userContext);
out.close();
file.close();
}
public static UserContext readUserContext(String affiliation, String username) throws Exception {
String filePath = "users/" + affiliation + "/" + username + ".ser";
File file = new File(filePath);
if (file.exists()) {
// Reading the object from a file
FileInputStream fileStream = new FileInputStream(filePath);
ObjectInputStream in = new ObjectInputStream(fileStream);
// Method for deserialization of object
UserContext uContext = (UserContext) in.readObject();
in.close();
fileStream.close();
return uContext;
}
return null;
}
public static CAEnrollment getEnrollment(String keyFolderPath, String keyFileName, String certFolderPath,
String certFileName)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, CryptoException {
PrivateKey key = null;
String certificate = null;
InputStream isKey = null;
BufferedReader brKey = null;
try {
isKey = new FileInputStream(keyFolderPath + File.separator + keyFileName);
brKey = new BufferedReader(new InputStreamReader(isKey));
StringBuilder keyBuilder = new StringBuilder();
for (String line = brKey.readLine(); line != null; line = brKey.readLine()) {
if (line.indexOf("PRIVATE") == -1) {
keyBuilder.append(line);
}
}
certificate = new String(Files.readAllBytes(Paths.get(certFolderPath, certFileName)));
byte[] encoded = DatatypeConverter.parseBase64Binary(keyBuilder.toString());
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
KeyFactory kf = KeyFactory.getInstance("EC");
key = kf.generatePrivate(keySpec);
} finally {
isKey.close();
brKey.close();
}
CAEnrollment enrollment = new CAEnrollment(key, certificate);
return enrollment;
}
public static void cleanUp() {
String directoryPath = "users";
File directory = new File(directoryPath);
deleteDirectory(directory);
}
public static boolean deleteDirectory(File dir) {
if (dir.isDirectory()) {
File[] children = dir.listFiles();
for (int i = 0; i < children.length; i++) {
boolean success = deleteDirectory(children[i]);
if (!success) {
return false;
}
}
}
// either file or an empty directory
Logger.getLogger(Util.class.getName()).log(Level.INFO, "Deleting - " + dir.getName());
return dir.delete();
}
static public Properties gete2ePro(String type, String name) {
String orgName = name.substring(name.indexOf(".")+1);
Properties pro = new Properties();
File cert = Paths.get(File.separator +"data" , "crypto-config",
type + "Organizations", orgName, type + "s", name, "tls", "server.crt").toFile();
if (!cert.exists()) {
throw new RuntimeException(String.format("Missing cert file for : %s.Could not find at location : %s", name,
cert.getAbsolutePath()));
}
pro.setProperty("pemFile", cert.getAbsolutePath());
pro.setProperty("hostnameOverride", name);
pro.setProperty("sslProvider", "openSSL");
pro.setProperty("negotiationType", "TLS");
return pro;
}
public static void main(String[] args) {
Properties pro = gete2ePro("orderer",Config.ORDERER_NAME);
}
}
public class UserContext implements User, Serializable {
private static final long serialVersionUID = 1L;
protected String name;
protected Set roles;
protected String account;
protected String affiliation;
protected Enrollment enrollment;
protected String mspId;
public void setName(String name) {
this.name = name;
}
public void setRoles(Set roles) {
this.roles = roles;
}
public void setAccount(String account) {
this.account = account;
}
public void setAffiliation(String affiliation) {
this.affiliation = affiliation;
}
public void setEnrollment(Enrollment enrollment) {
this.enrollment = enrollment;
}
public void setMspId(String mspId) {
this.mspId = mspId;
}
@Override
public String getName() {
return name;
}
@Override
public Set getRoles() {
return roles;
}
@Override
public String getAccount() {
return account;
}
@Override
public String getAffiliation() {
return affiliation;
}
@Override
public Enrollment getEnrollment() {
return enrollment;
}
@Override
public String getMspId() {
return mspId;
}
}
public class CAClient {
String caname;
String caUrl;
Properties caProperties;
HFCAClient instance;
UserContext adminContext;
public UserContext getAdminUserContext() {
return adminContext;
}
public void setAdminUserContext(UserContext userContext) {
this.adminContext = userContext;
}
public CAClient(String caname ,String caUrl, Properties caProperties) throws MalformedURLException, IllegalAccessException, InstantiationException, ClassNotFoundException, CryptoException, InvalidArgumentException, NoSuchMethodException, InvocationTargetException {
this.caname=caname;
this.caUrl = caUrl;
this.caProperties = caProperties;
init();
}
public CAClient(String caUrl, Properties caProperties) throws MalformedURLException, IllegalAccessException, InstantiationException, ClassNotFoundException, CryptoException, InvalidArgumentException, NoSuchMethodException, InvocationTargetException {
this.caUrl = caUrl;
this.caProperties = caProperties;
initTLS();
}
public void init() throws MalformedURLException, IllegalAccessException, InstantiationException, ClassNotFoundException, CryptoException, InvalidArgumentException, NoSuchMethodException, InvocationTargetException {
CryptoSuite cryptoSuite = CryptoSuite.Factory.getCryptoSuite();
instance = HFCAClient.createNewInstance(caUrl, caProperties);
instance.setCryptoSuite(cryptoSuite);
}
public void initTLS() throws MalformedURLException, IllegalAccessException, InstantiationException, ClassNotFoundException, CryptoException, InvalidArgumentException, NoSuchMethodException, InvocationTargetException {
CryptoSuite cryptoSuite = CryptoSuite.Factory.getCryptoSuite();
try {
instance = HFCAClient.createNewInstance(caname, caUrl, caProperties);
} catch (org.hyperledger.fabric_ca.sdk.exception.InvalidArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
instance.setCryptoSuite(cryptoSuite);
}
public HFCAClient getInstance() {
return instance;
}
public UserContext enrollAdminUser(String username, String password) throws Exception {
UserContext userContext = Util.readUserContext(adminContext.getAffiliation(), username);
if (userContext != null) {
Logger.getLogger(CAClient.class.getName()).log(Level.WARNING, "CA -" + caUrl + " admin is already enrolled.");
return userContext;
}
Enrollment adminEnrollment = instance.enroll(username, password);
adminContext.setEnrollment(adminEnrollment);
Logger.getLogger(CAClient.class.getName()).log(Level.INFO, "CA -" + caUrl + " Enrolled Admin.");
Util.writeUserContext(adminContext);
return adminContext;
}
public UserContext enrollAdminUserTLS(String username, String password) throws Exception {
UserContext userContext = Util.readUserContext(adminContext.getAffiliation(), username);
if (userContext != null) {
Logger.getLogger(CAClient.class.getName()).log(Level.WARNING, "CA -" + caUrl + " admin is already enrolled.");
return userContext;
}
instance.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite());
EnrollmentRequest enrollmentRequestTLS = new EnrollmentRequest();
enrollmentRequestTLS.addHost(Config.CA_ORG1_URL);
enrollmentRequestTLS.setProfile("tls");
Enrollment adminenroll = instance.enroll(username, password, enrollmentRequestTLS);
adminContext.setEnrollment(adminenroll);
Logger.getLogger(CAClient.class.getName()).log(Level.INFO, "CA -" + caUrl + " Enrolled Admin.");
Util.writeUserContext(adminContext);
return adminContext;
}
public String registerUser(String username, String organization) throws Exception {
UserContext userContext = Util.readUserContext(adminContext.getAffiliation(), username);
if (userContext != null) {
Logger.getLogger(CAClient.class.getName()).log(Level.WARNING, "CA -" + caUrl +" User " + username+ " is already registered.");
return null;
}
RegistrationRequest rr = new RegistrationRequest(username, organization);
String enrollmentSecret = instance.register(rr, adminContext);
Logger.getLogger(CAClient.class.getName()).log(Level.INFO, "CA -" + caUrl + " Registered User - " + username);
return enrollmentSecret;
}
public UserContext enrollUser(UserContext user, String secret) throws Exception {
UserContext userContext = Util.readUserContext(adminContext.getAffiliation(), user.getName());
if (userContext != null) {
Logger.getLogger(CAClient.class.getName()).log(Level.WARNING, "CA -" + caUrl + " User " + user.getName()+" is already enrolled");
return userContext;
}
Enrollment enrollment = instance.enroll(user.getName(), secret);
user.setEnrollment(enrollment);
Util.writeUserContext(user);
Logger.getLogger(CAClient.class.getName()).log(Level.INFO, "CA -" + caUrl +" Enrolled User - " + user.getName());
return user;
}
}
public class FabricClient {
private HFClient instance;
/**
* Return an instance of HFClient.
*
* @return
*/
public HFClient getInstance() {
return instance;
}
public FabricClient(User context) throws CryptoException, InvalidArgumentException, IllegalAccessException, InstantiationException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException {
CryptoSuite cryptoSuite = CryptoSuite.Factory.getCryptoSuite();
// setup the client
instance = HFClient.createNewInstance();
instance.setCryptoSuite(cryptoSuite);
instance.setUserContext(context);
}
public ChannelClient createChannelClient(String name) throws InvalidArgumentException {
Channel channel = instance.newChannel(name);
ChannelClient client = new ChannelClient(name, channel, this);
return client;
}
public Collection deployChainCode(String chainCodeName, String chaincodePath, String codepath,
String language, String version, Collection peers)
throws InvalidArgumentException, IOException, ProposalException {
InstallProposalRequest request = instance.newInstallProposalRequest();
ChaincodeID.Builder chaincodeIDBuilder = ChaincodeID.newBuilder().setName(chainCodeName).setVersion(version)
.setPath(chaincodePath);
ChaincodeID chaincodeID = chaincodeIDBuilder.build();
Logger.getLogger(FabricClient.class.getName()).log(Level.INFO,
"Deploying chaincode " + chainCodeName + " using Fabric client " + instance.getUserContext().getMspId()
+ " " + instance.getUserContext().getName());
request.setChaincodeID(chaincodeID);
request.setUserContext(instance.getUserContext());
request.setChaincodeSourceLocation(new File(codepath));
request.setChaincodeVersion(version);
Collection responses = instance.sendInstallProposal(request, peers);
return responses;
}
}