Skip to content

Commit

Permalink
Merge pull request #186 from WeBankFinTech/feature/customize-pubkey-type
Browse files Browse the repository at this point in the history
* Lite Credential now uses Secp256k1 sign/verify method
  • Loading branch information
chenhaozx committed Apr 27, 2020
2 parents a0af436 + e07707a commit 19b6322
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 20 deletions.
8 changes: 7 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ repositories {
mavenLocal()
mavenCentral()
maven { url "https://dl.bintray.com/ethereum/maven/" }
maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
maven { url "https://oss.sonatype.org/content/groups/public/" }
} else {
maven {
url 'dependencies'
Expand Down Expand Up @@ -139,8 +141,10 @@ dependencies {
if (!gradle.startParameter.isOffline()) {
compile logger, lombok, apache_commons, json, mysql_driver, zxing, rpc, pdfbox, protobuf, caffeine, oval
compile("com.webank:weid-contract-java:1.2.19") {
exclude group: "org.fisco-bcos", module: "web3sdk"
exclude group: "org.slf4j", module: "slf4j-log4j12"
}
compile("org.fisco-bcos:web3sdk:2.4.0-0423-SNAPSHOT")
compile files("lib/WeDPR-Java-SDK.jar")
testCompile logger, lombok, apache_commons, json, junit, jmockit, rpc, pdfbox, protobuf, caffeine, oval
} else {
Expand All @@ -155,9 +159,11 @@ dependencies {
testCompileOnly 'org.projectlombok:lombok:1.18.10'
compile logger, apache_commons, json, mysql_driver, zxing, rpc, pdfbox, protobuf, caffeine, oval
compile("com.webank:weid-contract-java:1.2.19") {
exclude group: "org.fisco-bcos", module: "web3sdk"
exclude group: "org.slf4j", module: "slf4j-log4j12"
}
compile files("lib/WeDPR-Java-SDK.jar")
compile("org.fisco-bcos:web3sdk:2.4.0-0423-SNAPSHOT")
compile files("lib/WeDPR-Java-SDK.jar")
testCompile logger, apache_commons, json, junit, jmockit, rpc, pdfbox, protobuf, caffeine, oval
} else {
compileOnly files('dist/lib/lombok-1.18.10.jar')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -466,8 +466,8 @@ private static void addSaltForList(
}

private static ErrorCode verifyContent(
CredentialPojo credential,
String publicKey,
CredentialPojo credential,
String publicKey,
boolean offLine
) {
ErrorCode errorCode;
Expand Down Expand Up @@ -534,8 +534,8 @@ private static ErrorCode verifyTimestampClaim(CredentialPojo credential) {
}

private static ErrorCode verifyContentInner(
CredentialPojo credential,
String publicKey,
CredentialPojo credential,
String publicKey,
boolean offline
) {
ErrorCode checkResp = CredentialPojoUtils.isCredentialPojoValid(credential);
Expand Down Expand Up @@ -913,12 +913,9 @@ private static ResponseData<Boolean> verifyLiteCredential(
if (!StringUtils.isBlank(publicKey)) {
boolean result;
try {
result = DataToolUtils
.verifySignature(
rawData,
credential.getSignature(),
new BigInteger(publicKey)
);
// For Lite CredentialPojo, we begin to use Secp256k1 verify to fit external type
result = DataToolUtils.secp256k1VerifySignature(rawData, credential.getSignature(),
new BigInteger(publicKey));
} catch (Exception e) {
logger.error("[verifyContent] verify signature fail.", e);
return new ResponseData<Boolean>(false, ErrorCode.CREDENTIAL_SIGNATURE_BROKEN);
Expand All @@ -941,7 +938,7 @@ private static ResponseData<Boolean> verifyLiteCredential(
} else {
WeIdDocument weIdDocument = innerResponseData.getResult();
ErrorCode verifyErrorCode = DataToolUtils
.verifySignatureFromWeId(rawData, credential.getSignature(), weIdDocument);
.verifySecp256k1SignatureFromWeId(rawData, credential.getSignature(), weIdDocument);
if (verifyErrorCode.getCode() != ErrorCode.SUCCESS.getCode()) {
return new ResponseData<Boolean>(false,
ErrorCode.getTypeByErrorCode(innerResponseData.getErrorCode()));
Expand Down Expand Up @@ -1102,12 +1099,12 @@ private ResponseData<CredentialPojo> createLiteCredential(CredentialPojo credent

String rawData = CredentialPojoUtils.getLiteCredentialThumbprintWithoutSig(credentialPojo);

String signature = DataToolUtils.sign(rawData, privateKey);
// For Lite CredentialPojo, we begin to use Secp256k1 format signature to fit external type
String signature = DataToolUtils.secp256k1Sign(rawData, new BigInteger(privateKey, 10));

String proofType = CredentialProofType.ECDSA.getTypeName();
credentialPojo.putProofValue(ParamKeyConstant.PROOF_TYPE, proofType);
credentialPojo.putProofValue(ParamKeyConstant.PROOF_SIGNATURE, signature);
//credentialPojo.addType(CredentialType.LITE1.getName());
ResponseData<CredentialPojo> responseData = new ResponseData<>(
credentialPojo,
ErrorCode.SUCCESS
Expand Down Expand Up @@ -1451,7 +1448,7 @@ public ResponseData<Boolean> verify(
return new ResponseData<Boolean>(false, ErrorCode.UNKNOW_ERROR);
}
}

/* (non-Javadoc)
* @see com.webank.weid.rpc.CredentialPojoService#verify(
* com.webank.weid.protocol.base.CredentialPojo,
Expand All @@ -1476,7 +1473,7 @@ public ResponseData<Boolean> verifyOffline(
}
return new ResponseData<Boolean>(true, ErrorCode.SUCCESS);
}

@Override
public ResponseData<Boolean> verifyPresentationFromPdf(
String pdfTemplatePath,
Expand Down
115 changes: 113 additions & 2 deletions src/main/java/com/webank/weid/util/DataToolUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
import org.bcos.web3j.crypto.Sign.SignatureData;
import org.bcos.web3j.utils.Numeric;
import org.bouncycastle.util.encoders.Base64;
import org.fisco.bcos.web3j.crypto.ECDSASign;
import org.fisco.bcos.web3j.crypto.tool.ECCDecrypt;
import org.fisco.bcos.web3j.crypto.tool.ECCEncrypt;
import org.slf4j.Logger;
Expand Down Expand Up @@ -513,6 +514,72 @@ public static Sign.SignatureData signMessage(
return Sign.signMessage(sha3(message.getBytes(StandardCharsets.UTF_8)), keyPair);
}

/**
* Secp256k1 sign.
*
* @param rawData original raw data
* @param privateKey private key in BigInteger format
*/
public static String secp256k1Sign(String rawData, BigInteger privateKey) {
ECDSASign ecdsaSign = new ECDSASign();
org.fisco.bcos.web3j.crypto.ECKeyPair keyPair = org.fisco.bcos.web3j.crypto.ECKeyPair
.create(privateKey);
org.fisco.bcos.web3j.crypto.Sign.SignatureData sigData = ecdsaSign
.secp256SignMessage(rawData.getBytes(), keyPair);
return secp256k1SigBase64Serialization(sigData);
}

/**
* Serialize secp256k1 signature into base64 encoded, in R, S, V (0, 1) format.
*
* @param sigData secp256k1 signature (v = 0,1)
* @return base64 string
*/
public static String secp256k1SigBase64Serialization(
org.fisco.bcos.web3j.crypto.Sign.SignatureData sigData) {
byte[] sigBytes = new byte[65];
sigBytes[64] = sigData.getV();
System.arraycopy(sigData.getR(), 0, sigBytes, 0, 32);
System.arraycopy(sigData.getS(), 0, sigBytes, 32, 32);
return new String(base64Encode(sigBytes), StandardCharsets.UTF_8);
}

/**
* De-Serialize secp256k1 signature base64 encoded string, in R, S, V (0, 1) format.
*
* @param signature signature base64 string
* @return secp256k1 signature (v = 0,1)
*/
public static org.fisco.bcos.web3j.crypto.Sign.SignatureData secp256k1SigBase64Deserialization(
String signature
) {
byte[] sigBytes = base64Decode(signature.getBytes(StandardCharsets.UTF_8));
byte[] r = new byte[32];
byte[] s = new byte[32];
System.arraycopy(sigBytes, 0, r, 0, 32);
System.arraycopy(sigBytes, 32, s, 0, 32);
return new org.fisco.bcos.web3j.crypto.Sign.SignatureData(sigBytes[64], r, s);
}

/**
* Verify secp256k1 signature.
*
* @param rawData original raw data
* @param signatureBase64 signature base64 string
* @param publicKey in BigInteger format
*/
public static boolean secp256k1VerifySignature(
String rawData,
String signatureBase64,
BigInteger publicKey
) {
org.fisco.bcos.web3j.crypto.Sign.SignatureData sigData = secp256k1SigBase64Deserialization(
signatureBase64);
ECDSASign ecdsaSign = new ECDSASign();
byte[] hashBytes = Hash.sha3(rawData.getBytes());
return ecdsaSign.secp256Verify(hashBytes, publicKey, sigData);
}

/**
* Sign a object based on the given privateKey in Decimal String BigInt.
*
Expand Down Expand Up @@ -590,7 +657,7 @@ public static boolean verifySignature(
BigInteger extractedPublicKey = signatureToPublicKey(message, signatureData);
return extractedPublicKey.equals(publicKey);
}

/**
* Verify whether the message and the Signature matches the given public Key.
*
Expand All @@ -607,7 +674,7 @@ public static boolean verifySignature(
throws SignatureException {
return verifySignature(message, signature, new BigInteger(publicKey));
}

/**
* eecrypt the data.
*
Expand Down Expand Up @@ -743,6 +810,50 @@ public static Sign.SignatureData rawSignatureDeserialization(int v, byte[] r, by
return new Sign.SignatureData(valueByte, r, s);
}

/**
* Verify a secp256k1 signature (base64).
*
* @param rawData the rawData to be verified
* @param signature the Signature Data in secp256k1 style
* @param weIdDocument the WeIdDocument to be extracted
* @return true if yes, false otherwise with exact error codes
*/
public static ErrorCode verifySecp256k1SignatureFromWeId(
String rawData,
String signature,
WeIdDocument weIdDocument) {
List<String> publicKeysListToVerify = new ArrayList<String>();

// Traverse public key list indexed Authentication key list
for (AuthenticationProperty authenticationProperty : weIdDocument
.getAuthentication()) {
String index = authenticationProperty.getPublicKey();
for (PublicKeyProperty publicKeyProperty : weIdDocument.getPublicKey()) {
if (publicKeyProperty.getId().equalsIgnoreCase(index)) {
publicKeysListToVerify.add(publicKeyProperty.getPublicKey());
}
}
}
try {
boolean result = false;
for (String publicKeyItem : publicKeysListToVerify) {
if (StringUtils.isNotEmpty(publicKeyItem)) {
result =
result
|| secp256k1VerifySignature(
rawData, signature, new BigInteger(publicKeyItem));
}
}
if (!result) {
return ErrorCode.CREDENTIAL_ISSUER_MISMATCH;
}
} catch (Exception e) {
logger.error("some exceptions occurred in signature verification", e);
return ErrorCode.CREDENTIAL_EXCEPTION_VERIFYSIGNATURE;
}
return ErrorCode.SUCCESS;
}

/**
* Verify a signature based on the provided raw data, and the WeID Document from chain. This
* will traverse each public key in the WeID Document and fetch all keys which belongs to the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

import com.webank.weid.common.LogUtil;
import com.webank.weid.constant.CredentialConstant;
import com.webank.weid.constant.CredentialType;
import com.webank.weid.constant.ErrorCode;
import com.webank.weid.full.TestBaseService;
import com.webank.weid.full.TestBaseUtil;
Expand All @@ -49,6 +50,7 @@
import com.webank.weid.protocol.request.CreateWeIdArgs;
import com.webank.weid.protocol.response.CreateWeIdDataResult;
import com.webank.weid.protocol.response.ResponseData;
import com.webank.weid.util.CredentialPojoUtils;
import com.webank.weid.util.DataToolUtils;
import com.webank.weid.util.DateUtils;
import com.webank.weid.util.WeIdUtils;
Expand Down Expand Up @@ -252,6 +254,35 @@ public void testCreateMultiSignCredentialPojo_failure() {
Assert.assertFalse(verifyResp.getResult());
}

@Test
public void testLiteCredentialPojo() {
CreateWeIdDataResult createWeIdDataResult = super.createWeId();
CreateCredentialPojoArgs<Map<String, Object>> args =
TestBaseUtil.buildCreateCredentialPojoArgs(createWeIdDataResult);
args.setType(CredentialType.LITE1);
args.setCptId(cptBaseInfo.getCptId());
ResponseData<CredentialPojo> response = credentialPojoService.createCredential(args);
LogUtil.info(logger, "createLiteCredential", response);
Assert.assertEquals(ErrorCode.SUCCESS.getCode(), response.getErrorCode().intValue());
CredentialPojo liteCredential = response.getResult();
ResponseData<Boolean> resp =
credentialPojoService.verify(liteCredential.getIssuer(), liteCredential);
Assert.assertTrue(resp.getResult());
// LiteCredential:
// 1. getThumbprint() -> signature (针对凭证claim内容生成thumbprint,用私钥生成签名)
String thumbprint = CredentialPojoUtils
.getLiteCredentialThumbprintWithoutSig(liteCredential);
System.out.println("Lite Credential Thumbprint: " + thumbprint + ", Thumbprint hash: "
+ DataToolUtils.sha3(thumbprint) + ", signature: " + liteCredential.getSignature());
// 2. getHash() -> createEvidence (对凭证完整内容包括签名内容进行hash,claim支持选择性披露)
System.out.println("Lite Credential Hash: "
+ CredentialPojoUtils.getLiteCredentialPojoHash(liteCredential));
Assert.assertEquals(CredentialPojoUtils.getLiteCredentialPojoHash(liteCredential),
liteCredential.getHash());
// 3. toJson() -> encrypt (唯一的序列化方法,瘦身,用于打包传输)
System.out.println("Lite Credential toJson: " + liteCredential.toJson());
}

/**
* case:when issuer is register authentication issuer, cpt publisher is not auth issuer,
* createCredentialPojo success.
Expand Down
16 changes: 14 additions & 2 deletions src/test/java/com/webank/weid/util/TestSignatureUtils.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright© (2018-2019) WeBank Co., Ltd.
* Copyright© (2018-2020) WeBank Co., Ltd.
*
* This file is part of weid-java-sdk.
*
Expand Down Expand Up @@ -32,7 +32,7 @@
/**
* Test SignatureUtils.
*
* @author v_wbjnzhang
* @author v_wbjnzhang and chaoxinhu
*/
public class TestSignatureUtils {

Expand Down Expand Up @@ -72,4 +72,16 @@ public void testSignatureUtils()
.convertBase64StringToSignatureData(new String(Base64.encode(serialized)));
logger.info(signatureData.toString());
}

@Test
public void testSecp256k1Signatures() {
String hexPrivKey =
"58317564669857453586637110679746575832914889677346283755719850144028639639651";
String msg = "12345";
org.fisco.bcos.web3j.crypto.ECKeyPair keyPair
= org.fisco.bcos.web3j.crypto.ECKeyPair.create(new BigInteger(hexPrivKey, 10));
String sig = DataToolUtils.secp256k1Sign(msg, new BigInteger(hexPrivKey, 10));
Boolean result = DataToolUtils.secp256k1VerifySignature(msg, sig, keyPair.getPublicKey());
Assert.assertTrue(result);
}
}

0 comments on commit 19b6322

Please sign in to comment.