From 09776f0610193982cdee6ec8e00d5244bb82e65d Mon Sep 17 00:00:00 2001 From: rickr Date: Mon, 5 Feb 2018 14:09:54 -0500 Subject: [PATCH] FAB-8077 Create client TLS from Fabric CA Change-Id: I68d811a517fb64c46cdb01f7f621277bb0d7627c Signed-off-by: rickr --- .../org/hyperledger/fabric/sdk/Endpoint.java | 15 ++++-- .../hyperledger/fabric/sdk/EndpointTest.java | 6 +-- .../fabric/sdk/testutils/TestConfig.java | 5 ++ .../sdkintegration/End2endAndBackAgainIT.java | 26 ++++++++-- .../fabric/sdkintegration/End2endIT.java | 51 +++++++++++++++++++ .../fabric/sdkintegration/SampleStore.java | 24 +++++++++ 6 files changed, 114 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/hyperledger/fabric/sdk/Endpoint.java b/src/main/java/org/hyperledger/fabric/sdk/Endpoint.java index 5b726bcc..1c9d7897 100644 --- a/src/main/java/org/hyperledger/fabric/sdk/Endpoint.java +++ b/src/main/java/org/hyperledger/fabric/sdk/Endpoint.java @@ -41,6 +41,7 @@ import io.grpc.netty.NettyChannelBuilder; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslProvider; +import org.apache.commons.codec.binary.Hex; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.bouncycastle.asn1.x500.RDN; @@ -48,12 +49,9 @@ import org.bouncycastle.asn1.x500.style.BCStyle; import org.bouncycastle.asn1.x500.style.IETFUtils; import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; -import org.bouncycastle.openssl.PEMKeyPair; -import org.bouncycastle.openssl.PEMParser; -import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; - import org.hyperledger.fabric.sdk.exception.CryptoException; import org.hyperledger.fabric.sdk.security.CryptoPrimitives; + import static org.hyperledger.fabric.sdk.helper.Utils.parseGrpcUrl; class Endpoint { @@ -146,12 +144,19 @@ class Endpoint { throw new RuntimeException("Properties \"clientKeyBytes\" and \"clientCertBytes\" must both be set or both be null"); } } + if ((ckb != null) && (ccb != null)) { + String what = "private key"; try { + logger.trace("client TLS private key bytes size:" + ckb.length); clientKey = cp.bytesToPrivateKey(ckb); + logger.trace("converted TLS key."); + what = "certificate"; + logger.trace("client TLS certificate bytes:" + Hex.encodeHexString(ccb)); clientCert = new X509Certificate[] {(X509Certificate) cp.bytesToCertificate(ccb)}; + logger.trace("converted client TLS certificate."); } catch (CryptoException e) { - throw new RuntimeException("Failed to parse TLS client key and/or certificate", e); + throw new RuntimeException("Failed to parse TLS client " + what, e); } } diff --git a/src/test/java/org/hyperledger/fabric/sdk/EndpointTest.java b/src/test/java/org/hyperledger/fabric/sdk/EndpointTest.java index 4bd8bd0d..64fc16ba 100644 --- a/src/test/java/org/hyperledger/fabric/sdk/EndpointTest.java +++ b/src/test/java/org/hyperledger/fabric/sdk/EndpointTest.java @@ -194,7 +194,7 @@ public void testNullPropertyClientCertBytes() { @Test public void testBadClientKeyFile() { thrown.expect(RuntimeException.class); - thrown.expectMessage("Failed to parse TLS client key and/or cert"); + thrown.expectMessage("Failed to parse TLS client private key"); Properties testprops = new Properties(); testprops.setProperty("trustServerCertificate", "true"); @@ -211,7 +211,7 @@ public void testBadClientKeyFile() { @Test public void testBadClientCertFile() { thrown.expect(RuntimeException.class); - thrown.expectMessage("Failed to parse TLS client key and/or cert"); + thrown.expectMessage("Failed to parse TLS client certificate"); Properties testprops = new Properties(); testprops.setProperty("trustServerCertificate", "true"); @@ -258,7 +258,7 @@ public void testClientTLSInvalidProperties() { try { new Endpoint("grpcs://localhost:594", testprops); } catch (RuntimeException e) { - Assert.assertEquals("Failed to parse TLS client key and/or certificate", e.getMessage()); + Assert.assertEquals("Failed to parse TLS client private key", e.getMessage()); } } diff --git a/src/test/java/org/hyperledger/fabric/sdk/testutils/TestConfig.java b/src/test/java/org/hyperledger/fabric/sdk/testutils/TestConfig.java index 0be2d72f..66a2f4fd 100644 --- a/src/test/java/org/hyperledger/fabric/sdk/testutils/TestConfig.java +++ b/src/test/java/org/hyperledger/fabric/sdk/testutils/TestConfig.java @@ -65,6 +65,11 @@ public class TestConfig { private static final Properties sdkProperties = new Properties(); private final boolean runningTLS; private final boolean runningFabricCATLS; + + public boolean isRunningFabricTLS() { + return runningFabricTLS; + } + private final boolean runningFabricTLS; private static final HashMap sampleOrgs = new HashMap<>(); diff --git a/src/test/java/org/hyperledger/fabric/sdkintegration/End2endAndBackAgainIT.java b/src/test/java/org/hyperledger/fabric/sdkintegration/End2endAndBackAgainIT.java index e83c4741..f0e89c95 100644 --- a/src/test/java/org/hyperledger/fabric/sdkintegration/End2endAndBackAgainIT.java +++ b/src/test/java/org/hyperledger/fabric/sdkintegration/End2endAndBackAgainIT.java @@ -582,11 +582,25 @@ private Channel reconstructChannel(String name, HFClient client, SampleOrg sampl } else { // foo channel do manual reconstruction. + Properties clientTLSProperties = new Properties(); + + final String clientPEMTLSCertificate = sampleStore.getClientPEMTLSCertificate(sampleOrg); + if (clientPEMTLSCertificate != null) { + clientTLSProperties.put("clientCertBytes", clientPEMTLSCertificate.getBytes(UTF_8)); + } + final String clientPEMTLSKey = sampleStore.getClientPEMTLSKey(sampleOrg); + + if (clientPEMTLSKey != null) { + clientTLSProperties.put("clientKeyBytes", clientPEMTLSKey.getBytes(UTF_8)); + } + newChannel = client.newChannel(name); for (String ordererName : sampleOrg.getOrdererNames()) { + Properties ordererProperties = (Properties) clientTLSProperties.clone(); + ordererProperties.putAll(testConfig.getOrdererProperties(ordererName)); newChannel.addOrderer(client.newOrderer(ordererName, sampleOrg.getOrdererLocation(ordererName), - testConfig.getOrdererProperties(ordererName))); + ordererProperties)); } boolean everyOther = false; @@ -594,6 +608,7 @@ private Channel reconstructChannel(String name, HFClient client, SampleOrg sampl for (String peerName : sampleOrg.getPeerNames()) { String peerLocation = sampleOrg.getPeerLocation(peerName); Properties peerProperties = testConfig.getPeerProperties(peerName); + peerProperties.putAll(clientTLSProperties); Peer peer = client.newPeer(peerName, peerLocation, peerProperties); final PeerOptions peerEventingOptions = // we have two peers on one use block on other use filtered everyOther ? @@ -613,8 +628,10 @@ private Channel reconstructChannel(String name, HFClient client, SampleOrg sampl //Should have two peers with all roles but event source. assertEquals(2, newChannel.getPeers(PeerRole.NO_EVENT_SOURCE).size()); for (String eventHubName : sampleOrg.getEventHubNames()) { + Properties eventhubProperties = (Properties) clientTLSProperties.clone(); + eventhubProperties.putAll(testConfig.getEventHubProperties(eventHubName)); EventHub eventHub = client.newEventHub(eventHubName, sampleOrg.getEventHubLocation(eventHubName), - testConfig.getEventHubProperties(eventHubName)); + eventhubProperties); newChannel.addEventHub(eventHub); } } else { @@ -710,13 +727,12 @@ private Channel reconstructChannel(String name, HFClient client, SampleOrg sampl return newChannel; } - /** - * - *This code test the replay feature of the new peer event services. + * This code test the replay feature of the new peer event services. * Instead of the default of starting the eventing peer to retrieve the newest block it sets it * retrieve starting from the start parameter. Also checks with block and filterblock replays. * Depends on end2end and end2endAndBackagain of have fully run to have the blocks need to work with. + * * @param client * @param replayTestChannel * @param start diff --git a/src/test/java/org/hyperledger/fabric/sdkintegration/End2endIT.java b/src/test/java/org/hyperledger/fabric/sdkintegration/End2endIT.java index ac0d5f87..ed8b38b3 100644 --- a/src/test/java/org/hyperledger/fabric/sdkintegration/End2endIT.java +++ b/src/test/java/org/hyperledger/fabric/sdkintegration/End2endIT.java @@ -16,8 +16,10 @@ import java.io.File; import java.io.IOException; +import java.io.StringWriter; import java.net.MalformedURLException; import java.nio.file.Paths; +import java.security.PrivateKey; import java.util.Arrays; import java.util.Collection; import java.util.EnumSet; @@ -31,6 +33,7 @@ import java.util.regex.Pattern; import org.apache.commons.codec.binary.Hex; +import org.bouncycastle.openssl.PEMWriter; import org.hyperledger.fabric.protos.ledger.rwset.kvrwset.KvRwset; import org.hyperledger.fabric.sdk.BlockEvent; import org.hyperledger.fabric.sdk.BlockInfo; @@ -40,6 +43,7 @@ import org.hyperledger.fabric.sdk.ChaincodeID; import org.hyperledger.fabric.sdk.Channel; import org.hyperledger.fabric.sdk.ChannelConfiguration; +import org.hyperledger.fabric.sdk.Enrollment; import org.hyperledger.fabric.sdk.EventHub; import org.hyperledger.fabric.sdk.HFClient; import org.hyperledger.fabric.sdk.InstallProposalRequest; @@ -60,6 +64,7 @@ import org.hyperledger.fabric.sdk.exception.TransactionEventException; import org.hyperledger.fabric.sdk.security.CryptoSuite; import org.hyperledger.fabric.sdk.testutils.TestConfig; +import org.hyperledger.fabric_ca.sdk.EnrollmentRequest; import org.hyperledger.fabric_ca.sdk.HFCAClient; import org.hyperledger.fabric_ca.sdk.HFCAInfo; import org.hyperledger.fabric_ca.sdk.RegistrationRequest; @@ -156,6 +161,8 @@ public void checkConfig() throws NoSuchFieldException, SecurityException, Illega } } + Map clientTLSProperties = new HashMap<>(); + @Test public void setup() { @@ -197,6 +204,26 @@ public void setup() { final String mspid = sampleOrg.getMSPID(); ca.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite()); + if (testConfig.isRunningFabricTLS()) { + //This shows how to get a client TLS certificate from Fabric CA + // we will use one client TLS certificate for orderer peers etc. + final EnrollmentRequest enrollmentRequestTLS = new EnrollmentRequest(); + enrollmentRequestTLS.addHost("localhost"); + enrollmentRequestTLS.setProfile("tls"); + final Enrollment enroll = ca.enroll("admin", "adminpw", 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)); + clientTLSProperties.put(sampleOrg.getName(), tlsProperties); + //Save in samplestore for follow on tests. + sampleStore.storeClientPEMTLCertificate(sampleOrg, tlsCertPEM); + sampleStore.storeClientPEMTLSKey(sampleOrg, tlsKeyPEM); + } + HFCAInfo info = ca.info(); //just check if we connect at all. assertNotNull(info); String infoName = info.getCAName(); @@ -276,6 +303,17 @@ sampleOrgDomainName, format("/users/Admin@%s/msp/keystore", sampleOrgDomainName) } + static String getPEMStringFromPrivateKey(PrivateKey privateKey) throws IOException { + StringWriter pemStrWriter = new StringWriter(); + PEMWriter pemWriter = new PEMWriter(pemStrWriter); + + pemWriter.writeObject(privateKey); + + pemWriter.close(); + + return pemStrWriter.toString(); + } + //CHECKSTYLE.OFF: Method length is 320 lines (max allowed is 150). void runChannel(HFClient client, Channel channel, boolean installChaincode, SampleOrg sampleOrg, int delta) { @@ -703,6 +741,10 @@ private Channel constructChannel(String name, HFClient client, SampleOrg sampleO ordererProperties.put("grpc.NettyChannelBuilderOption.keepAliveTimeout", new Object[] {8L, TimeUnit.SECONDS}); ordererProperties.put("grpc.NettyChannelBuilderOption.keepAliveWithoutCalls", new Object[] {true}); + if (!clientTLSProperties.isEmpty()) { + ordererProperties.putAll(clientTLSProperties.get(sampleOrg.getName())); + } + orderers.add(client.newOrderer(orderName, sampleOrg.getOrdererLocation(orderName), ordererProperties)); } @@ -727,6 +769,11 @@ private Channel constructChannel(String name, HFClient client, SampleOrg sampleO if (peerProperties == null) { peerProperties = new Properties(); } + + if (!clientTLSProperties.isEmpty()) { + peerProperties.putAll(clientTLSProperties.get(sampleOrg.getName())); + } + //Example of setting specific options on grpc's NettyChannelBuilder peerProperties.put("grpc.NettyChannelBuilderOption.maxInboundMessageSize", 9000000); @@ -758,6 +805,10 @@ private Channel constructChannel(String name, HFClient client, SampleOrg sampleO eventHubProperties.put("grpc.NettyChannelBuilderOption.keepAliveTime", new Object[] {5L, TimeUnit.MINUTES}); eventHubProperties.put("grpc.NettyChannelBuilderOption.keepAliveTimeout", new Object[] {8L, TimeUnit.SECONDS}); + if (!clientTLSProperties.isEmpty()) { + eventHubProperties.putAll(clientTLSProperties.get(sampleOrg.getName())); + } + EventHub eventHub = client.newEventHub(eventHubName, sampleOrg.getEventHubLocation(eventHubName), eventHubProperties); newChannel.addEventHub(eventHub); diff --git a/src/test/java/org/hyperledger/fabric/sdkintegration/SampleStore.java b/src/test/java/org/hyperledger/fabric/sdkintegration/SampleStore.java index fcf9395c..618694cd 100644 --- a/src/test/java/org/hyperledger/fabric/sdkintegration/SampleStore.java +++ b/src/test/java/org/hyperledger/fabric/sdkintegration/SampleStore.java @@ -276,4 +276,28 @@ Channel getChannel(HFClient client, String name) throws IOException, ClassNotFou return ret; } + public void storeClientPEMTLSKey(SampleOrg sampleOrg, String key) { + + setValue("clientPEMTLSKey." + sampleOrg.getName(), key); + + } + + public String getClientPEMTLSKey(SampleOrg sampleOrg) { + + return getValue("clientPEMTLSKey." + sampleOrg.getName()); + + } + + public void storeClientPEMTLCertificate(SampleOrg sampleOrg, String certificate) { + + setValue("clientPEMTLSCertificate." + sampleOrg.getName(), certificate); + + } + + public String getClientPEMTLSCertificate(SampleOrg sampleOrg) { + + return getValue("clientPEMTLSCertificate." + sampleOrg.getName()); + + } + } \ No newline at end of file