Skip to content

Commit

Permalink
FAB-7949 tlsbinding_event_reg
Browse files Browse the repository at this point in the history
Change-Id: I4f81f79495d2f53b38fd45f75219e27d312adb51
Signed-off-by: rickr <cr22rc@gmail.com>
  • Loading branch information
cr22rc committed Feb 9, 2018
1 parent 09776f0 commit 96ee2df
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 36 deletions.
4 changes: 2 additions & 2 deletions src/main/java/org/hyperledger/fabric/sdk/Channel.java
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ private void sendUpdateChannel(byte[] configupdate, byte[][] signers, Orderer or
final ByteString sigHeaderByteString = getSignatureHeaderAsByteString(transactionContext);

final ChannelHeader payloadChannelHeader = ProtoUtils.createChannelHeader(HeaderType.CONFIG_UPDATE,
transactionContext.getTxID(), name, transactionContext.getEpoch(), transactionContext.getFabricTimestamp(), null);
transactionContext.getTxID(), name, transactionContext.getEpoch(), transactionContext.getFabricTimestamp(), null, null);

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

TransactionContext txContext = getTransactionContext();

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

if (deliver.length < 1) {
logger.warn(format("Genesis block for channel %s fetch bad deliver missing status block only got blocks:%d", name, deliver.length));
Expand Down
29 changes: 27 additions & 2 deletions src/main/java/org/hyperledger/fabric/sdk/Endpoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
Expand All @@ -49,9 +49,12 @@
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x500.style.IETFUtils;
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.hyperledger.fabric.sdk.exception.CryptoException;
import org.hyperledger.fabric.sdk.security.CryptoPrimitives;

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

class Endpoint {
Expand All @@ -60,6 +63,8 @@ class Endpoint {
private final String addr;
private final int port;
private final String url;
private byte[] clientTLSCertificateDigest;
private byte[] tlsClientCertificatePEMBytes;
private NettyChannelBuilder channelBuilder = null;

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

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

if (tlsClientCertificatePEMBytes != null && clientTLSCertificateDigest == null) {

String pemCert = new String(tlsClientCertificatePEMBytes, UTF_8);
byte[] derBytes = Base64.getDecoder().decode(
pemCert.replaceAll("-+[ \t]*(BEGIN|END)[ \t]+CERTIFICATE[ \t]*-+", "").replaceAll("\\s", "").trim()
);

Digest digest = new SHA256Digest();
clientTLSCertificateDigest = new byte[digest.getDigestSize()];
digest.update(derBytes, 0, derBytes.length);
digest.doFinal(clientTLSCertificateDigest, 0);
}

return clientTLSCertificateDigest;
}

private static final Pattern METHOD_PATTERN = Pattern.compile("grpc\\.NettyChannelBuilderOption\\.([^.]*)$");
private static final Map<Class<?>, Class<?>> WRAPPERS_TO_PRIM = new ImmutableMap.Builder<Class<?>, Class<?>>()
.put(Boolean.class, boolean.class).put(Byte.class, byte.class).put(Character.class, char.class)
Expand Down
22 changes: 18 additions & 4 deletions src/main/java/org/hyperledger/fabric/sdk/EventHub.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

import javax.xml.bind.DatatypeConverter;

import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import io.grpc.ManagedChannel;
Expand Down Expand Up @@ -71,6 +73,7 @@ public class EventHub implements Serializable {
private transient boolean shutdown = false;
private Channel channel;
private transient TransactionContext transactionContext;
private transient byte[] clientTLSCertificateDigest;

/**
* Get disconnected time.
Expand Down Expand Up @@ -189,7 +192,10 @@ synchronized boolean connect(final TransactionContext transactionContext) throws

lastConnectedAttempt = System.currentTimeMillis();

managedChannel = new Endpoint(url, properties).getChannelBuilder().build();
Endpoint endpoint = new Endpoint(url, properties);
managedChannel = endpoint.getChannelBuilder().build();

clientTLSCertificateDigest = endpoint.getClientTLSCertificateDigest();

events = EventsGrpc.newStub(managedChannel);

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

PeerEvents.Register register = PeerEvents.Register.newBuilder()
.addEvents(PeerEvents.Interest.newBuilder().setEventType(PeerEvents.EventType.BLOCK).build()).build();
ByteString blockEventByteString = PeerEvents.Event.newBuilder().setRegister(register)
PeerEvents.Event.Builder blockEventBuilder = PeerEvents.Event.newBuilder().setRegister(register)
.setCreator(transactionContext.getIdentity().toByteString())
.setTimestamp(ProtoUtils.getCurrentFabricTimestamp())
.build().toByteString();
.setTimestamp(ProtoUtils.getCurrentFabricTimestamp());

if (null != clientTLSCertificateDigest) {
logger.trace("Setting clientTLSCertificate digest for event registration to " + DatatypeConverter.printHexBinary(clientTLSCertificateDigest));
blockEventBuilder.setTlsCertHash(ByteString.copyFrom(clientTLSCertificateDigest));

}

ByteString blockEventByteString = blockEventBuilder.build().toByteString();

PeerEvents.SignedEvent signedBlockEvent = PeerEvents.SignedEvent.newBuilder()
.setEventBytes(blockEventByteString)
.setSignature(transactionContext.signByteString(blockEventByteString.toByteArray()))
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/org/hyperledger/fabric/sdk/HFClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -391,20 +391,24 @@ public QueryByChaincodeRequest newQueryProposalRequest() {
* Set the User context for this client.
*
* @param userContext
* @return the old user context. Maybe null if never set!
* @throws InvalidArgumentException
*/

public void setUserContext(User userContext) throws InvalidArgumentException {
public User setUserContext(User userContext) throws InvalidArgumentException {

if (null == cryptoSuite) {
throw new InvalidArgumentException("No cryptoSuite has been set.");
}
userContextCheck(userContext);

User ret = this.userContext;
this.userContext = userContext;

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

return ret;
}

/**
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/org/hyperledger/fabric/sdk/Peer.java
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ void initiateEventing(TransactionContext transactionContext, PeerOptions peersOp

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

peerEventingClient = new PeerEventServiceClient(this, new Endpoint(url, properties), properties, peersOptions);

peerEventingClient.connect(transactionContext);

Expand Down Expand Up @@ -303,7 +304,8 @@ void reconnectPeerEventServiceClient(final PeerEventServiceClient failedPeerEven

logger.debug(t);

PeerEventServiceClient lpeerEventingClient = new PeerEventServiceClient(this, new Endpoint(url, properties).getChannelBuilder(), properties, null);
PeerEventServiceClient lpeerEventingClient = new PeerEventServiceClient(this,
new Endpoint(url, properties), properties, failedPeerEventServiceClient.getPeerOptions());

try {
lpeerEventingClient.connect(fltransactionContext);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,10 @@ class PeerEventServiceClient {
private final String name;
private final String url;
private final long peerEventRegistrationWaitTimeMilliSecs;

private final PeerOptions peerOptions;
private final boolean filterBlock;
private byte[] clientTLSCertificateDigest;
Properties properties = new Properties();
StreamObserver<Envelope> nso = null;
StreamObserver<DeliverResponse> so = null;
Expand All @@ -72,15 +74,16 @@ class PeerEventServiceClient {
/**
* Construct client for accessing Peer eventing service using the existing managedChannel.
*/
PeerEventServiceClient(Peer peer, ManagedChannelBuilder<?> channelBuilder, Properties properties, PeerOptions peerOptions) {
PeerEventServiceClient(Peer peer, Endpoint endpoint, Properties properties, PeerOptions peerOptions) {

this.channelBuilder = channelBuilder;
this.channelBuilder = endpoint.getChannelBuilder();
this.filterBlock = peerOptions.isRegisterEventsForFilteredBlocks();
this.peer = peer;
name = peer.getName();
url = peer.getUrl();
channelName = peer.getChannel().getName();
this.peerOptions = peerOptions;
clientTLSCertificateDigest = endpoint.getClientTLSCertificateDigest();

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

Expand All @@ -106,6 +109,10 @@ class PeerEventServiceClient {

}

PeerOptions getPeerOptions() {
return peerOptions.clone();
}

synchronized void shutdown(boolean force) {

if (shutdown) {
Expand Down Expand Up @@ -322,7 +329,7 @@ void connect(TransactionContext transactionContext) throws TransactionException
// Peer eventing
void peerVent(TransactionContext transactionContext) throws TransactionException {

final Envelope latestBlock;
final Envelope envelope;
try {

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

latestBlock = createSeekInfoEnvelope(transactionContext,
// properties.

envelope = createSeekInfoEnvelope(transactionContext,
start.build(),
Ab.SeekPosition.newBuilder()
.setSpecified(Ab.SeekSpecified.newBuilder().setNumber(peerOptions.getStopEvents()).build())
// .setSpecified(Ab.SeekSpecified.newBuilder().setNumber(1L).build())
.build(),
SeekInfo.SeekBehavior.BLOCK_UNTIL_READY
SeekInfo.SeekBehavior.BLOCK_UNTIL_READY,

);
DeliverResponse[] deliver = connectEnvelope(latestBlock);
clientTLSCertificateDigest);
connectEnvelope(envelope);
} catch (CryptoException e) {
throw new TransactionException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ private FabricProposal.Proposal createFabricProposal(String channelID, Chaincode
.setChaincodeId(chaincodeID).build();

Common.ChannelHeader chainHeader = createChannelHeader(HeaderType.ENDORSER_TRANSACTION,
context.getTxID(), channelID, context.getEpoch(), context.getFabricTimestamp(), chaincodeHeaderExtension);
context.getTxID(), channelID, context.getEpoch(), context.getFabricTimestamp(), chaincodeHeaderExtension, null);

ChaincodeInvocationSpec chaincodeInvocationSpec = createChaincodeInvocationSpec(
chaincodeID,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import java.util.Date;
import java.util.List;

import javax.xml.bind.DatatypeConverter;

import com.google.protobuf.ByteString;
import com.google.protobuf.Timestamp;
import com.google.protobuf.util.Timestamps;
Expand Down Expand Up @@ -66,7 +68,7 @@ private ProtoUtils() {

// static CryptoSuite suite = null;

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

if (isDebugLevel) {
logger.debug(format("ChannelHeader: type: %s, version: 1, Txid: %s, channelId: %s, epoch %d",
type.name(), txID, channelID, epoch));
String tlschs = "";
if (tlsCertHash != null) {
tlschs = DatatypeConverter.printHexBinary(tlsCertHash);

}
logger.debug(format("ChannelHeader: type: %s, version: 1, Txid: %s, channelId: %s, epoch %d, clientTLSCertificate digest: %s",
type.name(), txID, channelID, epoch, tlschs));

}

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

if (tlsCertHash != null) {
ret.setTlsCertHash(ByteString.copyFrom(tlsCertHash));
}

return ret.build();

}
Expand Down Expand Up @@ -239,11 +253,11 @@ static Timestamp getTimestampFromDate(Date date) {
.setNanos((int) ((millis % 1000) * 1000000)).build();
}

public static Envelope createSeekInfoEnvelope(TransactionContext transactionContext, SeekInfo seekInfo) throws CryptoException {
public static Envelope createSeekInfoEnvelope(TransactionContext transactionContext, SeekInfo seekInfo, byte[] tlsCertHash) throws CryptoException {

ChannelHeader seekInfoHeader = createChannelHeader(Common.HeaderType.DELIVER_SEEK_INFO,
transactionContext.getTxID(), transactionContext.getChannelID(), transactionContext.getEpoch(),
transactionContext.getFabricTimestamp(), null);
transactionContext.getFabricTimestamp(), null, tlsCertHash);

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

public static Envelope createSeekInfoEnvelope(TransactionContext transactionContext, SeekPosition startPosition,
SeekPosition stopPosition,
SeekBehavior seekBehavior) throws CryptoException {
SeekBehavior seekBehavior, byte[] tlsCertHash) throws CryptoException {

return createSeekInfoEnvelope(transactionContext, SeekInfo.newBuilder()
.setStart(startPosition)
.setStop(stopPosition)
.setBehavior(seekBehavior)
.build());
.build(), tlsCertHash);

}
}
Loading

0 comments on commit 96ee2df

Please sign in to comment.