Skip to content

Commit

Permalink
FAB-9565 Con profile and verify certs
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
cr22rc committed Apr 24, 2018
1 parent 202de7a commit f5f7043
Show file tree
Hide file tree
Showing 14 changed files with 566 additions and 198 deletions.
81 changes: 58 additions & 23 deletions src/main/java/org/hyperledger/fabric/sdk/Endpoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@
package org.hyperledger.fabric.sdk;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
Expand Down Expand Up @@ -97,19 +98,43 @@ class Endpoint {
} catch (Exception e) {
throw new RuntimeException(e);
}
if (properties.containsKey("pemFile") && properties.containsKey("pemBytes")) {
throw new RuntimeException("Properties \"pemBytes\" and \"pemFile\" can not be both set.");
}
if (properties.containsKey("pemFile")) {
Path path = Paths.get(properties.getProperty("pemFile"));
try {
pemBytes = Files.readAllBytes(path);
} catch (IOException e) {
throw new RuntimeException(e);

try (ByteArrayOutputStream bis = new ByteArrayOutputStream(64000)) {
byte[] pb = (byte[]) properties.get("pemBytes");
if (null != pb) {
bis.write(pb);
}
if (properties.containsKey("pemFile")) {

String pemFile = properties.getProperty("pemFile");

String[] pems = pemFile.split("[ \t]*,[ \t]*");

for (String pem : pems) {
if (null != pem && !pem.isEmpty()) {
try {
bis.write(Files.readAllBytes(Paths.get(pem)));
} catch (IOException e) {
throw new RuntimeException(format("Failed to read certificate file %s",
new File(pem).getAbsolutePath()), e);
}
}
}

}
pemBytes = bis.toByteArray();

if (pemBytes.length == 0) {
pemBytes = null;
}
} else if (properties.containsKey("pemBytes")) {
pemBytes = (byte[]) properties.get("pemBytes");
} catch (IOException e) {
throw new RuntimeException("Failed to read CA certificates file %s", e);
}

if (pemBytes == null) {
logger.warn(format("Endpoint %s is grpcs with no CA certificates", url));
}

if (null != pemBytes) {
try {
cn = properties.getProperty("hostnameOverride");
Expand Down Expand Up @@ -210,18 +235,20 @@ class Endpoint {
SslProvider sslprovider = sslp.equals("openSSL") ? SslProvider.OPENSSL : SslProvider.JDK;
NegotiationType ntype = nt.equals("TLS") ? NegotiationType.TLS : NegotiationType.PLAINTEXT;

InputStream myInputStream = new ByteArrayInputStream(pemBytes);
SslContextBuilder clientContextBuilder = GrpcSslContexts.configure(SslContextBuilder.forClient(), sslprovider);
if (clientKey != null && clientCert != null) {
clientContextBuilder = clientContextBuilder.keyManager(clientKey, clientCert);
SslContextBuilder clientContextBuilder = getSslContextBuilder(clientCert, clientKey, sslprovider);
SslContext sslContext;

try (InputStream myInputStream = new ByteArrayInputStream(pemBytes)) {
sslContext = clientContextBuilder
.trustManager(myInputStream)
.build();
}
SslContext sslContext = clientContextBuilder
.trustManager(myInputStream)
.build();
this.channelBuilder = NettyChannelBuilder
.forAddress(addr, port)
.sslContext(sslContext)
.negotiationType(ntype);

channelBuilder = NettyChannelBuilder
.forAddress(addr, port)
.sslContext(sslContext)
.negotiationType(ntype);

if (cn != null) {
channelBuilder.overrideAuthority(cn);
}
Expand All @@ -242,6 +269,14 @@ class Endpoint {
}
}

SslContextBuilder getSslContextBuilder(X509Certificate[] clientCert, PrivateKey clientKey, SslProvider sslprovider) {
SslContextBuilder clientContextBuilder = GrpcSslContexts.configure(SslContextBuilder.forClient(), sslprovider);
if (clientKey != null && clientCert != null) {
clientContextBuilder = clientContextBuilder.keyManager(clientKey, clientCert);
}
return clientContextBuilder;
}

byte[] getClientTLSCertificateDigest() {
//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

Expand Down
115 changes: 87 additions & 28 deletions src/main/java/org/hyperledger/fabric/sdk/NetworkConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
Expand All @@ -32,10 +33,10 @@
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonNumber;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import javax.json.JsonReader;
Expand Down Expand Up @@ -469,8 +470,26 @@ Channel loadChannel(HFClient client, String channelName) throws NetworkConfigura
throw new NetworkConfigurationException(format("Channel %s is already configured in the client!", channelName));
}
channel = reconstructChannel(client, channelName, jsonChannel);
} else {

final Set<String> channelNames = getChannelNames();
if (channelNames.isEmpty()) {
throw new NetworkConfigurationException("Channel configuration has no channels defined.");
}
final StringBuilder sb = new StringBuilder(1000);

channelNames.forEach(s -> {
if (sb.length() != 0) {
sb.append(", ");
}
sb.append(s);
});
throw new NetworkConfigurationException(format("Channel %s not found in configuration file. Found channel names: %s ", channelName, sb.toString()));

}

} else {
throw new NetworkConfigurationException("Channel configuration has no channels defined.");
}

return channel;
Expand Down Expand Up @@ -500,6 +519,9 @@ private void createAllOrderers() throws NetworkConfigurationException {
}

Node orderer = createNode(ordererName, jsonOrderer, "url");
if (orderer == null) {
throw new NetworkConfigurationException(format("Error loading config. Invalid orderer entry: %s", ordererName));
}
orderers.put(ordererName, orderer);
}
}
Expand Down Expand Up @@ -536,11 +558,16 @@ private void createAllPeers() throws NetworkConfigurationException {
}

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

// Also create an event hub with the same name as the peer
Node eventHub = createNode(peerName, jsonPeer, "eventUrl");
eventHubs.put(peerName, eventHub);
Node eventHub = createNode(peerName, jsonPeer, "eventUrl"); // may not be present
if (null != eventHub) {
eventHubs.put(peerName, eventHub);
}
}
}

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

