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

Commit 09776f0

Browse files
committed
FAB-8077 Create client TLS from Fabric CA
Change-Id: I68d811a517fb64c46cdb01f7f621277bb0d7627c Signed-off-by: rickr <cr22rc@gmail.com>
1 parent 5c57df4 commit 09776f0

File tree

6 files changed

+114
-13
lines changed

6 files changed

+114
-13
lines changed

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,19 +41,17 @@
4141
import io.grpc.netty.NettyChannelBuilder;
4242
import io.netty.handler.ssl.SslContext;
4343
import io.netty.handler.ssl.SslProvider;
44+
import org.apache.commons.codec.binary.Hex;
4445
import org.apache.commons.logging.Log;
4546
import org.apache.commons.logging.LogFactory;
4647
import org.bouncycastle.asn1.x500.RDN;
4748
import org.bouncycastle.asn1.x500.X500Name;
4849
import org.bouncycastle.asn1.x500.style.BCStyle;
4950
import org.bouncycastle.asn1.x500.style.IETFUtils;
5051
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
51-
import org.bouncycastle.openssl.PEMKeyPair;
52-
import org.bouncycastle.openssl.PEMParser;
53-
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
54-
5552
import org.hyperledger.fabric.sdk.exception.CryptoException;
5653
import org.hyperledger.fabric.sdk.security.CryptoPrimitives;
54+
5755
import static org.hyperledger.fabric.sdk.helper.Utils.parseGrpcUrl;
5856

5957
class Endpoint {
@@ -146,12 +144,19 @@ class Endpoint {
146144
throw new RuntimeException("Properties \"clientKeyBytes\" and \"clientCertBytes\" must both be set or both be null");
147145
}
148146
}
147+
149148
if ((ckb != null) && (ccb != null)) {
149+
String what = "private key";
150150
try {
151+
logger.trace("client TLS private key bytes size:" + ckb.length);
151152
clientKey = cp.bytesToPrivateKey(ckb);
153+
logger.trace("converted TLS key.");
154+
what = "certificate";
155+
logger.trace("client TLS certificate bytes:" + Hex.encodeHexString(ccb));
152156
clientCert = new X509Certificate[] {(X509Certificate) cp.bytesToCertificate(ccb)};
157+
logger.trace("converted client TLS certificate.");
153158
} catch (CryptoException e) {
154-
throw new RuntimeException("Failed to parse TLS client key and/or certificate", e);
159+
throw new RuntimeException("Failed to parse TLS client " + what, e);
155160
}
156161
}
157162

