Skip to content

Commit

Permalink
[FAB-10351] Idemix CA support for java SDK
Browse files Browse the repository at this point in the history
Add support for the java SDK to get an idemix credential from fabric CA
and use that credential to submit a proposal and transaction for the
end-to-end flow.

Change-Id: Ia56563b35211b7e490a08923c6e45c50204bc9a4
Signed-off-by: Saad Karim <skarim@us.ibm.com>
Signed-off-by: Keith Smith <bksmith@us.ibm.com>
Signed-off-by: Rafa Torres <rtm@zurich.ibm.com>
  • Loading branch information
Saad Karim authored and Rafa Torres committed Sep 11, 2018
1 parent 5211d6c commit 8779804
Show file tree
Hide file tree
Showing 143 changed files with 2,045 additions and 119 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,14 @@ For v1.2 integration the commands use the v12 profiles in configtx.yaml.
* configtxgen --configPath . -outputCreateChannelTx foo.tx -profile TwoOrgsChannel_v12 -channelID foo
This should produce in the `v1.2` directory: bar.tx,foo.tx, orderer.block

For v1.3 integration, cd to the `src/test/fixture/sdkintegration/e2e-2Orgs/v1.3` directory
and execute the following commands:
* configtxgen -outputBlock orderer.block -profile TwoOrgsOrdererGenesis_v13
* configtxgen -outputCreateChannelTx foo.tx -profile TwoOrgsChannel_v13 -channelID foo
* configtxgen -outputCreateChannelTx bar.tx -profile TwoOrgsChannel_v13 -channelID bar

This should produce the following files in the same directory: orderer.block, foo.tx, and bar.tx

**Note:** The above describes how this was done. If you redo this there are private key files
which are produced with unique names which won't match what's expected in the integration tests.
One example of this is the docker-compose.yaml (search for **_sk**)
Expand Down
40 changes: 28 additions & 12 deletions src/main/java/org/hyperledger/fabric/sdk/Channel.java
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@
import org.hyperledger.fabric.sdk.helper.Config;
import org.hyperledger.fabric.sdk.helper.DiagnosticFileDumper;
import org.hyperledger.fabric.sdk.helper.Utils;
import org.hyperledger.fabric.sdk.identity.IdentityFactory;
import org.hyperledger.fabric.sdk.security.certgen.TLSCertificateBuilder;
import org.hyperledger.fabric.sdk.security.certgen.TLSCertificateKeyPair;
import org.hyperledger.fabric.sdk.transaction.GetConfigBlockBuilder;
Expand Down Expand Up @@ -168,7 +167,6 @@ public class Channel implements Serializable {
private transient LinkedHashMap<String, ChaincodeEventListenerEntry> chainCodeListeners = new LinkedHashMap<>();
transient HFClient client;
private Set<String> discoveryEndpoints = Collections.synchronizedSet(new HashSet<>());

/**
* Runs processing events from event hubs.
*/
Expand Down Expand Up @@ -1786,11 +1784,12 @@ private Map<String, MSP> traverseConfigGroupsMSP(String name, ConfigGroup config
if (!msps.containsKey(name)) {

MspConfig.MSPConfig mspConfig = MspConfig.MSPConfig.parseFrom(mspv.getValue());
Integer type = new Integer(mspConfig.getType());
if (type == 0) {
MspConfig.FabricMSPConfig fabricMSPConfig = MspConfig.FabricMSPConfig.parseFrom(mspConfig.getConfig());

MspConfig.FabricMSPConfig fabricMSPConfig = MspConfig.FabricMSPConfig.parseFrom(mspConfig.getConfig());

msps.put(name, new MSP(name, fabricMSPConfig));

msps.put(name, new MSP(name, fabricMSPConfig));
}
}
}

Expand Down Expand Up @@ -3623,8 +3622,7 @@ private Pair(Peer peer, Future<FabricProposalResponse.ProposalResponse> future)
}
}

