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

Commit 96ee2df

Browse files
committed
FAB-7949 tlsbinding_event_reg
Change-Id: I4f81f79495d2f53b38fd45f75219e27d312adb51 Signed-off-by: rickr <cr22rc@gmail.com>
1 parent 09776f0 commit 96ee2df

File tree

9 files changed

+106
-36
lines changed

9 files changed

+106
-36
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ private void sendUpdateChannel(byte[] configupdate, byte[][] signers, Orderer or
459459
final ByteString sigHeaderByteString = getSignatureHeaderAsByteString(transactionContext);
460460

461461
final ChannelHeader payloadChannelHeader = ProtoUtils.createChannelHeader(HeaderType.CONFIG_UPDATE,
462-
transactionContext.getTxID(), name, transactionContext.getEpoch(), transactionContext.getFabricTimestamp(), null);
462+
transactionContext.getTxID(), name, transactionContext.getEpoch(), transactionContext.getFabricTimestamp(), null, null);
463463

464464
final Header payloadHeader = Header.newBuilder().setChannelHeader(payloadChannelHeader.toByteString())
465465
.setSignatureHeader(sigHeaderByteString).build();
@@ -1357,7 +1357,7 @@ private int seekBlock(SeekInfo seekInfo, List<DeliverResponse> deliverResponses,
13571357

13581358
TransactionContext txContext = getTransactionContext();
13591359

1360-
DeliverResponse[] deliver = orderer.sendDeliver(createSeekInfoEnvelope(txContext, seekInfo));
1360+
DeliverResponse[] deliver = orderer.sendDeliver(createSeekInfoEnvelope(txContext, seekInfo, null));
13611361

13621362
if (deliver.length < 1) {
13631363
logger.warn(format("Genesis block for channel %s fetch bad deliver missing status block only got blocks:%d", name, deliver.length));

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

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@
1919
import java.io.InputStream;
2020
import java.lang.reflect.InvocationTargetException;
2121
import java.lang.reflect.Method;
22-
import java.nio.charset.StandardCharsets;
2322
import java.nio.file.Files;
2423
import java.nio.file.Path;
2524
import java.nio.file.Paths;
2625
import java.security.PrivateKey;
2726
import java.security.cert.X509Certificate;
27+
import java.util.Base64;
2828
import java.util.Collections;
2929
import java.util.HashMap;
3030
import java.util.Map;
@@ -49,9 +49,12 @@
4949
import org.bouncycastle.asn1.x500.style.BCStyle;
5050
import org.bouncycastle.asn1.x500.style.IETFUtils;
5151
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
52+
import org.bouncycastle.crypto.Digest;
53+
import org.bouncycastle.crypto.digests.SHA256Digest;
5254
import org.hyperledger.fabric.sdk.exception.CryptoException;
5355
import org.hyperledger.fabric.sdk.security.CryptoPrimitives;
5456

57+
import static java.nio.charset.StandardCharsets.UTF_8;
5558
import static org.hyperledger.fabric.sdk.helper.Utils.parseGrpcUrl;
5659

5760
class Endpoint {
@@ -60,6 +63,8 @@ class Endpoint {
6063
private final String addr;
6164
private final int port;
6265
private final String url;
66+
private byte[] clientTLSCertificateDigest;
67+
private byte[] tlsClientCertificatePEMBytes;
6368
private NettyChannelBuilder channelBuilder = null;
6469

6570
private static final Map<String, String> CN_CACHE = Collections.synchronizedMap(new HashMap<>());
@@ -103,7 +108,7 @@ class Endpoint {
103108
try {
104109
cn = properties.getProperty("hostnameOverride");
105110
if (cn == null && "true".equals(properties.getProperty("trustServerCertificate"))) {
106-
final String cnKey = new String(pemBytes, StandardCharsets.UTF_8);
111+
final String cnKey = new String(pemBytes, UTF_8);
107112
cn = CN_CACHE.get(cnKey);
108113
if (cn == null) {
109114
X500Name x500name = new JcaX509CertificateHolder(
@@ -155,6 +160,7 @@ class Endpoint {
155160
logger.trace("client TLS certificate bytes:" + Hex.encodeHexString(ccb));
156161
clientCert = new X509Certificate[] {(X509Certificate) cp.bytesToCertificate(ccb)};
157162
logger.trace("converted client TLS certificate.");
163+
tlsClientCertificatePEMBytes = ccb; // Save this away it's the exact pem we used.
158164
} catch (CryptoException e) {
159165
throw new RuntimeException("Failed to parse TLS client " + what, e);
160166
}
@@ -218,6 +224,25 @@ class Endpoint {
218224
}
219225
}
220226

227+
byte[] getClientTLSCertificateDigest() {
228+
//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
229+
230+
if (tlsClientCertificatePEMBytes != null && clientTLSCertificateDigest == null) {
231+
232+
String pemCert = new String(tlsClientCertificatePEMBytes, UTF_8);
233+
byte[] derBytes = Base64.getDecoder().decode(
234+
pemCert.replaceAll("-+[ \t]*(BEGIN|END)[ \t]+CERTIFICATE[ \t]*-+", "").replaceAll("\\s", "").trim()
235+
);
236+
237+
Digest digest = new SHA256Digest();
238+
clientTLSCertificateDigest = new byte[digest.getDigestSize()];
239+
digest.update(derBytes, 0, derBytes.length);
240+
digest.doFinal(clientTLSCertificateDigest, 0);
241+
}
242+
243+
return clientTLSCertificateDigest;
244+
}
245+
221246
private static final Pattern METHOD_PATTERN = Pattern.compile("grpc\\.NettyChannelBuilderOption\\.([^.]*)$");
222247
private static final Map<Class<?>, Class<?>> WRAPPERS_TO_PRIM = new ImmutableMap.Builder<Class<?>, Class<?>>()
223248
.put(Boolean.class, boolean.class).put(Byte.class, byte.class).put(Character.class, char.class)

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

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import java.util.concurrent.ExecutorService;
2222
import java.util.concurrent.TimeUnit;
2323

24+
import javax.xml.bind.DatatypeConverter;
25+
2426
import com.google.protobuf.ByteString;
2527
import com.google.protobuf.InvalidProtocolBufferException;
2628
import io.grpc.ManagedChannel;
@@ -71,6 +73,7 @@ public class EventHub implements Serializable {
7173
private transient boolean shutdown = false;
7274
private Channel channel;
7375
private transient TransactionContext transactionContext;
76+
private transient byte[] clientTLSCertificateDigest;
7477

7578
/**
7679
* Get disconnected time.
@@ -189,7 +192,10 @@ synchronized boolean connect(final TransactionContext transactionContext) throws
189192

190193
lastConnectedAttempt = System.currentTimeMillis();
191194

192-
managedChannel = new Endpoint(url, properties).getChannelBuilder().build();
195+
Endpoint endpoint = new Endpoint(url, properties);
196+
managedChannel = endpoint.getChannelBuilder().build();
197+
198+
clientTLSCertificateDigest = endpoint.getClientTLSCertificateDigest();
193199

194200
events = EventsGrpc.newStub(managedChannel);
195201

@@ -321,10 +327,18 @@ private void blockListen(TransactionContext transactionContext) throws CryptoExc
321327

322328
PeerEvents.Register register = PeerEvents.Register.newBuilder()
323329
.addEvents(PeerEvents.Interest.newBuilder().setEventType(PeerEvents.EventType.BLOCK).build()).build();
324-
ByteString blockEventByteString = PeerEvents.Event.newBuilder().setRegister(register)
330+
PeerEvents.Event.Builder blockEventBuilder = PeerEvents.Event.newBuilder().setRegister(register)
325331
.setCreator(transactionContext.getIdentity().toByteString())
326-
.setTimestamp(ProtoUtils.getCurrentFabricTimestamp())
327-
.build().toByteString();
332+
.setTimestamp(ProtoUtils.getCurrentFabricTimestamp());
333+
334+
if (null != clientTLSCertificateDigest) {
335+
logger.trace("Setting clientTLSCertificate digest for event registration to " + DatatypeConverter.printHexBinary(clientTLSCertificateDigest));
336+
blockEventBuilder.setTlsCertHash(ByteString.copyFrom(clientTLSCertificateDigest));
337+
338+
}
339+
340+
ByteString blockEventByteString = blockEventBuilder.build().toByteString();
341+
328342
PeerEvents.SignedEvent signedBlockEvent = PeerEvents.SignedEvent.newBuilder()
329343
.setEventBytes(blockEventByteString)
330344
.setSignature(transactionContext.signByteString(blockEventByteString.toByteArray()))

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,20 +391,24 @@ public QueryByChaincodeRequest newQueryProposalRequest() {
391391
* Set the User context for this client.
392392
*
393393
* @param userContext
394+
* @return the old user context. Maybe null if never set!
394395
* @throws InvalidArgumentException
395396
*/
396397

397-
public void setUserContext(User userContext) throws InvalidArgumentException {
398+
public User setUserContext(User userContext) throws InvalidArgumentException {
398399

399400
if (null == cryptoSuite) {
400401
throw new InvalidArgumentException("No cryptoSuite has been set.");
401402
}
402403
userContextCheck(userContext);
404+
405+
User ret = this.userContext;
403406
this.userContext = userContext;
404407

405408
logger.debug(
406409
format("Setting user context to MSPID: %s user: %s", userContext.getMspId(), userContext.getName()));
407410

411+
return ret;
408412
}
409413

410414
/**

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ void initiateEventing(TransactionContext transactionContext, PeerOptions peersOp
108108

109109
//PeerEventServiceClient(Peer peer, ManagedChannelBuilder<?> channelBuilder, Properties properties)
110110
// peerEventingClient = new PeerEventServiceClient(this, new HashSet<Channel>(Arrays.asList(new Channel[] {channel})));
111-
peerEventingClient = new PeerEventServiceClient(this, new Endpoint(url, properties).getChannelBuilder(), properties, peersOptions);
111+
112+
peerEventingClient = new PeerEventServiceClient(this, new Endpoint(url, properties), properties, peersOptions);
112113

113114
peerEventingClient.connect(transactionContext);
114115

@@ -303,7 +304,8 @@ void reconnectPeerEventServiceClient(final PeerEventServiceClient failedPeerEven
303304

304305
logger.debug(t);
305306

306-
PeerEventServiceClient lpeerEventingClient = new PeerEventServiceClient(this, new Endpoint(url, properties).getChannelBuilder(), properties, null);
307+
PeerEventServiceClient lpeerEventingClient = new PeerEventServiceClient(this,
308+
new Endpoint(url, properties), properties, failedPeerEventServiceClient.getPeerOptions());
307309

308310
try {
309311
lpeerEventingClient.connect(fltransactionContext);

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

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,10 @@ class PeerEventServiceClient {
5858
private final String name;
5959
private final String url;
6060
private final long peerEventRegistrationWaitTimeMilliSecs;
61+
6162
private final PeerOptions peerOptions;
6263
private final boolean filterBlock;
64+
private byte[] clientTLSCertificateDigest;
6365
Properties properties = new Properties();
6466
StreamObserver<Envelope> nso = null;
6567
StreamObserver<DeliverResponse> so = null;
@@ -72,15 +74,16 @@ class PeerEventServiceClient {
7274
/**
7375
* Construct client for accessing Peer eventing service using the existing managedChannel.
7476
*/
75-
PeerEventServiceClient(Peer peer, ManagedChannelBuilder<?> channelBuilder, Properties properties, PeerOptions peerOptions) {
77+
PeerEventServiceClient(Peer peer, Endpoint endpoint, Properties properties, PeerOptions peerOptions) {
7678

77-
this.channelBuilder = channelBuilder;
79+
this.channelBuilder = endpoint.getChannelBuilder();
7880
this.filterBlock = peerOptions.isRegisterEventsForFilteredBlocks();
7981
this.peer = peer;
8082
name = peer.getName();
8183
url = peer.getUrl();
8284
channelName = peer.getChannel().getName();
8385
this.peerOptions = peerOptions;
86+
clientTLSCertificateDigest = endpoint.getClientTLSCertificateDigest();
8487

8588
this.channelEventQue = peer.getChannel().getChannelEventQue();
8689

@@ -106,6 +109,10 @@ class PeerEventServiceClient {
106109

107110
}
108111

112+
PeerOptions getPeerOptions() {
113+
return peerOptions.clone();
114+
}
115+
109116
synchronized void shutdown(boolean force) {
110117

111118
if (shutdown) {
@@ -322,7 +329,7 @@ void connect(TransactionContext transactionContext) throws TransactionException
322329
// Peer eventing
323330
void peerVent(TransactionContext transactionContext) throws TransactionException {
324331

325-
final Envelope latestBlock;
332+
final Envelope envelope;
326333
try {
327334

328335
Ab.SeekPosition.Builder start = Ab.SeekPosition.newBuilder();
@@ -334,16 +341,17 @@ void peerVent(TransactionContext transactionContext) throws TransactionException
334341
start.setNewest(Ab.SeekNewest.getDefaultInstance());
335342
}
336343

337-
latestBlock = createSeekInfoEnvelope(transactionContext,
344+
// properties.
345+
346+
envelope = createSeekInfoEnvelope(transactionContext,
338347
start.build(),
339348
Ab.SeekPosition.newBuilder()
340349
.setSpecified(Ab.SeekSpecified.newBuilder().setNumber(peerOptions.getStopEvents()).build())
341-
// .setSpecified(Ab.SeekSpecified.newBuilder().setNumber(1L).build())
342350
.build(),
343-
SeekInfo.SeekBehavior.BLOCK_UNTIL_READY
351+
SeekInfo.SeekBehavior.BLOCK_UNTIL_READY,
344352

345-
);
346-
DeliverResponse[] deliver = connectEnvelope(latestBlock);
353+
clientTLSCertificateDigest);
354+
connectEnvelope(envelope);
347355
} catch (CryptoException e) {
348356
throw new TransactionException(e);
349357
}

src/main/java/org/hyperledger/fabric/sdk/transaction/ProposalBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ private FabricProposal.Proposal createFabricProposal(String channelID, Chaincode
116116
.setChaincodeId(chaincodeID).build();
117117

118118
Common.ChannelHeader chainHeader = createChannelHeader(HeaderType.ENDORSER_TRANSACTION,
119-
context.getTxID(), channelID, context.getEpoch(), context.getFabricTimestamp(), chaincodeHeaderExtension);
119+
context.getTxID(), channelID, context.getEpoch(), context.getFabricTimestamp(), chaincodeHeaderExtension, null);
120120

121121
ChaincodeInvocationSpec chaincodeInvocationSpec = createChaincodeInvocationSpec(
122122
chaincodeID,

src/main/java/org/hyperledger/fabric/sdk/transaction/ProtoUtils.java

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import java.util.Date;
1919
import java.util.List;
2020

21+
import javax.xml.bind.DatatypeConverter;
22+
2123
import com.google.protobuf.ByteString;
2224
import com.google.protobuf.Timestamp;
2325
import com.google.protobuf.util.Timestamps;
@@ -66,7 +68,7 @@ private ProtoUtils() {
6668

6769
// static CryptoSuite suite = null;
6870

69-
/**
71+
/*
7072
* createChannelHeader create chainHeader
7173
*
7274
* @param type header type. See {@link ChannelHeader.Builder#setType}.
@@ -75,13 +77,21 @@ private ProtoUtils() {
7577
* @param epoch the epoch in which this header was generated. See {@link ChannelHeader.Builder#setEpoch}.
7678
* @param timeStamp local time when the message was created. See {@link ChannelHeader.Builder#setTimestamp}.
7779
* @param chaincodeHeaderExtension extension to attach dependent on the header type. See {@link ChannelHeader.Builder#setExtension}.
80+
* @param tlsCertHash
7881
* @return a new chain header.
7982
*/
80-
public static ChannelHeader createChannelHeader(HeaderType type, String txID, String channelID, long epoch, Timestamp timeStamp, ChaincodeHeaderExtension chaincodeHeaderExtension) {
83+
public static ChannelHeader createChannelHeader(HeaderType type, String txID, String channelID, long epoch,
84+
Timestamp timeStamp, ChaincodeHeaderExtension chaincodeHeaderExtension,
85+
byte[] tlsCertHash) {
8186

8287
if (isDebugLevel) {
83-
logger.debug(format("ChannelHeader: type: %s, version: 1, Txid: %s, channelId: %s, epoch %d",
84-
type.name(), txID, channelID, epoch));
88+
String tlschs = "";
89+
if (tlsCertHash != null) {
90+
tlschs = DatatypeConverter.printHexBinary(tlsCertHash);
91+
92+
}
93+
logger.debug(format("ChannelHeader: type: %s, version: 1, Txid: %s, channelId: %s, epoch %d, clientTLSCertificate digest: %s",
94+
type.name(), txID, channelID, epoch, tlschs));
8595

8696
}
8797

@@ -96,6 +106,10 @@ public static ChannelHeader createChannelHeader(HeaderType type, String txID, St
96106
ret.setExtension(chaincodeHeaderExtension.toByteString());
97107
}
98108

109+
if (tlsCertHash != null) {
110+
ret.setTlsCertHash(ByteString.copyFrom(tlsCertHash));
111+
}
112+
99113
return ret.build();
100114

101115
}
@@ -239,11 +253,11 @@ static Timestamp getTimestampFromDate(Date date) {
239253
.setNanos((int) ((millis % 1000) * 1000000)).build();
240254
}
241255

242-
public static Envelope createSeekInfoEnvelope(TransactionContext transactionContext, SeekInfo seekInfo) throws CryptoException {
256+
public static Envelope createSeekInfoEnvelope(TransactionContext transactionContext, SeekInfo seekInfo, byte[] tlsCertHash) throws CryptoException {
243257

244258
ChannelHeader seekInfoHeader = createChannelHeader(Common.HeaderType.DELIVER_SEEK_INFO,
245259
transactionContext.getTxID(), transactionContext.getChannelID(), transactionContext.getEpoch(),
246-
transactionContext.getFabricTimestamp(), null);
260+
transactionContext.getFabricTimestamp(), null, tlsCertHash);
247261

248262
SignatureHeader signatureHeader = SignatureHeader.newBuilder()
249263
.setCreator(transactionContext.getIdentity().toByteString())
@@ -268,13 +282,13 @@ public static Envelope createSeekInfoEnvelope(TransactionContext transactionCont
268282

269283
public static Envelope createSeekInfoEnvelope(TransactionContext transactionContext, SeekPosition startPosition,
270284
SeekPosition stopPosition,
271-
SeekBehavior seekBehavior) throws CryptoException {
285+
SeekBehavior seekBehavior, byte[] tlsCertHash) throws CryptoException {
272286

273287
return createSeekInfoEnvelope(transactionContext, SeekInfo.newBuilder()
274288
.setStart(startPosition)
275289
.setStop(stopPosition)
276290
.setBehavior(seekBehavior)
277-
.build());
291+
.build(), tlsCertHash);
278292

279293
}
280294
}

0 commit comments

Comments
 (0)