src/test/java/org/hyperledger/fabric/sdk/EndpointTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ public void testNullPropertyClientCertBytes() {
194194
@Test
195195
public void testBadClientKeyFile() {
196196
thrown.expect(RuntimeException.class);
197-
thrown.expectMessage("Failed to parse TLS client key and/or cert");
197+
thrown.expectMessage("Failed to parse TLS client private key");
198198

199199
Properties testprops = new Properties();
200200
testprops.setProperty("trustServerCertificate", "true");
@@ -211,7 +211,7 @@ public void testBadClientKeyFile() {
211211
@Test
212212
public void testBadClientCertFile() {
213213
thrown.expect(RuntimeException.class);
214-
thrown.expectMessage("Failed to parse TLS client key and/or cert");
214+
thrown.expectMessage("Failed to parse TLS client certificate");
215215

216216
Properties testprops = new Properties();
217217
testprops.setProperty("trustServerCertificate", "true");
@@ -258,7 +258,7 @@ public void testClientTLSInvalidProperties() {
258258
try {
259259
new Endpoint("grpcs://localhost:594", testprops);
260260
} catch (RuntimeException e) {
261-
Assert.assertEquals("Failed to parse TLS client key and/or certificate", e.getMessage());
261+
Assert.assertEquals("Failed to parse TLS client private key", e.getMessage());
262262
}
263263
}
264264

src/test/java/org/hyperledger/fabric/sdk/testutils/TestConfig.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ public class TestConfig {
6565
private static final Properties sdkProperties = new Properties();
6666
private final boolean runningTLS;
6767
private final boolean runningFabricCATLS;
68+
69+
public boolean isRunningFabricTLS() {
70+
return runningFabricTLS;
71+
}
72+
6873
private final boolean runningFabricTLS;
6974
private static final HashMap<String, SampleOrg> sampleOrgs = new HashMap<>();
7075

src/test/java/org/hyperledger/fabric/sdkintegration/End2endAndBackAgainIT.java

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -582,18 +582,33 @@ private Channel reconstructChannel(String name, HFClient client, SampleOrg sampl
582582
} else {
583583
// foo channel do manual reconstruction.
584584

585+
Properties clientTLSProperties = new Properties();
586+
587+
final String clientPEMTLSCertificate = sampleStore.getClientPEMTLSCertificate(sampleOrg);
588+
if (clientPEMTLSCertificate != null) {
589+
clientTLSProperties.put("clientCertBytes", clientPEMTLSCertificate.getBytes(UTF_8));
590+
}
591+
final String clientPEMTLSKey = sampleStore.getClientPEMTLSKey(sampleOrg);
592+
593+
if (clientPEMTLSKey != null) {
594+
clientTLSProperties.put("clientKeyBytes", clientPEMTLSKey.getBytes(UTF_8));
595+
}
596+
585597
newChannel = client.newChannel(name);
586598

587599
for (String ordererName : sampleOrg.getOrdererNames()) {
600+
Properties ordererProperties = (Properties) clientTLSProperties.clone();
601+
ordererProperties.putAll(testConfig.getOrdererProperties(ordererName));
588602
newChannel.addOrderer(client.newOrderer(ordererName, sampleOrg.getOrdererLocation(ordererName),
589-
testConfig.getOrdererProperties(ordererName)));
603+
ordererProperties));
590604
}
591605

592606
boolean everyOther = false;
593607

594608
for (String peerName : sampleOrg.getPeerNames()) {
595609
String peerLocation = sampleOrg.getPeerLocation(peerName);
596610
Properties peerProperties = testConfig.getPeerProperties(peerName);
611+
peerProperties.putAll(clientTLSProperties);
597612
Peer peer = client.newPeer(peerName, peerLocation, peerProperties);
598613
final PeerOptions peerEventingOptions = // we have two peers on one use block on other use filtered
599614
everyOther ?
@@ -613,8 +628,10 @@ private Channel reconstructChannel(String name, HFClient client, SampleOrg sampl
613628
//Should have two peers with all roles but event source.
614629
assertEquals(2, newChannel.getPeers(PeerRole.NO_EVENT_SOURCE).size());
615630
for (String eventHubName : sampleOrg.getEventHubNames()) {
631+
Properties eventhubProperties = (Properties) clientTLSProperties.clone();
632+
eventhubProperties.putAll(testConfig.getEventHubProperties(eventHubName));
616633
EventHub eventHub = client.newEventHub(eventHubName, sampleOrg.getEventHubLocation(eventHubName),
617-
testConfig.getEventHubProperties(eventHubName));
634+
eventhubProperties);
618635
newChannel.addEventHub(eventHub);
619636
}
620637
} else {
@@ -710,13 +727,12 @@ private Channel reconstructChannel(String name, HFClient client, SampleOrg sampl
710727
return newChannel;
711728
}
712729

713-
714730
/**
715-
*
716-
*This code test the replay feature of the new peer event services.
731+
* This code test the replay feature of the new peer event services.
717732
* Instead of the default of starting the eventing peer to retrieve the newest block it sets it
718733
* retrieve starting from the start parameter. Also checks with block and filterblock replays.
719734
* Depends on end2end and end2endAndBackagain of have fully run to have the blocks need to work with.
735+
*
720736
* @param client
721737
* @param replayTestChannel
722738
* @param start

src/test/java/org/hyperledger/fabric/sdkintegration/End2endIT.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616

1717
import java.io.File;
1818
import java.io.IOException;
19+
import java.io.StringWriter;
1920
import java.net.MalformedURLException;
2021
import java.nio.file.Paths;
22+
import java.security.PrivateKey;
2123
import java.util.Arrays;
2224
import java.util.Collection;
2325
import java.util.EnumSet;
@@ -31,6 +33,7 @@
3133
import java.util.regex.Pattern;
3234

3335
import org.apache.commons.codec.binary.Hex;
36+
import org.bouncycastle.openssl.PEMWriter;
3437
import org.hyperledger.fabric.protos.ledger.rwset.kvrwset.KvRwset;
3538
import org.hyperledger.fabric.sdk.BlockEvent;
3639
import org.hyperledger.fabric.sdk.BlockInfo;
@@ -40,6 +43,7 @@
4043
import org.hyperledger.fabric.sdk.ChaincodeID;
4144
import org.hyperledger.fabric.sdk.Channel;
4245
import org.hyperledger.fabric.sdk.ChannelConfiguration;
46+
import org.hyperledger.fabric.sdk.Enrollment;
4347
import org.hyperledger.fabric.sdk.EventHub;
4448
import org.hyperledger.fabric.sdk.HFClient;
4549
import org.hyperledger.fabric.sdk.InstallProposalRequest;
@@ -60,6 +64,7 @@
6064
import org.hyperledger.fabric.sdk.exception.TransactionEventException;
6165
import org.hyperledger.fabric.sdk.security.CryptoSuite;
6266
import org.hyperledger.fabric.sdk.testutils.TestConfig;
67+
import org.hyperledger.fabric_ca.sdk.EnrollmentRequest;
6368
import org.hyperledger.fabric_ca.sdk.HFCAClient;
6469
import org.hyperledger.fabric_ca.sdk.HFCAInfo;
6570
import org.hyperledger.fabric_ca.sdk.RegistrationRequest;
@@ -156,6 +161,8 @@ public void checkConfig() throws NoSuchFieldException, SecurityException, Illega
156161
}
157162
}
158163

164+
Map<String, Properties> clientTLSProperties = new HashMap<>();
165+
159166
@Test
160167
public void setup() {
161168

@@ -197,6 +204,26 @@ public void setup() {
197204
final String mspid = sampleOrg.getMSPID();
198205
ca.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite());
199206

207+
if (testConfig.isRunningFabricTLS()) {
208+
//This shows how to get a client TLS certificate from Fabric CA
209+
// we will use one client TLS certificate for orderer peers etc.
210+
final EnrollmentRequest enrollmentRequestTLS = new EnrollmentRequest();
211+
enrollmentRequestTLS.addHost("localhost");
212+
enrollmentRequestTLS.setProfile("tls");
213+
final Enrollment enroll = ca.enroll("admin", "adminpw", enrollmentRequestTLS);
214+
final String tlsCertPEM = enroll.getCert();
215+
final String tlsKeyPEM = getPEMStringFromPrivateKey(enroll.getKey());
216+
217+
final Properties tlsProperties = new Properties();
218+
219+
tlsProperties.put("clientKeyBytes", tlsKeyPEM.getBytes(UTF_8));
220+
tlsProperties.put("clientCertBytes", tlsCertPEM.getBytes(UTF_8));
221+
clientTLSProperties.put(sampleOrg.getName(), tlsProperties);
222+
//Save in samplestore for follow on tests.
223+
sampleStore.storeClientPEMTLCertificate(sampleOrg, tlsCertPEM);
224+
sampleStore.storeClientPEMTLSKey(sampleOrg, tlsKeyPEM);
225+
}
226+
200227
HFCAInfo info = ca.info(); //just check if we connect at all.
201228
assertNotNull(info);
202229
String infoName = info.getCAName();
@@ -276,6 +303,17 @@ sampleOrgDomainName, format("/users/Admin@%s/msp/keystore", sampleOrgDomainName)
276303

277304
}
278305

306+
static String getPEMStringFromPrivateKey(PrivateKey privateKey) throws IOException {
307+
StringWriter pemStrWriter = new StringWriter();
308+
PEMWriter pemWriter = new PEMWriter(pemStrWriter);
309+
310+
pemWriter.writeObject(privateKey);
311+
312+
pemWriter.close();
313+
314+
return pemStrWriter.toString();
315+
}
316+
279317
//CHECKSTYLE.OFF: Method length is 320 lines (max allowed is 150).
280318
void runChannel(HFClient client, Channel channel, boolean installChaincode, SampleOrg sampleOrg, int delta) {
281319

@@ -703,6 +741,10 @@ private Channel constructChannel(String name, HFClient client, SampleOrg sampleO
703741
ordererProperties.put("grpc.NettyChannelBuilderOption.keepAliveTimeout", new Object[] {8L, TimeUnit.SECONDS});
704742
ordererProperties.put("grpc.NettyChannelBuilderOption.keepAliveWithoutCalls", new Object[] {true});
705743

744+
if (!clientTLSProperties.isEmpty()) {
745+
ordererProperties.putAll(clientTLSProperties.get(sampleOrg.getName()));
746+
}
747+
706748
orderers.add(client.newOrderer(orderName, sampleOrg.getOrdererLocation(orderName),
707749
ordererProperties));
708750
}
@@ -727,6 +769,11 @@ private Channel constructChannel(String name, HFClient client, SampleOrg sampleO
727769
if (peerProperties == null) {
728770
peerProperties = new Properties();
729771
}
772+
773+
if (!clientTLSProperties.isEmpty()) {
774+
peerProperties.putAll(clientTLSProperties.get(sampleOrg.getName()));
775+
}
776+
730777
//Example of setting specific options on grpc's NettyChannelBuilder
731778
peerProperties.put("grpc.NettyChannelBuilderOption.maxInboundMessageSize", 9000000);
732779

@@ -758,6 +805,10 @@ private Channel constructChannel(String name, HFClient client, SampleOrg sampleO
758805
eventHubProperties.put("grpc.NettyChannelBuilderOption.keepAliveTime", new Object[] {5L, TimeUnit.MINUTES});
759806
eventHubProperties.put("grpc.NettyChannelBuilderOption.keepAliveTimeout", new Object[] {8L, TimeUnit.SECONDS});
760807

808+
if (!clientTLSProperties.isEmpty()) {
809+
eventHubProperties.putAll(clientTLSProperties.get(sampleOrg.getName()));
810+
}
811+
761812
EventHub eventHub = client.newEventHub(eventHubName, sampleOrg.getEventHubLocation(eventHubName),
762813
eventHubProperties);
763814
newChannel.addEventHub(eventHub);

src/test/java/org/hyperledger/fabric/sdkintegration/SampleStore.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,4 +276,28 @@ Channel getChannel(HFClient client, String name) throws IOException, ClassNotFou
276276
return ret;
277277
}
278278

279+
public void storeClientPEMTLSKey(SampleOrg sampleOrg, String key) {
280+
281+
setValue("clientPEMTLSKey." + sampleOrg.getName(), key);
282+
283+
}
284+
285+
public String getClientPEMTLSKey(SampleOrg sampleOrg) {
286+
287+
return getValue("clientPEMTLSKey." + sampleOrg.getName());
288+
289+
}
290+
291+
public void storeClientPEMTLCertificate(SampleOrg sampleOrg, String certificate) {
292+
293+
setValue("clientPEMTLSCertificate." + sampleOrg.getName(), certificate);
294+
295+
}
296+
297+
public String getClientPEMTLSCertificate(SampleOrg sampleOrg) {
298+
299+
return getValue("clientPEMTLSCertificate." + sampleOrg.getName());
300+
301+
}
302+
279303
}

0 commit comments

Comments
 (0)