ProposalResponse proposalResponse = new ProposalResponse(transactionContext.getTxID(),
transactionContext.getChannelID(), status, message);
ProposalResponse proposalResponse = new ProposalResponse(transactionContext, status, message);
proposalResponse.setProposalResponse(fabricResponse);
proposalResponse.setProposal(signedProposal);
proposalResponse.setPeer(peerFuturePair.peer);
Expand Down Expand Up @@ -4282,14 +4280,32 @@ public CompletableFuture<TransactionEvent> sendTransaction(Collection<ProposalRe
FabricProposal.Proposal proposal = null;
ByteString proposalResponsePayload = null;
String proposalTransactionID = null;
TransactionContext transactionContext = null;

for (ProposalResponse sdkProposalResponse : proposalResponses) {
ed.add(sdkProposalResponse.getProposalResponse().getEndorsement());
if (proposal == null) {
proposal = sdkProposalResponse.getProposal();
proposalTransactionID = sdkProposalResponse.getTransactionID();
if (proposalTransactionID == null) {
throw new InvalidArgumentException("Proposals with missing transaction ID");
}
proposalResponsePayload = sdkProposalResponse.getProposalResponse().getPayload();

if (proposalResponsePayload == null) {
throw new InvalidArgumentException("Proposals with missing payload.");
}
transactionContext = sdkProposalResponse.getTransactionContext();
if (transactionContext == null) {
throw new InvalidArgumentException("Proposals with missing transaction context.");
}
} else {
final String transactionID = sdkProposalResponse.getTransactionID();
if (transactionID == null) {
throw new InvalidArgumentException("Proposals with missing transaction id.");
}
if (!proposalTransactionID.equals(transactionID)) {
throw new InvalidArgumentException(format("Proposals with different transaction IDs %s, and %s", proposalTransactionID, transactionID));
}
}
}

Expand All @@ -4300,7 +4316,7 @@ public CompletableFuture<TransactionEvent> sendTransaction(Collection<ProposalRe
.endorsements(ed)
.proposalResponsePayload(proposalResponsePayload).build();

Envelope transactionEnvelope = createTransactionEnvelope(transactionPayload, userContext);
Envelope transactionEnvelope = createTransactionEnvelope(transactionPayload, transactionContext);

NOfEvents nOfEvents = transactionOptions.nOfEvents;

Expand Down Expand Up @@ -4464,11 +4480,11 @@ private String getRespData(BroadcastResponse resp) {

}

private Envelope createTransactionEnvelope(Payload transactionPayload, User user) throws CryptoException, InvalidArgumentException {
private Envelope createTransactionEnvelope(Payload transactionPayload, TransactionContext transactionContext) throws CryptoException, InvalidArgumentException {

return Envelope.newBuilder()
.setPayload(transactionPayload.toByteString())
.setSignature(ByteString.copyFrom(IdentityFactory.getSigningIdentity(client.getCryptoSuite(), user).sign(transactionPayload.toByteArray())))
.setSignature(ByteString.copyFrom(transactionContext.sign(transactionPayload.toByteArray())))
.build();

}
Expand Down
10 changes: 8 additions & 2 deletions src/main/java/org/hyperledger/fabric/sdk/ProposalResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.hyperledger.fabric.sdk.helper.Config;
import org.hyperledger.fabric.sdk.helper.DiagnosticFileDumper;
import org.hyperledger.fabric.sdk.security.CryptoSuite;
import org.hyperledger.fabric.sdk.transaction.TransactionContext;

import static java.lang.String.format;
import static org.hyperledger.fabric.sdk.helper.Utils.toHexString;
Expand All @@ -47,10 +48,15 @@ public class ProposalResponse extends ChaincodeResponse {
private FabricProposalResponse.ProposalResponse proposalResponse;
private Peer peer = null;
private ChaincodeID chaincodeID = null;
private final TransactionContext transactionContext;

ProposalResponse(String transactionID, String chaincodeID, int status, String message) {
super(transactionID, chaincodeID, status, message);
ProposalResponse(TransactionContext transactionContext, int status, String message) {
super(transactionContext.getTxID(), transactionContext.getChannelID(), status, message);
this.transactionContext = transactionContext;
}

TransactionContext getTransactionContext() {
return transactionContext;
}

ProposalResponsePayloadDeserializer getProposalResponsePayloadDeserializer() throws InvalidArgumentException {
Expand Down
16 changes: 9 additions & 7 deletions src/main/java/org/hyperledger/fabric/sdk/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import org.hyperledger.fabric.sdk.exception.InvalidArgumentException;
import org.hyperledger.fabric.sdk.helper.Utils;
import org.hyperledger.fabric.sdk.identity.X509Enrollment;

import static java.lang.String.format;

Expand Down Expand Up @@ -83,18 +84,19 @@ static void userContextCheck(User userContext) throws InvalidArgumentException {
if (enrollment == null) {
throw new InvalidArgumentException(format("UserContext for user %s has no enrollment set.", userName));
}
if (enrollment instanceof X509Enrollment) {
if (Utils.isNullOrEmpty(enrollment.getCert())) {
throw new InvalidArgumentException(format("UserContext for user %s enrollment missing user certificate.", userName));
}
if (null == enrollment.getKey()) {
throw new InvalidArgumentException(format("UserContext for user %s has Enrollment missing signing key", userName));
}
}

if (Utils.isNullOrEmpty(userContext.getMspId())) {
throw new InvalidArgumentException(format("UserContext for user %s has user's MSPID missing.", userName));
}

if (Utils.isNullOrEmpty(enrollment.getCert())) {
throw new InvalidArgumentException(format("UserContext for user %s enrollment missing user certificate.", userName));
}
if (null == enrollment.getKey()) {
throw new InvalidArgumentException(format("UserContext for user %s has Enrollment missing signing key", userName));
}

}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package org.hyperledger.fabric.sdk.identity;

import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;

import javax.security.auth.DestroyFailedException;

import org.apache.milagro.amcl.FP256BN.BIG;
import org.hyperledger.fabric.protos.idemix.Idemix.CredentialRevocationInformation;
import org.hyperledger.fabric.sdk.Enrollment;
Expand All @@ -12,6 +13,8 @@

public class IdemixEnrollment implements Enrollment {

private static final String algo = "idemix";


protected final IdemixIssuerPublicKey ipk;
protected final PublicKey revocationPk;
Expand All @@ -22,9 +25,6 @@ public class IdemixEnrollment implements Enrollment {
protected final String ou;
protected final boolean role;

private KeyPair key;
private String cert;

public IdemixEnrollment(IdemixIssuerPublicKey ipk, PublicKey revocationPk, String mspId, BIG sk, IdemixCredential cred, CredentialRevocationInformation cri, String ou, boolean role) {
this.ipk = ipk;
this.revocationPk = revocationPk;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.hyperledger.fabric.sdk.identity;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;
import java.util.Arrays;
Expand All @@ -24,6 +25,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.milagro.amcl.FP256BN.BIG;
import org.hyperledger.fabric.protos.common.MspPrincipal;
import org.hyperledger.fabric.protos.idemix.Idemix;
import org.hyperledger.fabric.protos.msp.Identities.SerializedIdentity;
import org.hyperledger.fabric.sdk.exception.CryptoException;
Expand Down Expand Up @@ -171,9 +173,15 @@ public IdemixSigningIdentity(IdemixIssuerPublicKey ipk, PublicKey revocationPk,
if (cred.getAttrs().length != 4) {
throw new CryptoException("Error: There are " + cred.getAttrs().length + " attributes and the expected are 4");
}

byte[] ouBytes = cred.getAttrs()[0];
byte[] ouProtoBytes = MspPrincipal.OrganizationUnit.newBuilder()
.setMspIdentifier(mspId)
.setOrganizationalUnitIdentifier(new String(ouBytes))
.build().toByteArray();
byte[] roleBytes = cred.getAttrs()[1];
byte[] roleProtoBytes = MspPrincipal.MSPRole.newBuilder()
.setRoleValue(ByteBuffer.wrap(roleBytes).getInt())
.build().toByteArray();
byte[] eIdBytes = cred.getAttrs()[2];
byte[] rHBytes = cred.getAttrs()[3];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,11 @@
import org.hyperledger.fabric.protos.peer.Chaincode.ChaincodeSpec;
import org.hyperledger.fabric.protos.peer.Chaincode.ChaincodeSpec.Type;
import org.hyperledger.fabric.protos.peer.FabricProposal.ChaincodeHeaderExtension;
import org.hyperledger.fabric.sdk.Enrollment;
import org.hyperledger.fabric.sdk.User;
import org.hyperledger.fabric.sdk.exception.CryptoException;
import org.hyperledger.fabric.sdk.exception.InvalidArgumentException;
import org.hyperledger.fabric.sdk.identity.X509Enrollment;
import org.hyperledger.fabric.sdk.security.CryptoPrimitives;
import org.hyperledger.fabric.sdk.security.CryptoSuite;

Expand Down Expand Up @@ -192,28 +194,31 @@ public static ByteString getSignatureHeaderAsByteString(User user, TransactionCo

if (isDebugLevel) {

String cert = user.getEnrollment().getCert();
// logger.debug(format(" User: %s Certificate:\n%s", user.getName(), cert));
Enrollment enrollment = user.getEnrollment();
String cert = enrollment.getCert();
logger.debug(format(" User: %s Certificate:\n%s", user.getName(), cert));

if (null == suite) {
if (enrollment instanceof X509Enrollment) {
if (null == suite) {

try {
suite = CryptoSuite.Factory.getCryptoSuite();
} catch (Exception e) {
//best try.
}

try {
suite = CryptoSuite.Factory.getCryptoSuite();
} catch (Exception e) {
//best try.
}
if (null != suite && suite instanceof CryptoPrimitives) {

}
if (null != suite && suite instanceof CryptoPrimitives) {
CryptoPrimitives cp = (CryptoPrimitives) suite;
byte[] der = cp.certificateToDER(cert);
if (null != der && der.length > 0) {

CryptoPrimitives cp = (CryptoPrimitives) suite;
byte[] der = cp.certificateToDER(cert);
if (null != der && der.length > 0) {
cert = toHexString(suite.hash(der));

cert = toHexString(suite.hash(der));
}

}

}

logger.debug(format("SignatureHeader: nonce: %s, User:%s, MSPID: %s, idBytes: %s",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ public String getTxID() {
return txID;
}

byte[] sign(byte[] b) throws CryptoException, InvalidArgumentException {
public byte[] sign(byte[] b) throws CryptoException, InvalidArgumentException {
return signingIdentity.sign(b);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1169,7 +1169,7 @@ public Enrollment idemixEnroll(Enrollment enrollment, String mspID) throws Enrol
}
String ou = attrs.getString("OU");
if (Utils.isNullOrEmpty(ou)) {
throw new InvalidArgumentException("fabric-ca-server did not return a 'ou' in the response from " + HFCA_IDEMIXCRED);
throw new InvalidArgumentException("fabric-ca-server did not return a 'ou' attribute in the response from " + HFCA_IDEMIXCRED);
}
boolean role = attrs.getBoolean("Role");

Expand Down
4 changes: 3 additions & 1 deletion src/test/fixture/sdkintegration/.env
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ ORG_HYPERLEDGER_FABRIC_SDKTEST_INTEGRATIONTESTS_CLIENT_AUTH_REQUIRED=false
IMAGE_TAG_FABRIC=
IMAGE_TAG_FABRIC_CA=
#FAB_CONFIG_GEN_VERS=v1.0
FAB_CONFIG_GEN_VERS=v1.2
#FAB_CONFIG_GEN_VERS=v1.1
#FAB_CONFIG_GEN_VERS=v1.2
FAB_CONFIG_GEN_VERS=v1.3
V11_IDENTITIES_ALLOWREMOVE=--cfg.identities.allowremove
V11_AFFILIATIONS_ALLOWREMOVE=--cfg.affiliations.allowremove
19 changes: 16 additions & 3 deletions src/test/fixture/sdkintegration/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,37 @@ services:
ca0:
image: hyperledger/fabric-ca${IMAGE_TAG_FABRIC_CA}
environment:
- FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server
- FABRIC_CA_SERVER_HOME=/etc/hyperledger/fabric-ca-server
- FABRIC_CA_SERVER_CA_NAME=ca0
- FABRIC_CA_SERVER_CA_CERTFILE=/etc/hyperledger/fabric-ca-server-config/ca.org1.example.com-cert.pem
- FABRIC_CA_SERVER_CA_KEYFILE=/etc/hyperledger/fabric-ca-server-config/fcf776b02a05600408d0be9d9752afc59f64950b721cacb363b5b95a0fea6216_sk
- FABRIC_CA_SERVER_TLS_CERTFILE=/etc/hyperledger/fabric-ca-server-config/ca.org1.example.com-cert.pem
- FABRIC_CA_SERVER_TLS_KEYFILE=/etc/hyperledger/fabric-ca-server-config/fcf776b02a05600408d0be9d9752afc59f64950b721cacb363b5b95a0fea6216_sk
- FABRIC_CA_SERVER_REGISTRY_MAXENROLLMENTS=-1
ports:
- "7054:7054"
command: sh -c 'fabric-ca-server start -n ca0 ${V11_IDENTITIES_ALLOWREMOVE} ${V11_AFFILIATIONS_ALLOWREMOVE} --registry.maxenrollments -1 --ca.certfile /etc/hyperledger/fabric-ca-server-config/ca.org1.example.com-cert.pem --ca.keyfile /etc/hyperledger/fabric-ca-server-config/fcf776b02a05600408d0be9d9752afc59f64950b721cacb363b5b95a0fea6216_sk -b admin:adminpw ${ORG_HYPERLEDGER_FABRIC_SDKTEST_INTEGRATIONTESTS_CA_TLS} --tls.certfile /etc/hyperledger/fabric-ca-server-config/ca.org1.example.com-cert.pem --tls.keyfile /etc/hyperledger/fabric-ca-server-config/fcf776b02a05600408d0be9d9752afc59f64950b721cacb363b5b95a0fea6216_sk -d'
command: bash -c 'cp -R /tmp/msp /etc/hyperledger/fabric-ca-server; mv /etc/hyperledger/fabric-ca-server/msp/*PublicKey /etc/hyperledger/fabric-ca-server; fabric-ca-server start -b admin:adminpw ${V11_IDENTITIES_ALLOWREMOVE} ${V11_AFFILIATIONS_ALLOWREMOVE} ${ORG_HYPERLEDGER_FABRIC_SDKTEST_INTEGRATIONTESTS_CA_TLS} -d'

volumes:
- ./e2e-2Orgs/${FAB_CONFIG_GEN_VERS}/crypto-config/peerOrganizations/org1.example.com/ca/:/etc/hyperledger/fabric-ca-server-config:ro
- ./e2e-2Orgs/${FAB_CONFIG_GEN_VERS}/crypto-config/peerOrganizations/org3.example.com/msp/:/tmp/msp:ro
container_name: ca_peerOrg1

ca1:
image: hyperledger/fabric-ca${IMAGE_TAG_FABRIC_CA}
environment:
- FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server
- FABRIC_CA_SERVER_CA_CERTFILE=/etc/hyperledger/fabric-ca-server-config/ca.org2.example.com-cert.pem
- FABRIC_CA_SERVER_CA_KEYFILE=/etc/hyperledger/fabric-ca-server-config/b59bba37975dafcc4a93984aa01d3d29b64894617db9e0c9a2d486b5273cbd27_sk
- FABRIC_CA_SERVER_TLS_CERTFILE=/etc/hyperledger/fabric-ca-server-config/ca.org2.example.com-cert.pem
- FABRIC_CA_SERVER_TLS_KEYFILE=/etc/hyperledger/fabric-ca-server-config/b59bba37975dafcc4a93984aa01d3d29b64894617db9e0c9a2d486b5273cbd27_sk
- FABRIC_CA_SERVER_REGISTRY_MAXENROLLMENTS=-1
ports:
- "8054:7054"
command: sh -c 'fabric-ca-server start --registry.maxenrollments -1 --ca.certfile /etc/hyperledger/fabric-ca-server-config/ca.org2.example.com-cert.pem --ca.keyfile /etc/hyperledger/fabric-ca-server-config/b59bba37975dafcc4a93984aa01d3d29b64894617db9e0c9a2d486b5273cbd27_sk -b admin:adminpw ${ORG_HYPERLEDGER_FABRIC_SDKTEST_INTEGRATIONTESTS_CA_TLS} --tls.certfile /etc/hyperledger/fabric-ca-server-config/ca.org2.example.com-cert.pem --tls.keyfile /etc/hyperledger/fabric-ca-server-config/b59bba37975dafcc4a93984aa01d3d29b64894617db9e0c9a2d486b5273cbd27_sk -d'
command: bash -c 'cp -R /tmp/msp /etc/hyperledger/fabric-ca-server; mv /etc/hyperledger/fabric-ca-server/msp/*PublicKey /etc/hyperledger/fabric-ca-server; fabric-ca-server start -b admin:adminpw ${ORG_HYPERLEDGER_FABRIC_SDKTEST_INTEGRATIONTESTS_CA_TLS} -d'
volumes:
- ./e2e-2Orgs/${FAB_CONFIG_GEN_VERS}/crypto-config/peerOrganizations/org2.example.com/ca/:/etc/hyperledger/fabric-ca-server-config:ro
- ./e2e-2Orgs/${FAB_CONFIG_GEN_VERS}/crypto-config/peerOrganizations/org4.example.com/msp/:/tmp/msp:ro
container_name: ca_peerOrg2


Expand Down
Loading

0 comments on commit 8779804

Please sign in to comment.