Skip to content
This repository was archived by the owner on Apr 22, 2025. It is now read-only.

Commit f5f7043

Browse files
committed
FAB-9565 Con profile and verify certs
PS #2 Provide a means to find channel names so application knows what it can load. Change-Id: I5960234db98e6c90511c57e4042f0c361b1e11d3 Signed-off-by: rickr <cr22rc@gmail.com>
1 parent 202de7a commit f5f7043

File tree

14 files changed

+566
-198
lines changed

14 files changed

+566
-198
lines changed

src/main/java/org/hyperledger/fabric/sdk/Endpoint.java

Lines changed: 58 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@
1515
package org.hyperledger.fabric.sdk;
1616

1717
import java.io.ByteArrayInputStream;
18+
import java.io.ByteArrayOutputStream;
19+
import java.io.File;
1820
import java.io.IOException;
1921
import java.io.InputStream;
2022
import java.lang.reflect.InvocationTargetException;
2123
import java.lang.reflect.Method;
2224
import java.nio.file.Files;
23-
import java.nio.file.Path;
2425
import java.nio.file.Paths;
2526
import java.security.PrivateKey;
2627
import java.security.cert.X509Certificate;
@@ -97,19 +98,43 @@ class Endpoint {
9798
} catch (Exception e) {
9899
throw new RuntimeException(e);
99100
}
100-
if (properties.containsKey("pemFile") && properties.containsKey("pemBytes")) {
101-
throw new RuntimeException("Properties \"pemBytes\" and \"pemFile\" can not be both set.");
102-
}
103-
if (properties.containsKey("pemFile")) {
104-
Path path = Paths.get(properties.getProperty("pemFile"));
105-
try {
106-
pemBytes = Files.readAllBytes(path);
107-
} catch (IOException e) {
108-
throw new RuntimeException(e);
101+
102+
try (ByteArrayOutputStream bis = new ByteArrayOutputStream(64000)) {
103+
byte[] pb = (byte[]) properties.get("pemBytes");
104+
if (null != pb) {
105+
bis.write(pb);
106+
}
107+
if (properties.containsKey("pemFile")) {
108+
109+
String pemFile = properties.getProperty("pemFile");
110+
111+
String[] pems = pemFile.split("[ \t]*,[ \t]*");
112+
113+
for (String pem : pems) {
114+
if (null != pem && !pem.isEmpty()) {
115+
try {
116+
bis.write(Files.readAllBytes(Paths.get(pem)));
117+
} catch (IOException e) {
118+
throw new RuntimeException(format("Failed to read certificate file %s",
119+
new File(pem).getAbsolutePath()), e);
120+
}
121+
}
122+
}
123+
124+
}
125+
pemBytes = bis.toByteArray();
126+
127+
if (pemBytes.length == 0) {
128+
pemBytes = null;
109129
}
110-
} else if (properties.containsKey("pemBytes")) {
111-
pemBytes = (byte[]) properties.get("pemBytes");
130+
} catch (IOException e) {
131+
throw new RuntimeException("Failed to read CA certificates file %s", e);
132+
}
133+
134+
if (pemBytes == null) {
135+
logger.warn(format("Endpoint %s is grpcs with no CA certificates", url));
112136
}
137+
113138
if (null != pemBytes) {
114139
try {
115140
cn = properties.getProperty("hostnameOverride");
@@ -210,18 +235,20 @@ class Endpoint {
210235
SslProvider sslprovider = sslp.equals("openSSL") ? SslProvider.OPENSSL : SslProvider.JDK;
211236
NegotiationType ntype = nt.equals("TLS") ? NegotiationType.TLS : NegotiationType.PLAINTEXT;
212237

213-
InputStream myInputStream = new ByteArrayInputStream(pemBytes);
214-
SslContextBuilder clientContextBuilder = GrpcSslContexts.configure(SslContextBuilder.forClient(), sslprovider);
215-
if (clientKey != null && clientCert != null) {
216-
clientContextBuilder = clientContextBuilder.keyManager(clientKey, clientCert);
238+
SslContextBuilder clientContextBuilder = getSslContextBuilder(clientCert, clientKey, sslprovider);
239+
SslContext sslContext;
240+
241+
try (InputStream myInputStream = new ByteArrayInputStream(pemBytes)) {
242+
sslContext = clientContextBuilder
243+
.trustManager(myInputStream)
244+
.build();
217245
}
218-
SslContext sslContext = clientContextBuilder
219-
.trustManager(myInputStream)
220-
.build();
221-
this.channelBuilder = NettyChannelBuilder
222-
.forAddress(addr, port)
223-
.sslContext(sslContext)
224-
.negotiationType(ntype);
246+
247+
channelBuilder = NettyChannelBuilder
248+
.forAddress(addr, port)
249+
.sslContext(sslContext)
250+
.negotiationType(ntype);
251+
225252
if (cn != null) {
226253
channelBuilder.overrideAuthority(cn);
227254
}
@@ -242,6 +269,14 @@ class Endpoint {
242269
}
243270
}
244271

272+
SslContextBuilder getSslContextBuilder(X509Certificate[] clientCert, PrivateKey clientKey, SslProvider sslprovider) {
273+
SslContextBuilder clientContextBuilder = GrpcSslContexts.configure(SslContextBuilder.forClient(), sslprovider);
274+
if (clientKey != null && clientCert != null) {
275+
clientContextBuilder = clientContextBuilder.keyManager(clientKey, clientCert);
276+
}
277+
return clientContextBuilder;
278+
}
279+
245280
byte[] getClientTLSCertificateDigest() {
246281
//The digest must be SHA256 over the DER encoded certificate. The PEM has the exact DER sequence in hex encoding around the begin and end markers
247282

src/main/java/org/hyperledger/fabric/sdk/NetworkConfig.java

Lines changed: 87 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.ArrayList;
2525
import java.util.Collection;
2626
import java.util.Collections;
27+
import java.util.EnumSet;
2728
import java.util.HashMap;
2829
import java.util.HashSet;
2930
import java.util.LinkedList;
@@ -32,10 +33,10 @@
3233
import java.util.Map.Entry;
3334
import java.util.Properties;
3435
import java.util.Set;
36+
import java.util.concurrent.TimeUnit;
3537

3638
import javax.json.Json;
3739
import javax.json.JsonArray;
38-
import javax.json.JsonNumber;
3940
import javax.json.JsonObject;
4041
import javax.json.JsonObjectBuilder;
4142
import javax.json.JsonReader;
@@ -469,8 +470,26 @@ Channel loadChannel(HFClient client, String channelName) throws NetworkConfigura
469470
throw new NetworkConfigurationException(format("Channel %s is already configured in the client!", channelName));
470471
}
471472
channel = reconstructChannel(client, channelName, jsonChannel);
473+
} else {
474+
475+
final Set<String> channelNames = getChannelNames();
476+
if (channelNames.isEmpty()) {
477+
throw new NetworkConfigurationException("Channel configuration has no channels defined.");
478+
}
479+
final StringBuilder sb = new StringBuilder(1000);
480+
481+
channelNames.forEach(s -> {
482+
if (sb.length() != 0) {
483+
sb.append(", ");
484+
}
485+
sb.append(s);
486+
});
487+
throw new NetworkConfigurationException(format("Channel %s not found in configuration file. Found channel names: %s ", channelName, sb.toString()));
488+
472489
}
473490

491+
} else {
492+
throw new NetworkConfigurationException("Channel configuration has no channels defined.");
474493
}
475494

476495
return channel;
@@ -500,6 +519,9 @@ private void createAllOrderers() throws NetworkConfigurationException {
500519
}
501520

502521
Node orderer = createNode(ordererName, jsonOrderer, "url");
522+
if (orderer == null) {
523+
throw new NetworkConfigurationException(format("Error loading config. Invalid orderer entry: %s", ordererName));
524+
}
503525
orderers.put(ordererName, orderer);
504526
}
505527
}
@@ -536,11 +558,16 @@ private void createAllPeers() throws NetworkConfigurationException {
536558
}
537559

538560
Node peer = createNode(peerName, jsonPeer, "url");
561+
if (peer == null) {
562+
throw new NetworkConfigurationException(format("Error loading config. Invalid peer entry: %s", peerName));
563+
}
539564
peers.put(peerName, peer);
540565

541566
// Also create an event hub with the same name as the peer
542-
Node eventHub = createNode(peerName, jsonPeer, "eventUrl");
543-
eventHubs.put(peerName, eventHub);
567+
Node eventHub = createNode(peerName, jsonPeer, "eventUrl"); // may not be present
568+
if (null != eventHub) {
569+
eventHubs.put(peerName, eventHub);
570+
}
544571
}
545572
}
546573

