From 326145a62b129fe295d48d9fde04ecd78c3ff206 Mon Sep 17 00:00:00 2001 From: cyjseagull Date: Thu, 16 Jul 2020 15:25:16 +0800 Subject: [PATCH] add CryptoInterface to init and implement basic crypto functions by globalConfig --- build.gradle | 2 +- .../bcos/sdk/crypto/CryptoInterface.java | 105 ++++++++++++++++++ .../sdk/crypto/keypair/CryptoKeyPair.java | 30 ++++- .../bcos/sdk/crypto/keypair/ECDSAKeyPair.java | 10 ++ .../bcos/sdk/crypto/keypair/SM2KeyPair.java | 10 ++ .../UnsupportedCryptoTypeException.java | 25 +++++ .../fisco/bcos/sdk/test/crypto/HashTest.java | 63 ++++++++++- .../bcos/sdk/test/crypto/SignatureTest.java | 59 ++++++++++ 8 files changed, 298 insertions(+), 6 deletions(-) create mode 100644 src/main/java/org/fisco/bcos/sdk/crypto/CryptoInterface.java create mode 100644 src/main/java/org/fisco/bcos/sdk/exceptions/UnsupportedCryptoTypeException.java diff --git a/build.gradle b/build.gradle index 56614ec29..3880ac1ec 100644 --- a/build.gradle +++ b/build.gradle @@ -54,6 +54,7 @@ jacocoTestReport { } dependencies { + compile 'org.bouncycastle:bcprov-jdk15on:1.60' compile 'org.apache.commons:commons-lang3:3.1' compile 'io.netty:netty-all:4.1.50.Final' compile 'com.fasterxml.jackson.core:jackson-databind:2.11.0' @@ -64,7 +65,6 @@ dependencies { testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:2.23.0' - } archivesBaseName = 'java-sdk' diff --git a/src/main/java/org/fisco/bcos/sdk/crypto/CryptoInterface.java b/src/main/java/org/fisco/bcos/sdk/crypto/CryptoInterface.java new file mode 100644 index 000000000..7dc5a6f19 --- /dev/null +++ b/src/main/java/org/fisco/bcos/sdk/crypto/CryptoInterface.java @@ -0,0 +1,105 @@ +/** + * Copyright 2014-2020 [fisco-dev] + * + *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.fisco.bcos.sdk.crypto; + +import java.security.KeyPair; +import org.fisco.bcos.sdk.crypto.hash.Hash; +import org.fisco.bcos.sdk.crypto.hash.Keccak256; +import org.fisco.bcos.sdk.crypto.hash.SM3Hash; +import org.fisco.bcos.sdk.crypto.keypair.CryptoKeyPair; +import org.fisco.bcos.sdk.crypto.keypair.ECDSAKeyPair; +import org.fisco.bcos.sdk.crypto.keypair.SM2KeyPair; +import org.fisco.bcos.sdk.crypto.signature.ECDSASignature; +import org.fisco.bcos.sdk.crypto.signature.SM2Signature; +import org.fisco.bcos.sdk.crypto.signature.Signature; +import org.fisco.bcos.sdk.crypto.signature.SignatureResult; +import org.fisco.bcos.sdk.exceptions.UnsupportedCryptoTypeException; + +public class CryptoInterface { + + public static final int ECDSA_TYPE = 0; + public static final int SM_TYPE = 1; + public final int cryptoTypeConfig; + + public final Signature signatureImpl; + public final Hash hashImpl; + private final CryptoKeyPair keyPairFactory; + + /** + * init the common crypto implementation accordign to the crypto type + * + * @param cryptoTypeConfig + */ + public CryptoInterface(int cryptoTypeConfig) { + this.cryptoTypeConfig = cryptoTypeConfig; + if (this.cryptoTypeConfig == ECDSA_TYPE) { + this.signatureImpl = new ECDSASignature(); + this.hashImpl = new Keccak256(); + this.keyPairFactory = new ECDSAKeyPair(); + + } else if (this.cryptoTypeConfig == SM_TYPE) { + this.signatureImpl = new SM2Signature(); + this.hashImpl = new SM3Hash(); + this.keyPairFactory = new SM2KeyPair(); + + } else { + throw new UnsupportedCryptoTypeException( + "only support " + ECDSA_TYPE + "/" + SM_TYPE + " crypto type"); + } + } + + public int getCryptoTypeConfig() { + return cryptoTypeConfig; + } + + public Signature getSignatureImpl() { + return signatureImpl; + } + + public Hash getHashImpl() { + return hashImpl; + } + + public String hash(final String inputData) { + return hashImpl.hash(inputData); + } + + public byte[] hash(final byte[] inputBytes) { + return hashImpl.hash(inputBytes); + } + + public SignatureResult sign(final byte[] message, final CryptoKeyPair keyPair) { + return signatureImpl.sign(message, keyPair); + } + + public SignatureResult sign(final String message, final CryptoKeyPair keyPair) { + return signatureImpl.sign(message, keyPair); + } + + public boolean verify(final String publicKey, final String message, final String signature) { + return signatureImpl.verify(publicKey, message, signature); + } + + public boolean verify(final String publicKey, final byte[] message, final byte[] signature) { + return signatureImpl.verify(publicKey, message, signature); + } + + public CryptoKeyPair createKeyPair() { + return this.keyPairFactory.generateKeyPair(); + } + + public CryptoKeyPair createKeyPair(KeyPair keyPair) { + return this.keyPairFactory.createKeyPair(keyPair); + } +} diff --git a/src/main/java/org/fisco/bcos/sdk/crypto/keypair/CryptoKeyPair.java b/src/main/java/org/fisco/bcos/sdk/crypto/keypair/CryptoKeyPair.java index 4c15ab2f9..033355821 100644 --- a/src/main/java/org/fisco/bcos/sdk/crypto/keypair/CryptoKeyPair.java +++ b/src/main/java/org/fisco/bcos/sdk/crypto/keypair/CryptoKeyPair.java @@ -15,7 +15,11 @@ import com.webank.wedpr.crypto.CryptoResult; import java.math.BigInteger; +import java.security.KeyPair; +import java.util.Arrays; import java.util.Objects; +import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; +import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; public abstract class CryptoKeyPair { private BigInteger privateKey; @@ -23,6 +27,7 @@ public abstract class CryptoKeyPair { protected String hexPrivateKey; protected String hexPublicKey; + public KeyPair keyPair; public CryptoKeyPair() {} @@ -32,12 +37,29 @@ public CryptoKeyPair(final BigInteger privateKey) { * todo: get publicKey according to privateKey this.publicKey = * privateKeyToPublic(privateKey); */ + this.keyPair = null; calculateHexedKeyPair(); } public CryptoKeyPair(final BigInteger privateKey, final BigInteger publicKey) { this.privateKey = privateKey; this.publicKey = publicKey; + this.keyPair = null; + calculateHexedKeyPair(); + } + + /** + * init CryptoKeyPair from the keyPair + * + * @param keyPair + */ + public CryptoKeyPair(KeyPair keyPair) { + this.keyPair = keyPair; + // init privateKey/publicKey from the keyPair + this.privateKey = ((BCECPrivateKey) keyPair.getPrivate()).getD(); + byte[] publicKeyBytes = ((BCECPublicKey) keyPair.getPublic()).getQ().getEncoded(false); + this.publicKey = + new BigInteger(1, Arrays.copyOfRange(publicKeyBytes, 1, publicKeyBytes.length)); calculateHexedKeyPair(); } @@ -89,13 +111,15 @@ public String getHexPublicKey() { */ public abstract CryptoKeyPair generateKeyPair(); + public abstract CryptoKeyPair createKeyPair(KeyPair keyPair); + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - CryptoKeyPair keyPair = (CryptoKeyPair) o; - return Objects.equals(privateKey, keyPair.privateKey) - && Objects.equals(publicKey, keyPair.publicKey); + CryptoKeyPair comparedKeyPair = (CryptoKeyPair) o; + return Objects.equals(this.privateKey, comparedKeyPair.privateKey) + && Objects.equals(this.publicKey, comparedKeyPair.publicKey); } @Override diff --git a/src/main/java/org/fisco/bcos/sdk/crypto/keypair/ECDSAKeyPair.java b/src/main/java/org/fisco/bcos/sdk/crypto/keypair/ECDSAKeyPair.java index 5f58fb9b5..a47df74f8 100644 --- a/src/main/java/org/fisco/bcos/sdk/crypto/keypair/ECDSAKeyPair.java +++ b/src/main/java/org/fisco/bcos/sdk/crypto/keypair/ECDSAKeyPair.java @@ -16,6 +16,7 @@ import com.webank.wedpr.crypto.CryptoResult; import com.webank.wedpr.crypto.NativeInterface; import java.math.BigInteger; +import java.security.KeyPair; public class ECDSAKeyPair extends CryptoKeyPair { @@ -29,6 +30,10 @@ public ECDSAKeyPair(final BigInteger privateKey, final BigInteger publicKey) { super(privateKey, publicKey); } + public ECDSAKeyPair(KeyPair javaKeyPair) { + super(javaKeyPair); + } + protected ECDSAKeyPair(final CryptoResult ecKeyPairInfo) { super(ecKeyPairInfo); } @@ -42,4 +47,9 @@ protected ECDSAKeyPair(final CryptoResult ecKeyPairInfo) { public CryptoKeyPair generateKeyPair() { return new ECDSAKeyPair(NativeInterface.secp256k1keyPair()); } + + @Override + public CryptoKeyPair createKeyPair(KeyPair javaKeyPair) { + return new ECDSAKeyPair(javaKeyPair); + } } diff --git a/src/main/java/org/fisco/bcos/sdk/crypto/keypair/SM2KeyPair.java b/src/main/java/org/fisco/bcos/sdk/crypto/keypair/SM2KeyPair.java index 41b86558d..734340554 100644 --- a/src/main/java/org/fisco/bcos/sdk/crypto/keypair/SM2KeyPair.java +++ b/src/main/java/org/fisco/bcos/sdk/crypto/keypair/SM2KeyPair.java @@ -15,10 +15,15 @@ import com.webank.wedpr.crypto.CryptoResult; import com.webank.wedpr.crypto.NativeInterface; +import java.security.KeyPair; public class SM2KeyPair extends CryptoKeyPair { public SM2KeyPair() {} + public SM2KeyPair(KeyPair javaKeyPair) { + super(javaKeyPair); + } + protected SM2KeyPair(CryptoResult sm2keyPairInfo) { super(sm2keyPairInfo); } @@ -32,4 +37,9 @@ protected SM2KeyPair(CryptoResult sm2keyPairInfo) { public CryptoKeyPair generateKeyPair() { return new SM2KeyPair(NativeInterface.sm2keyPair()); } + + @Override + public CryptoKeyPair createKeyPair(KeyPair javaKeyPair) { + return new SM2KeyPair(javaKeyPair); + } } diff --git a/src/main/java/org/fisco/bcos/sdk/exceptions/UnsupportedCryptoTypeException.java b/src/main/java/org/fisco/bcos/sdk/exceptions/UnsupportedCryptoTypeException.java new file mode 100644 index 000000000..88aa6c220 --- /dev/null +++ b/src/main/java/org/fisco/bcos/sdk/exceptions/UnsupportedCryptoTypeException.java @@ -0,0 +1,25 @@ +/** + * Copyright 2014-2020 [fisco-dev] + * + *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.fisco.bcos.sdk.exceptions; + +/** Exceptioned when calling CryptoInterface. */ +public class UnsupportedCryptoTypeException extends RuntimeException { + public UnsupportedCryptoTypeException(String message) { + super(message); + } + + public UnsupportedCryptoTypeException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/test/java/org/fisco/bcos/sdk/test/crypto/HashTest.java b/src/test/java/org/fisco/bcos/sdk/test/crypto/HashTest.java index a10cfb4c2..a581d19a3 100644 --- a/src/test/java/org/fisco/bcos/sdk/test/crypto/HashTest.java +++ b/src/test/java/org/fisco/bcos/sdk/test/crypto/HashTest.java @@ -13,14 +13,65 @@ */ package org.fisco.bcos.sdk.test.crypto; +import org.fisco.bcos.sdk.crypto.CryptoInterface; import org.fisco.bcos.sdk.crypto.hash.Hash; import org.fisco.bcos.sdk.crypto.hash.Keccak256; import org.fisco.bcos.sdk.crypto.hash.SM3Hash; +import org.fisco.bcos.sdk.exceptions.UnsupportedCryptoTypeException; import org.fisco.bcos.sdk.utils.Hex; import org.junit.Assert; import org.junit.Test; public class HashTest { + @Test + public void testCryptoInterfaceForSMHash() { + CryptoInterface cryptoInterface = new CryptoInterface(CryptoInterface.SM_TYPE); + // check sm3 hash for "abcde" + checkHashWithCryptoInterface( + cryptoInterface, + "abcde", + "afe4ccac5ab7d52bcae36373676215368baf52d3905e1fecbe369cc120e97628"); + + // check sm3 hash for "hello" + checkHashWithCryptoInterface( + cryptoInterface, + "hello", + "becbbfaae6548b8bf0cfcad5a27183cd1be6093b1cceccc303d9c61d0a645268"); + + // check sm3 hash for empty string + checkHashWithCryptoInterface( + cryptoInterface, + "", + "1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b"); + } + + @Test + public void testCryptoInterfaceForKeccak256Hash() { + CryptoInterface cryptoInterface = new CryptoInterface(CryptoInterface.ECDSA_TYPE); + // check keccak256 for "abcde" + checkHashWithCryptoInterface( + cryptoInterface, + "abcde", + "6377c7e66081cb65e473c1b95db5195a27d04a7108b468890224bedbe1a8a6eb"); + + // check keccak256 for "hello" + checkHashWithCryptoInterface( + cryptoInterface, + "hello", + "1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8"); + + // check keccak256 for empty string + checkHashWithCryptoInterface( + cryptoInterface, + "", + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); + } + + @Test(expected = UnsupportedCryptoTypeException.class) + public void testUnsupportedCryptoType() { + CryptoInterface cryptoInterface = new CryptoInterface(3); + } + @Test public void testKeccak256() { Hash hasher = new Keccak256(); @@ -70,8 +121,16 @@ private void testSM3(Hash hasher) { private void checkHash(Hash hasher, String message, String expectedHash) { String calculatedHash = hasher.hash(message); - Assert.assertTrue(calculatedHash.equals(expectedHash)); + Assert.assertEquals(true, calculatedHash.equals(expectedHash)); byte[] calculatedHashBytes = hasher.hash(message.getBytes()); - Assert.assertTrue(Hex.toHexString(calculatedHashBytes).equals(expectedHash)); + Assert.assertEquals(true, Hex.toHexString(calculatedHashBytes).equals(expectedHash)); + } + + private void checkHashWithCryptoInterface( + CryptoInterface cryptoInterface, String message, String expectedHash) { + String calculatedHash = cryptoInterface.hash(message); + Assert.assertEquals(true, calculatedHash.equals(expectedHash)); + byte[] calculatedHashBytes = cryptoInterface.hash(message.getBytes()); + Assert.assertEquals(true, Hex.toHexString(calculatedHashBytes).equals(expectedHash)); } } diff --git a/src/test/java/org/fisco/bcos/sdk/test/crypto/SignatureTest.java b/src/test/java/org/fisco/bcos/sdk/test/crypto/SignatureTest.java index cb67f18ce..c6162309f 100644 --- a/src/test/java/org/fisco/bcos/sdk/test/crypto/SignatureTest.java +++ b/src/test/java/org/fisco/bcos/sdk/test/crypto/SignatureTest.java @@ -13,6 +13,7 @@ */ package org.fisco.bcos.sdk.test.crypto; +import org.fisco.bcos.sdk.crypto.CryptoInterface; import org.fisco.bcos.sdk.crypto.keypair.CryptoKeyPair; import org.fisco.bcos.sdk.crypto.keypair.ECDSAKeyPair; import org.fisco.bcos.sdk.crypto.keypair.SM2KeyPair; @@ -24,6 +25,24 @@ import org.junit.Test; public class SignatureTest { + @Test + public void testCryptoInterfaceForECDSA() { + CryptoInterface cryptoInterface = new CryptoInterface(CryptoInterface.ECDSA_TYPE); + // generate keyPair + CryptoKeyPair keyPair = cryptoInterface.createKeyPair(); + // test signature + testSignature(cryptoInterface, keyPair); + } + + @Test + public void testCryptoInterfaceForSM2() { + CryptoInterface cryptoInterface = new CryptoInterface(CryptoInterface.SM_TYPE); + // generate keyPair + CryptoKeyPair keyPair = cryptoInterface.createKeyPair(); + // test signature + testSignature(cryptoInterface, keyPair); + } + @Test public void testECDSASignature() { Signature ecdsaSignature = new ECDSASignature(); @@ -77,4 +96,44 @@ private void testSignature(Signature signature, CryptoKeyPair keyPair) { signResult.convertToString())); } } + + private void testSignature(CryptoInterface signature, CryptoKeyPair keyPair) { + String message = "abcde"; + // check valid case + for (int i = 0; i < 10; i++) { + message = "abcd----" + Integer.toString(i); + // sign + SignatureResult signResult = signature.sign(message, keyPair); + // verify + Assert.assertTrue( + signature.verify( + keyPair.getHexPublicKey(), message, signResult.convertToString())); + signResult = signature.sign(message.getBytes(), keyPair); + Assert.assertTrue( + signature.verify( + keyPair.getHexPublicKey(), message, signResult.convertToString())); + } + + // check invalid case + for (int i = 0; i < 10; i++) { + message = "abcd----" + Integer.toString(i); + String invaidMessage = "abcd---" + Integer.toString(i + 1); + // sign + SignatureResult signResult = signature.sign(message, keyPair); + // verify + Assert.assertEquals( + false, + signature.verify( + keyPair.getHexPublicKey(), + invaidMessage, + signResult.convertToString())); + signResult = signature.sign(message.getBytes(), keyPair); + Assert.assertEquals( + false, + signature.verify( + keyPair.getHexPublicKey(), + invaidMessage, + signResult.convertToString())); + } + } }