channel.addPeer(peer, peerOptions);

foundPeer = true;

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

}

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

// Creates a new Node instance from a JSON object
private Node createNode(String nodeName, JsonObject jsonOrderer, String urlPropName) throws NetworkConfigurationException {
private Node createNode(String nodeName, JsonObject jsonNode, String urlPropName) throws NetworkConfigurationException {

// jsonNode.
// if (jsonNode.isNull(urlPropName)) {
// return null;
// }

String url = jsonOrderer.getString(urlPropName);
String url = jsonNode.getString(urlPropName, null);
if (url == null) {
return null;
}

Properties props = extractProperties(jsonNode, "grpcOptions");

if (null != props) {
String value = props.getProperty("grpc.keepalive_time_ms");
if (null != value) {
props.remove("grpc.keepalive_time_ms");
props.put("grpc.NettyChannelBuilderOption.keepAliveTime", new Object[] {new Long(value), TimeUnit.MILLISECONDS});
}

Properties props = extractProperties(jsonOrderer, "grpcOptions");
value = props.getProperty("grpc.keepalive_timeout_ms");
if (null != value) {
props.remove("grpc.keepalive_timeout_ms");
props.put("grpc.NettyChannelBuilderOption.keepAliveTimeout", new Object[] {new Long(value), TimeUnit.MILLISECONDS});
}
}

// Extract the pem details
getTLSCerts(nodeName, jsonOrderer, props);
getTLSCerts(nodeName, jsonNode, props);

return new Node(nodeName, url, props);
}

private void getTLSCerts(String nodeName, JsonObject jsonOrderer, Properties props) throws NetworkConfigurationException {
private void getTLSCerts(String nodeName, JsonObject jsonOrderer, Properties props) {
JsonObject jsonTlsCaCerts = getJsonObject(jsonOrderer, "tlsCACerts");
if (jsonTlsCaCerts != null) {
String pemFilename = getJsonValueAsString(jsonTlsCaCerts.get("path"));
String pemBytes = getJsonValueAsString(jsonTlsCaCerts.get("pem"));

if (pemFilename != null && pemBytes != null) {
throw new NetworkConfigurationException(format("Endpoint %s should not specify both tlsCACerts path and pem", nodeName));
}

if (pemFilename != null) {
// Determine full pathname and ensure the file exists
File pemFile = new File(pemFilename);
String fullPathname = pemFile.getAbsolutePath();
props.put("pemFile", fullPathname);
// let the sdk handle non existing errors could be they don't exist during parsing but are there later.
props.put("pemFile", pemFilename);
}

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

try {
privateKey = getPrivateKeyFromString(adminPrivateKeyString);
privateKey = getPrivateKeyFromString(adminPrivateKeyString);
} catch (IOException ioe) {
throw new NetworkConfigurationException(format("%s: Invalid private key", msgPrefix), ioe);
throw new NetworkConfigurationException(format("%s: Invalid private key", msgPrefix), ioe);
}

final PrivateKey privateKeyFinal = privateKey;
Expand All @@ -815,7 +858,6 @@ public String getCert() {
}
});


}

return org;
Expand Down Expand Up @@ -997,7 +1039,7 @@ private static String getJsonValueAsString(JsonValue value) {

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

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

/**
* Get the channel names found.
*
* @return A set of the channel names found in the configuration file or empty set if none found.
*/

public Set<String> getChannelNames() {
Set<String> ret = Collections.EMPTY_SET;

JsonObject channels = getJsonObject(jsonConfig, "channels");
if (channels != null) {
final Set<String> channelNames = channels.keySet();
if (channelNames != null && !channelNames.isEmpty()) {
ret = new HashSet<>(channelNames);
}
}
return ret;
}

// Holds a network "node" (eg. Peer, Orderer, EventHub)
private class Node {

Expand Down Expand Up @@ -1167,7 +1228,6 @@ public String getMspId() {
return mspId;
}


public List<String> getPeerNames() {
return peerNames;
}
Expand All @@ -1186,7 +1246,6 @@ public UserInfo getPeerAdmin() {
return peerAdmin;
}


}

/**
Expand Down
Loading

0 comments on commit f5f7043

Please sign in to comment.