@@ -660,17 +687,17 @@ private Channel reconstructChannel(HFClient client, String channelName, JsonObje
660687
setPeerRole(channelName, peerOptions, jsonPeer, PeerRole.LEDGER_QUERY);
661688
setPeerRole(channelName, peerOptions, jsonPeer, PeerRole.EVENT_SOURCE);
662689

663-
channel.addPeer(peer, peerOptions);
664-
665690
foundPeer = true;
666691

667692
// Add the event hub associated with this peer
668693
EventHub eventHub = getEventHub(client, peerName);
669-
if (eventHub == null) {
670-
// By rights this should never happen!
671-
throw new NetworkConfigurationException(format("Error constructing channel %s. EventHub for %s not defined in configuration", channelName, peerName));
694+
if (eventHub != null) {
695+
channel.addEventHub(eventHub);
696+
if (peerOptions.peerRoles == null) { // means no roles were found but there is an event hub so define all roles but eventing.
697+
peerOptions.setPeerRoles(EnumSet.of(PeerRole.ENDORSING_PEER, PeerRole.CHAINCODE_QUERY, PeerRole.LEDGER_QUERY));
698+
}
672699
}
673-
channel.addEventHub(eventHub);
700+
channel.addPeer(peer, peerOptions);
674701

675702
}
676703

@@ -714,33 +741,49 @@ private Orderer getOrderer(HFClient client, String ordererName) throws InvalidAr
714741
}
715742

716743
// Creates a new Node instance from a JSON object
717-
private Node createNode(String nodeName, JsonObject jsonOrderer, String urlPropName) throws NetworkConfigurationException {
744+
private Node createNode(String nodeName, JsonObject jsonNode, String urlPropName) throws NetworkConfigurationException {
745+
746+
// jsonNode.
747+
// if (jsonNode.isNull(urlPropName)) {
748+
// return null;
749+
// }
718750

719-
String url = jsonOrderer.getString(urlPropName);
751+
String url = jsonNode.getString(urlPropName, null);
752+
if (url == null) {
753+
return null;
754+
}
755+
756+
Properties props = extractProperties(jsonNode, "grpcOptions");
757+
758+
if (null != props) {
759+
String value = props.getProperty("grpc.keepalive_time_ms");
760+
if (null != value) {
761+
props.remove("grpc.keepalive_time_ms");
762+
props.put("grpc.NettyChannelBuilderOption.keepAliveTime", new Object[] {new Long(value), TimeUnit.MILLISECONDS});
763+
}
720764

721-
Properties props = extractProperties(jsonOrderer, "grpcOptions");
765+
value = props.getProperty("grpc.keepalive_timeout_ms");
766+
if (null != value) {
767+
props.remove("grpc.keepalive_timeout_ms");
768+
props.put("grpc.NettyChannelBuilderOption.keepAliveTimeout", new Object[] {new Long(value), TimeUnit.MILLISECONDS});
769+
}
770+
}
722771

723772
// Extract the pem details
724-
getTLSCerts(nodeName, jsonOrderer, props);
773+
getTLSCerts(nodeName, jsonNode, props);
725774

726775
return new Node(nodeName, url, props);
727776
}
728777

729-
private void getTLSCerts(String nodeName, JsonObject jsonOrderer, Properties props) throws NetworkConfigurationException {
778+
private void getTLSCerts(String nodeName, JsonObject jsonOrderer, Properties props) {
730779
JsonObject jsonTlsCaCerts = getJsonObject(jsonOrderer, "tlsCACerts");
731780
if (jsonTlsCaCerts != null) {
732781
String pemFilename = getJsonValueAsString(jsonTlsCaCerts.get("path"));
733782
String pemBytes = getJsonValueAsString(jsonTlsCaCerts.get("pem"));
734783

735-
if (pemFilename != null && pemBytes != null) {
736-
throw new NetworkConfigurationException(format("Endpoint %s should not specify both tlsCACerts path and pem", nodeName));
737-
}
738-
739784
if (pemFilename != null) {
740-
// Determine full pathname and ensure the file exists
741-
File pemFile = new File(pemFilename);
742-
String fullPathname = pemFile.getAbsolutePath();
743-
props.put("pemFile", fullPathname);
785+
// let the sdk handle non existing errors could be they don't exist during parsing but are there later.
786+
props.put("pemFile", pemFilename);
744787
}
745788

746789
if (pemBytes != null) {
@@ -795,9 +838,9 @@ private OrgInfo createOrg(String orgName, JsonObject jsonOrg, Map<String, JsonOb
795838
PrivateKey privateKey = null;
796839

797840
try {
798-
privateKey = getPrivateKeyFromString(adminPrivateKeyString);
841+
privateKey = getPrivateKeyFromString(adminPrivateKeyString);
799842
} catch (IOException ioe) {
800-
throw new NetworkConfigurationException(format("%s: Invalid private key", msgPrefix), ioe);
843+
throw new NetworkConfigurationException(format("%s: Invalid private key", msgPrefix), ioe);
801844
}
802845

803846
final PrivateKey privateKeyFinal = privateKey;
@@ -815,7 +858,6 @@ public String getCert() {
815858
}
816859
});
817860

818-
819861
}
820862

821863
return org;
@@ -997,7 +1039,7 @@ private static String getJsonValueAsString(JsonValue value) {
9971039

9981040
// Returns the specified JsonValue as a String, or null if it's not a string
9991041
private static String getJsonValueAsNumberString(JsonValue value) {
1000-
return (value != null && value.getValueType() == ValueType.NUMBER) ? ((JsonNumber) value).toString() : null;
1042+
return (value != null && value.getValueType() == ValueType.NUMBER) ? value.toString() : null;
10011043
}
10021044

10031045
// Returns the specified JsonValue as a Boolean, or null if it's not a boolean
@@ -1022,6 +1064,25 @@ private static JsonObject getJsonObject(JsonObject object, String propName) {
10221064
return obj;
10231065
}
10241066

1067+
/**
1068+
* Get the channel names found.
1069+
*
1070+
* @return A set of the channel names found in the configuration file or empty set if none found.
1071+
*/
1072+
1073+
public Set<String> getChannelNames() {
1074+
Set<String> ret = Collections.EMPTY_SET;
1075+
1076+
JsonObject channels = getJsonObject(jsonConfig, "channels");
1077+
if (channels != null) {
1078+
final Set<String> channelNames = channels.keySet();
1079+
if (channelNames != null && !channelNames.isEmpty()) {
1080+
ret = new HashSet<>(channelNames);
1081+
}
1082+
}
1083+
return ret;
1084+
}
1085+
10251086
// Holds a network "node" (eg. Peer, Orderer, EventHub)
10261087
private class Node {
10271088

@@ -1167,7 +1228,6 @@ public String getMspId() {
11671228
return mspId;
11681229
}
11691230

1170-
11711231
public List<String> getPeerNames() {
11721232
return peerNames;
11731233
}
@@ -1186,7 +1246,6 @@ public UserInfo getPeerAdmin() {
11861246
return peerAdmin;
11871247
}
11881248

1189-
11901249
}
11911250

11921251
/**

0 commit comments

Comments
 (0)