From 252a4ea1fe8e1fbd01d68aaa3912270aaa948155 Mon Sep 17 00:00:00 2001 From: cyjseagull Date: Thu, 16 Jul 2020 20:08:49 +0800 Subject: [PATCH] Supplement the remaining functions of the crypto module: 1. support load key-pair from the p12/pem files 2. add getAddress to CryptoKeyPair 3. Place excepito in the corresponding module --- .../bcos/sdk/crypto/CryptoInterface.java | 2 +- .../exceptions/HashException.java | 2 +- .../crypto/exceptions/KeyPairException.java | 25 +++ .../exceptions/LoadKeyStoreException.java | 25 +++ .../exceptions/SignatureException.java | 2 +- .../UnsupportedCryptoTypeException.java | 2 +- .../fisco/bcos/sdk/crypto/hash/Keccak256.java | 2 +- .../fisco/bcos/sdk/crypto/hash/SM3Hash.java | 2 +- .../sdk/crypto/keypair/CryptoKeyPair.java | 113 ++++++++--- .../bcos/sdk/crypto/keypair/ECDSAKeyPair.java | 9 +- .../bcos/sdk/crypto/keypair/SM2KeyPair.java | 7 +- .../bcos/sdk/crypto/keystore/KeyManager.java | 172 +++++++++++++++++ .../bcos/sdk/crypto/keystore/P12Manager.java | 78 ++++++++ .../bcos/sdk/crypto/keystore/PEMManager.java | 73 +++++++ .../sdk/crypto/signature/ECDSASignature.java | 2 +- .../signature/ECDSASignatureResult.java | 2 +- .../sdk/crypto/signature/SM2Signature.java | 2 +- .../sdk/crypto/signature/SignatureResult.java | 2 +- .../org/fisco/bcos/sdk/rlp/RlpDecoder.java | 2 +- .../java/org/fisco/bcos/sdk/utils/Hex.java | 4 +- .../org/fisco/bcos/sdk/utils/Numeric.java | 4 +- .../exceptions/DecoderException.java | 2 +- .../exceptions/EncoderException.java | 2 +- .../exceptions/MessageDecodingException.java | 2 +- .../exceptions/MessageEncodingException.java | 2 +- .../fisco/bcos/sdk/test/crypto/HashTest.java | 2 +- .../bcos/sdk/test/crypto/KeyManagerTest.java | 181 ++++++++++++++++++ .../bcos/sdk/test/crypto/SignatureTest.java | 125 +++++++++++- ...c3c4bb89bd90299db4c62be0174c4966286c00.pem | 5 + ...e14c53197adbcb719d915fb93342c25600faaf.p12 | Bin 0 -> 321 bytes src/test/resources/keystore/ecdsa/invalid.p12 | Bin 0 -> 313 bytes src/test/resources/keystore/ecdsa/invalid.pem | 5 + ...b3558746e8f9a47a474774e8c4a9e67d4e3174.pem | 5 + ...68461309925093236df82b51df630a55d32377.p12 | Bin 0 -> 329 bytes src/test/resources/keystore/gm/invalid.p12 | Bin 0 -> 324 bytes src/test/resources/keystore/gm/invalid.pem | 5 + 36 files changed, 821 insertions(+), 47 deletions(-) rename src/main/java/org/fisco/bcos/sdk/{ => crypto}/exceptions/HashException.java (94%) create mode 100644 src/main/java/org/fisco/bcos/sdk/crypto/exceptions/KeyPairException.java create mode 100644 src/main/java/org/fisco/bcos/sdk/crypto/exceptions/LoadKeyStoreException.java rename src/main/java/org/fisco/bcos/sdk/{ => crypto}/exceptions/SignatureException.java (95%) rename src/main/java/org/fisco/bcos/sdk/{ => crypto}/exceptions/UnsupportedCryptoTypeException.java (95%) create mode 100644 src/main/java/org/fisco/bcos/sdk/crypto/keystore/KeyManager.java create mode 100644 src/main/java/org/fisco/bcos/sdk/crypto/keystore/P12Manager.java create mode 100644 src/main/java/org/fisco/bcos/sdk/crypto/keystore/PEMManager.java rename src/main/java/org/fisco/bcos/sdk/{ => utils}/exceptions/DecoderException.java (95%) rename src/main/java/org/fisco/bcos/sdk/{ => utils}/exceptions/EncoderException.java (95%) rename src/main/java/org/fisco/bcos/sdk/{ => utils}/exceptions/MessageDecodingException.java (95%) rename src/main/java/org/fisco/bcos/sdk/{ => utils}/exceptions/MessageEncodingException.java (95%) create mode 100644 src/test/java/org/fisco/bcos/sdk/test/crypto/KeyManagerTest.java create mode 100644 src/test/resources/keystore/ecdsa/0x0fc3c4bb89bd90299db4c62be0174c4966286c00.pem create mode 100644 src/test/resources/keystore/ecdsa/0x45e14c53197adbcb719d915fb93342c25600faaf.p12 create mode 100644 src/test/resources/keystore/ecdsa/invalid.p12 create mode 100644 src/test/resources/keystore/ecdsa/invalid.pem create mode 100644 src/test/resources/keystore/gm/0x40b3558746e8f9a47a474774e8c4a9e67d4e3174.pem create mode 100644 src/test/resources/keystore/gm/0x6f68461309925093236df82b51df630a55d32377.p12 create mode 100644 src/test/resources/keystore/gm/invalid.p12 create mode 100644 src/test/resources/keystore/gm/invalid.pem diff --git a/src/main/java/org/fisco/bcos/sdk/crypto/CryptoInterface.java b/src/main/java/org/fisco/bcos/sdk/crypto/CryptoInterface.java index 7dc5a6f19..7859b324a 100644 --- a/src/main/java/org/fisco/bcos/sdk/crypto/CryptoInterface.java +++ b/src/main/java/org/fisco/bcos/sdk/crypto/CryptoInterface.java @@ -14,6 +14,7 @@ package org.fisco.bcos.sdk.crypto; import java.security.KeyPair; +import org.fisco.bcos.sdk.crypto.exceptions.UnsupportedCryptoTypeException; import org.fisco.bcos.sdk.crypto.hash.Hash; import org.fisco.bcos.sdk.crypto.hash.Keccak256; import org.fisco.bcos.sdk.crypto.hash.SM3Hash; @@ -24,7 +25,6 @@ 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 { diff --git a/src/main/java/org/fisco/bcos/sdk/exceptions/HashException.java b/src/main/java/org/fisco/bcos/sdk/crypto/exceptions/HashException.java similarity index 94% rename from src/main/java/org/fisco/bcos/sdk/exceptions/HashException.java rename to src/main/java/org/fisco/bcos/sdk/crypto/exceptions/HashException.java index 3ddcc2763..dc1172fb4 100644 --- a/src/main/java/org/fisco/bcos/sdk/exceptions/HashException.java +++ b/src/main/java/org/fisco/bcos/sdk/crypto/exceptions/HashException.java @@ -11,7 +11,7 @@ * express or implied. See the License for the specific language governing permissions and * limitations under the License. */ -package org.fisco.bcos.sdk.exceptions; +package org.fisco.bcos.sdk.crypto.exceptions; /** Exceptioned when calling hash. */ public class HashException extends RuntimeException { diff --git a/src/main/java/org/fisco/bcos/sdk/crypto/exceptions/KeyPairException.java b/src/main/java/org/fisco/bcos/sdk/crypto/exceptions/KeyPairException.java new file mode 100644 index 000000000..9f56dafb2 --- /dev/null +++ b/src/main/java/org/fisco/bcos/sdk/crypto/exceptions/KeyPairException.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.crypto.exceptions; + +/** Exceptioned when calling hash. */ +public class KeyPairException extends RuntimeException { + public KeyPairException(String message) { + super(message); + } + + public KeyPairException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/fisco/bcos/sdk/crypto/exceptions/LoadKeyStoreException.java b/src/main/java/org/fisco/bcos/sdk/crypto/exceptions/LoadKeyStoreException.java new file mode 100644 index 000000000..078400fa9 --- /dev/null +++ b/src/main/java/org/fisco/bcos/sdk/crypto/exceptions/LoadKeyStoreException.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.crypto.exceptions; + +/** Exceptioned when calling KeyManager. */ +public class LoadKeyStoreException extends RuntimeException { + public LoadKeyStoreException(String message) { + super(message); + } + + public LoadKeyStoreException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/fisco/bcos/sdk/exceptions/SignatureException.java b/src/main/java/org/fisco/bcos/sdk/crypto/exceptions/SignatureException.java similarity index 95% rename from src/main/java/org/fisco/bcos/sdk/exceptions/SignatureException.java rename to src/main/java/org/fisco/bcos/sdk/crypto/exceptions/SignatureException.java index 7a57f0850..e97ec8d58 100644 --- a/src/main/java/org/fisco/bcos/sdk/exceptions/SignatureException.java +++ b/src/main/java/org/fisco/bcos/sdk/crypto/exceptions/SignatureException.java @@ -11,7 +11,7 @@ * express or implied. See the License for the specific language governing permissions and * limitations under the License. */ -package org.fisco.bcos.sdk.exceptions; +package org.fisco.bcos.sdk.crypto.exceptions; /** Exceptioned when calling signature related functions. */ public class SignatureException extends RuntimeException { diff --git a/src/main/java/org/fisco/bcos/sdk/exceptions/UnsupportedCryptoTypeException.java b/src/main/java/org/fisco/bcos/sdk/crypto/exceptions/UnsupportedCryptoTypeException.java similarity index 95% rename from src/main/java/org/fisco/bcos/sdk/exceptions/UnsupportedCryptoTypeException.java rename to src/main/java/org/fisco/bcos/sdk/crypto/exceptions/UnsupportedCryptoTypeException.java index 88aa6c220..c52382fdd 100644 --- a/src/main/java/org/fisco/bcos/sdk/exceptions/UnsupportedCryptoTypeException.java +++ b/src/main/java/org/fisco/bcos/sdk/crypto/exceptions/UnsupportedCryptoTypeException.java @@ -11,7 +11,7 @@ * express or implied. See the License for the specific language governing permissions and * limitations under the License. */ -package org.fisco.bcos.sdk.exceptions; +package org.fisco.bcos.sdk.crypto.exceptions; /** Exceptioned when calling CryptoInterface. */ public class UnsupportedCryptoTypeException extends RuntimeException { diff --git a/src/main/java/org/fisco/bcos/sdk/crypto/hash/Keccak256.java b/src/main/java/org/fisco/bcos/sdk/crypto/hash/Keccak256.java index 65151a9bb..a5643120f 100644 --- a/src/main/java/org/fisco/bcos/sdk/crypto/hash/Keccak256.java +++ b/src/main/java/org/fisco/bcos/sdk/crypto/hash/Keccak256.java @@ -15,7 +15,7 @@ import com.webank.wedpr.crypto.CryptoResult; import com.webank.wedpr.crypto.NativeInterface; -import org.fisco.bcos.sdk.exceptions.HashException; +import org.fisco.bcos.sdk.crypto.exceptions.HashException; import org.fisco.bcos.sdk.utils.Hex; public class Keccak256 implements Hash { diff --git a/src/main/java/org/fisco/bcos/sdk/crypto/hash/SM3Hash.java b/src/main/java/org/fisco/bcos/sdk/crypto/hash/SM3Hash.java index 2cdcc9136..cbc596b40 100644 --- a/src/main/java/org/fisco/bcos/sdk/crypto/hash/SM3Hash.java +++ b/src/main/java/org/fisco/bcos/sdk/crypto/hash/SM3Hash.java @@ -15,7 +15,7 @@ import com.webank.wedpr.crypto.CryptoResult; import com.webank.wedpr.crypto.NativeInterface; -import org.fisco.bcos.sdk.exceptions.HashException; +import org.fisco.bcos.sdk.crypto.exceptions.HashException; import org.fisco.bcos.sdk.utils.Hex; public class SM3Hash implements Hash { 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 033355821..7e32f44d0 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 @@ -20,8 +20,20 @@ import java.util.Objects; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; +import org.fisco.bcos.sdk.crypto.exceptions.KeyPairException; +import org.fisco.bcos.sdk.crypto.hash.Hash; +import org.fisco.bcos.sdk.utils.Hex; +import org.fisco.bcos.sdk.utils.Numeric; +import org.fisco.bcos.sdk.utils.Strings; +import org.fisco.bcos.sdk.utils.exceptions.DecoderException; public abstract class CryptoKeyPair { + public static final int ADDRESS_SIZE = 160; + public static final int ADDRESS_LENGTH_IN_HEX = ADDRESS_SIZE >> 2; + + public static final int PUBLIC_KEY_SIZE = 64; + public static final int PUBLIC_KEY_LENGTH_IN_HEX = PUBLIC_KEY_SIZE << 1; + private BigInteger privateKey; protected BigInteger publicKey; @@ -29,6 +41,8 @@ public abstract class CryptoKeyPair { protected String hexPublicKey; public KeyPair keyPair; + protected Hash hashImpl; + public CryptoKeyPair() {} public CryptoKeyPair(final BigInteger privateKey) { @@ -61,13 +75,10 @@ public CryptoKeyPair(KeyPair keyPair) { this.publicKey = new BigInteger(1, Arrays.copyOfRange(publicKeyBytes, 1, publicKeyBytes.length)); calculateHexedKeyPair(); + // Note: In the current version of sm2 verification, the public key prefix must contain 04, + // otherwise an error will be reported + this.hexPublicKey = "04" + this.hexPublicKey; } - - private void calculateHexedKeyPair() { - this.hexPrivateKey = this.privateKey.toString(16); - this.hexPublicKey = this.publicKey.toString(16); - } - /** * get CryptoKeyPair information from CryptoResult * @@ -77,7 +88,14 @@ private void calculateHexedKeyPair() { this.hexPrivateKey = nativeResult.privteKey; this.hexPublicKey = nativeResult.publicKey; this.privateKey = new BigInteger(this.hexPrivateKey, 16); - this.publicKey = new BigInteger(this.hexPublicKey, 16); + // Note: The generated publicKey is prefixed with 04, When converting it to BigInteger, need + // to remove 04 + this.publicKey = new BigInteger(this.hexPublicKey.substring(2), 16); + } + + private void calculateHexedKeyPair() { + this.hexPrivateKey = this.privateKey.toString(16); + this.hexPublicKey = this.publicKey.toString(16); } public BigInteger getPrivateKey() { @@ -96,22 +114,9 @@ public String getHexPublicKey() { return hexPublicKey; } - /** - * todo: get the public key from the given private key - * - * @param privateKey - * @return: the public key calculated from the private key public abstract BigInteger - * privateKeyToPublic(BigInteger privateKey); - */ - - /** - * generate keyPair randomly - * - * @return: the generated keyPair - */ - public abstract CryptoKeyPair generateKeyPair(); - - public abstract CryptoKeyPair createKeyPair(KeyPair keyPair); + public KeyPair getKeyPair() { + return this.keyPair; + } @Override public boolean equals(Object o) { @@ -126,4 +131,66 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(privateKey, publicKey); } + + /** + * generate keyPair randomly + * + * @return: the generated keyPair + */ + public abstract CryptoKeyPair generateKeyPair(); + + public abstract CryptoKeyPair createKeyPair(KeyPair keyPair); + + protected String getPublicKeyNoPrefix(String publicKeyStr) { + String publicKeyNoPrefix = Numeric.cleanHexPrefix(publicKeyStr); + // Hexadecimal public key length is less than 128, add 0 in front + if (publicKeyNoPrefix.length() < PUBLIC_KEY_LENGTH_IN_HEX) { + publicKeyNoPrefix = + Strings.zeros(PUBLIC_KEY_LENGTH_IN_HEX - publicKeyNoPrefix.length()) + + publicKeyNoPrefix; + } + return publicKeyNoPrefix; + } + /** + * get the address according to the public key + * + * @return: the hexed address calculated from the publicKey + */ + public String getAddress() { + // Note: The generated publicKey is prefixed with 04, When calculate the address, need to + // remove 04 + return getAddress(this.getHexPublicKey().substring(2)); + } + /** + * calculate the address according to the given public key + * + * @param publicKey: the Hexed publicKey that need to calculate address + * @return + */ + public String getAddress(String publicKey) { + try { + String publicKeyNoPrefix = getPublicKeyNoPrefix(publicKey); + // calculate hash for the public key + String publicKeyHash = Hex.toHexString(hashImpl.hash(Hex.decode(publicKeyNoPrefix))); + // right most 160 bits + return publicKeyHash.substring(publicKeyHash.length() - ADDRESS_LENGTH_IN_HEX); + } catch (DecoderException e) { + throw new KeyPairException( + "getAddress for " + + publicKey + + "failed, the publicKey param must be hex string, error message: " + + e.getMessage(), + e); + } + } + + public byte[] getAddress(byte[] publicKey) { + byte[] hash = hashImpl.hash(publicKey); + return Arrays.copyOfRange(hash, hash.length - 20, hash.length); // right most 160 bits + } + + public byte[] getAddress(BigInteger publicKey) { + byte[] publicKeyBytes = Numeric.toBytesPadded(publicKey, PUBLIC_KEY_SIZE); + return getAddress(publicKeyBytes); + } } 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 a47df74f8..d8e8d7a81 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 @@ -17,25 +17,32 @@ import com.webank.wedpr.crypto.NativeInterface; import java.math.BigInteger; import java.security.KeyPair; +import org.fisco.bcos.sdk.crypto.hash.Keccak256; public class ECDSAKeyPair extends CryptoKeyPair { - public ECDSAKeyPair() {} + public ECDSAKeyPair() { + hashImpl = new Keccak256(); + } public ECDSAKeyPair(BigInteger privateKey) { super(privateKey); + hashImpl = new Keccak256(); } public ECDSAKeyPair(final BigInteger privateKey, final BigInteger publicKey) { super(privateKey, publicKey); + hashImpl = new Keccak256(); } public ECDSAKeyPair(KeyPair javaKeyPair) { super(javaKeyPair); + hashImpl = new Keccak256(); } protected ECDSAKeyPair(final CryptoResult ecKeyPairInfo) { super(ecKeyPairInfo); + hashImpl = new Keccak256(); } /** 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 734340554..b29214fff 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 @@ -16,16 +16,21 @@ import com.webank.wedpr.crypto.CryptoResult; import com.webank.wedpr.crypto.NativeInterface; import java.security.KeyPair; +import org.fisco.bcos.sdk.crypto.hash.SM3Hash; public class SM2KeyPair extends CryptoKeyPair { - public SM2KeyPair() {} + public SM2KeyPair() { + hashImpl = new SM3Hash(); + } public SM2KeyPair(KeyPair javaKeyPair) { super(javaKeyPair); + hashImpl = new SM3Hash(); } protected SM2KeyPair(CryptoResult sm2keyPairInfo) { super(sm2keyPairInfo); + hashImpl = new SM3Hash(); } /** diff --git a/src/main/java/org/fisco/bcos/sdk/crypto/keystore/KeyManager.java b/src/main/java/org/fisco/bcos/sdk/crypto/keystore/KeyManager.java new file mode 100644 index 000000000..2052777e3 --- /dev/null +++ b/src/main/java/org/fisco/bcos/sdk/crypto/keystore/KeyManager.java @@ -0,0 +1,172 @@ +/** + * 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.keystore; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Security; +import java.security.cert.CertificateException; +import java.security.interfaces.ECPrivateKey; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.util.Collections; +import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util; +import org.bouncycastle.jce.ECNamedCurveTable; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec; +import org.bouncycastle.jce.spec.ECNamedCurveSpec; +import org.fisco.bcos.sdk.crypto.exceptions.LoadKeyStoreException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public abstract class KeyManager { + protected static Logger logger = LoggerFactory.getLogger(KeyManager.class); + + protected final String keyStoreFile; + protected final String password; + protected KeyStore keyStore; + + /** + * constructor for the P12: with password + * + * @param keyStoreFile: the path of the keystore file + * @param password: password to read the keystore file + */ + public KeyManager(final String keyStoreFile, final String password) { + this.keyStoreFile = keyStoreFile; + this.password = password; + Security.setProperty("crypto.policy", "unlimited"); + Security.addProvider(new BouncyCastleProvider()); + load(); + } + + /** + * constructor for PEM: without password + * + * @param keyStoreFile:the path of the keystore file + */ + public KeyManager(final String keyStoreFile) { + this.keyStoreFile = keyStoreFile; + this.password = null; + Security.setProperty("crypto.policy", "unlimited"); + Security.addProvider(new BouncyCastleProvider()); + load(); + } + + protected abstract PrivateKey getPrivateKey(); + /** + * load information from the given input stream + * + * @param in: the input stream that should used to load keyPair + * @param password: the password to load the keyPair + * @throws NoSuchAlgorithmException + * @throws CertificateException + * @throws IOException + * @throws KeyStoreException + * @throws NoSuchProviderException + */ + public final String getKeyStoreFile() { + return this.keyStoreFile; + } + + protected abstract void load(InputStream in); + /** load information from the keyStoreFile */ + protected void load() { + try { + InputStream keyStoreFileInputStream = new FileInputStream(keyStoreFile); + this.load(keyStoreFileInputStream); + } catch (FileNotFoundException | org.bouncycastle.util.encoders.DecoderException e) { + String errorMessage = + "load keys from " + + keyStoreFile + + "failed for FileNotFoundException, error message:" + + e.getMessage(); + logger.error(errorMessage); + throw new LoadKeyStoreException(errorMessage, e); + } + } + + protected PublicKey getPublicKey() { + try { + ECPrivateKey privateKey = (ECPrivateKey) getPrivateKey(); + ECParameterSpec params = privateKey.getParams(); + + org.bouncycastle.jce.spec.ECParameterSpec bcSpec = EC5Util.convertSpec(params, false); + org.bouncycastle.math.ec.ECPoint q = bcSpec.getG().multiply(privateKey.getS()); + org.bouncycastle.math.ec.ECPoint bcW = + bcSpec.getCurve().decodePoint(q.getEncoded(false)); + ECPoint w = + new ECPoint( + bcW.getAffineXCoord().toBigInteger(), + bcW.getAffineYCoord().toBigInteger()); + ECPublicKeySpec keySpec = new ECPublicKeySpec(w, tryFindNamedCurveSpec(params)); + return (PublicKey) + KeyFactory.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME) + .generatePublic(keySpec); + } catch (NoSuchAlgorithmException | InvalidKeySpecException | NoSuchProviderException e) { + String errorMessage = + "get publicKey from " + + keyStoreFile + + "failed, error message:" + + e.getMessage(); + logger.error(errorMessage); + throw new LoadKeyStoreException(errorMessage, e); + } + } + + /** + * get keyPair loaded from the keyStore file + * + * @return: the keyPair + */ + public KeyPair getKeyPair() { + PrivateKey privateKey = getPrivateKey(); + PublicKey publicKey = getPublicKey(); + return new KeyPair(publicKey, privateKey); + } + + @SuppressWarnings("unchecked") + private static ECParameterSpec tryFindNamedCurveSpec(ECParameterSpec params) { + org.bouncycastle.jce.spec.ECParameterSpec bcSpec = EC5Util.convertSpec(params, false); + for (Object name : Collections.list(ECNamedCurveTable.getNames())) { + ECNamedCurveParameterSpec bcNamedSpec = + ECNamedCurveTable.getParameterSpec((String) name); + if (bcNamedSpec.getN().equals(bcSpec.getN()) + && bcNamedSpec.getH().equals(bcSpec.getH()) + && bcNamedSpec.getCurve().equals(bcSpec.getCurve()) + && bcNamedSpec.getG().equals(bcSpec.getG())) { + return new ECNamedCurveSpec( + bcNamedSpec.getName(), + bcNamedSpec.getCurve(), + bcNamedSpec.getG(), + bcNamedSpec.getN(), + bcNamedSpec.getH(), + bcNamedSpec.getSeed()); + } + } + return params; + } +} diff --git a/src/main/java/org/fisco/bcos/sdk/crypto/keystore/P12Manager.java b/src/main/java/org/fisco/bcos/sdk/crypto/keystore/P12Manager.java new file mode 100644 index 000000000..0c1e15388 --- /dev/null +++ b/src/main/java/org/fisco/bcos/sdk/crypto/keystore/P12Manager.java @@ -0,0 +1,78 @@ +/** + * 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.keystore; + +import java.io.IOException; +import java.io.InputStream; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import org.fisco.bcos.sdk.crypto.exceptions.LoadKeyStoreException; + +public class P12Manager extends KeyManager { + private static final String NAME = "key"; + private KeyStore keyStore; + + public P12Manager(final String keyStoreFile, final String password) { + super(keyStoreFile, password); + } + + /** + * load keyPair from the given input stream + * + * @param in: the input stream that should used to load keyPair + * @param password: the password to load the keyPair + */ + protected void load(InputStream in) { + try { + keyStore = KeyStore.getInstance("PKCS12", "BC"); + keyStore.load(in, this.password.toCharArray()); + } catch (IOException + | CertificateException + | NoSuchAlgorithmException + | NoSuchProviderException + | KeyStoreException e) { + String errorMessage = + "load keys from p12 file " + + keyStoreFile + + "failed, error message:" + + e.getMessage(); + logger.error(errorMessage); + throw new LoadKeyStoreException(errorMessage, e); + } + } + + /** + * get private key from the keyStore + * + * @return: the private key + */ + protected PrivateKey getPrivateKey() { + try { + return (PrivateKey) keyStore.getKey(NAME, password.toCharArray()); + } catch (UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException e) { + String errorMessage = + "get private key from " + + keyStoreFile + + "failed for UnrecoverableKeyException, error message" + + e.getMessage(); + logger.error(errorMessage); + throw new LoadKeyStoreException(errorMessage, e); + } + } +} diff --git a/src/main/java/org/fisco/bcos/sdk/crypto/keystore/PEMManager.java b/src/main/java/org/fisco/bcos/sdk/crypto/keystore/PEMManager.java new file mode 100644 index 000000000..b53c2efa6 --- /dev/null +++ b/src/main/java/org/fisco/bcos/sdk/crypto/keystore/PEMManager.java @@ -0,0 +1,73 @@ +/** + * 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.keystore; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.io.pem.PemObject; +import org.bouncycastle.util.io.pem.PemReader; +import org.fisco.bcos.sdk.crypto.exceptions.LoadKeyStoreException; + +public class PEMManager extends KeyManager { + private PemObject pem; + + public PEMManager(final String keyStoreFile) { + super(keyStoreFile); + } + + protected void load(InputStream in) { + try { + PemReader pemReader = new PemReader(new InputStreamReader(in)); + pem = pemReader.readPemObject(); + pemReader.close(); + } catch (IOException e) { + String errorMessage = + "load key info from the pem file " + + keyStoreFile + + "failed, error message:" + + e.getMessage(); + logger.error(errorMessage); + throw new LoadKeyStoreException(errorMessage, e); + } + if (pem == null) { + logger.error("The file " + keyStoreFile + " does not represent a pem account."); + throw new LoadKeyStoreException("The file does not represent a pem account."); + } + } + + protected PrivateKey getPrivateKey() { + try { + PKCS8EncodedKeySpec encodedKeySpec = new PKCS8EncodedKeySpec(pem.getContent()); + KeyFactory keyFacotry = + KeyFactory.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME); + return keyFacotry.generatePrivate(encodedKeySpec); + } catch (InvalidKeySpecException | NoSuchProviderException | NoSuchAlgorithmException e) { + String errorMessage = + "getPrivateKey from pem file " + + keyStoreFile + + "failed, error message:" + + e.getMessage(); + logger.error(errorMessage); + throw new LoadKeyStoreException(errorMessage, e); + } + } +} diff --git a/src/main/java/org/fisco/bcos/sdk/crypto/signature/ECDSASignature.java b/src/main/java/org/fisco/bcos/sdk/crypto/signature/ECDSASignature.java index cf7c35b66..092393243 100644 --- a/src/main/java/org/fisco/bcos/sdk/crypto/signature/ECDSASignature.java +++ b/src/main/java/org/fisco/bcos/sdk/crypto/signature/ECDSASignature.java @@ -14,8 +14,8 @@ package org.fisco.bcos.sdk.crypto.signature; import com.webank.pkeysign.service.ECCSignService; +import org.fisco.bcos.sdk.crypto.exceptions.SignatureException; import org.fisco.bcos.sdk.crypto.keypair.CryptoKeyPair; -import org.fisco.bcos.sdk.exceptions.SignatureException; public class ECDSASignature implements Signature { diff --git a/src/main/java/org/fisco/bcos/sdk/crypto/signature/ECDSASignatureResult.java b/src/main/java/org/fisco/bcos/sdk/crypto/signature/ECDSASignatureResult.java index a41583954..5d29ce306 100644 --- a/src/main/java/org/fisco/bcos/sdk/crypto/signature/ECDSASignatureResult.java +++ b/src/main/java/org/fisco/bcos/sdk/crypto/signature/ECDSASignatureResult.java @@ -15,7 +15,7 @@ import java.util.ArrayList; import java.util.List; -import org.fisco.bcos.sdk.exceptions.SignatureException; +import org.fisco.bcos.sdk.crypto.exceptions.SignatureException; import org.fisco.bcos.sdk.rlp.RlpString; import org.fisco.bcos.sdk.rlp.RlpType; import org.fisco.bcos.sdk.utils.Hex; diff --git a/src/main/java/org/fisco/bcos/sdk/crypto/signature/SM2Signature.java b/src/main/java/org/fisco/bcos/sdk/crypto/signature/SM2Signature.java index 782796b87..8d47771e1 100644 --- a/src/main/java/org/fisco/bcos/sdk/crypto/signature/SM2Signature.java +++ b/src/main/java/org/fisco/bcos/sdk/crypto/signature/SM2Signature.java @@ -14,8 +14,8 @@ package org.fisco.bcos.sdk.crypto.signature; import com.webank.pkeysign.service.SM2SignService; +import org.fisco.bcos.sdk.crypto.exceptions.SignatureException; import org.fisco.bcos.sdk.crypto.keypair.CryptoKeyPair; -import org.fisco.bcos.sdk.exceptions.SignatureException; public class SM2Signature implements Signature { public static final SM2SignService sm2SignService = new SM2SignService(); diff --git a/src/main/java/org/fisco/bcos/sdk/crypto/signature/SignatureResult.java b/src/main/java/org/fisco/bcos/sdk/crypto/signature/SignatureResult.java index 28386f4d3..0c04e9c7f 100644 --- a/src/main/java/org/fisco/bcos/sdk/crypto/signature/SignatureResult.java +++ b/src/main/java/org/fisco/bcos/sdk/crypto/signature/SignatureResult.java @@ -14,7 +14,7 @@ package org.fisco.bcos.sdk.crypto.signature; import java.util.List; -import org.fisco.bcos.sdk.exceptions.SignatureException; +import org.fisco.bcos.sdk.crypto.exceptions.SignatureException; import org.fisco.bcos.sdk.rlp.RlpString; import org.fisco.bcos.sdk.rlp.RlpType; import org.fisco.bcos.sdk.utils.Hex; diff --git a/src/main/java/org/fisco/bcos/sdk/rlp/RlpDecoder.java b/src/main/java/org/fisco/bcos/sdk/rlp/RlpDecoder.java index 99eed5613..39ac1206d 100644 --- a/src/main/java/org/fisco/bcos/sdk/rlp/RlpDecoder.java +++ b/src/main/java/org/fisco/bcos/sdk/rlp/RlpDecoder.java @@ -14,7 +14,7 @@ package org.fisco.bcos.sdk.rlp; import java.util.ArrayList; -import org.fisco.bcos.sdk.exceptions.DecoderException; +import org.fisco.bcos.sdk.utils.exceptions.DecoderException; /** Recursive Length Prefix (RLP) decoder. */ public final class RlpDecoder { diff --git a/src/main/java/org/fisco/bcos/sdk/utils/Hex.java b/src/main/java/org/fisco/bcos/sdk/utils/Hex.java index 05ec69d5a..c59015647 100644 --- a/src/main/java/org/fisco/bcos/sdk/utils/Hex.java +++ b/src/main/java/org/fisco/bcos/sdk/utils/Hex.java @@ -16,8 +16,8 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; -import org.fisco.bcos.sdk.exceptions.DecoderException; -import org.fisco.bcos.sdk.exceptions.EncoderException; +import org.fisco.bcos.sdk.utils.exceptions.DecoderException; +import org.fisco.bcos.sdk.utils.exceptions.EncoderException; /** Utility class for converting hex data to bytes and back again. */ public class Hex { diff --git a/src/main/java/org/fisco/bcos/sdk/utils/Numeric.java b/src/main/java/org/fisco/bcos/sdk/utils/Numeric.java index 03afe5a46..83e2988cb 100644 --- a/src/main/java/org/fisco/bcos/sdk/utils/Numeric.java +++ b/src/main/java/org/fisco/bcos/sdk/utils/Numeric.java @@ -16,8 +16,8 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.util.Arrays; -import org.fisco.bcos.sdk.exceptions.MessageDecodingException; -import org.fisco.bcos.sdk.exceptions.MessageEncodingException; +import org.fisco.bcos.sdk.utils.exceptions.MessageDecodingException; +import org.fisco.bcos.sdk.utils.exceptions.MessageEncodingException; /** Message codec functions. */ public final class Numeric { diff --git a/src/main/java/org/fisco/bcos/sdk/exceptions/DecoderException.java b/src/main/java/org/fisco/bcos/sdk/utils/exceptions/DecoderException.java similarity index 95% rename from src/main/java/org/fisco/bcos/sdk/exceptions/DecoderException.java rename to src/main/java/org/fisco/bcos/sdk/utils/exceptions/DecoderException.java index ad800cbd7..2f358ba3d 100644 --- a/src/main/java/org/fisco/bcos/sdk/exceptions/DecoderException.java +++ b/src/main/java/org/fisco/bcos/sdk/utils/exceptions/DecoderException.java @@ -11,7 +11,7 @@ * express or implied. See the License for the specific language governing permissions and * limitations under the License. */ -package org.fisco.bcos.sdk.exceptions; +package org.fisco.bcos.sdk.utils.exceptions; /** Exception thrown if an attempt is made to decode invalid data, or some other failure occurs. */ public class DecoderException extends IllegalStateException { diff --git a/src/main/java/org/fisco/bcos/sdk/exceptions/EncoderException.java b/src/main/java/org/fisco/bcos/sdk/utils/exceptions/EncoderException.java similarity index 95% rename from src/main/java/org/fisco/bcos/sdk/exceptions/EncoderException.java rename to src/main/java/org/fisco/bcos/sdk/utils/exceptions/EncoderException.java index 304ec40e8..d9f5673ba 100644 --- a/src/main/java/org/fisco/bcos/sdk/exceptions/EncoderException.java +++ b/src/main/java/org/fisco/bcos/sdk/utils/exceptions/EncoderException.java @@ -11,7 +11,7 @@ * express or implied. See the License for the specific language governing permissions and * limitations under the License. */ -package org.fisco.bcos.sdk.exceptions; +package org.fisco.bcos.sdk.utils.exceptions; /** Exception thrown if an attempt is made to encode invalid data, or some other failure occurs. */ public class EncoderException extends IllegalStateException { diff --git a/src/main/java/org/fisco/bcos/sdk/exceptions/MessageDecodingException.java b/src/main/java/org/fisco/bcos/sdk/utils/exceptions/MessageDecodingException.java similarity index 95% rename from src/main/java/org/fisco/bcos/sdk/exceptions/MessageDecodingException.java rename to src/main/java/org/fisco/bcos/sdk/utils/exceptions/MessageDecodingException.java index 17d2b974e..06c190ef3 100644 --- a/src/main/java/org/fisco/bcos/sdk/exceptions/MessageDecodingException.java +++ b/src/main/java/org/fisco/bcos/sdk/utils/exceptions/MessageDecodingException.java @@ -11,7 +11,7 @@ * express or implied. See the License for the specific language governing permissions and * limitations under the License. */ -package org.fisco.bcos.sdk.exceptions; +package org.fisco.bcos.sdk.utils.exceptions; /** Encoding exception. */ public class MessageDecodingException extends RuntimeException { diff --git a/src/main/java/org/fisco/bcos/sdk/exceptions/MessageEncodingException.java b/src/main/java/org/fisco/bcos/sdk/utils/exceptions/MessageEncodingException.java similarity index 95% rename from src/main/java/org/fisco/bcos/sdk/exceptions/MessageEncodingException.java rename to src/main/java/org/fisco/bcos/sdk/utils/exceptions/MessageEncodingException.java index 9742d0af1..3b2534e1f 100644 --- a/src/main/java/org/fisco/bcos/sdk/exceptions/MessageEncodingException.java +++ b/src/main/java/org/fisco/bcos/sdk/utils/exceptions/MessageEncodingException.java @@ -11,7 +11,7 @@ * express or implied. See the License for the specific language governing permissions and * limitations under the License. */ -package org.fisco.bcos.sdk.exceptions; +package org.fisco.bcos.sdk.utils.exceptions; /** Encoding exception. */ public class MessageEncodingException extends RuntimeException { 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 a581d19a3..14d993ed9 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 @@ -14,10 +14,10 @@ package org.fisco.bcos.sdk.test.crypto; import org.fisco.bcos.sdk.crypto.CryptoInterface; +import org.fisco.bcos.sdk.crypto.exceptions.UnsupportedCryptoTypeException; 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; diff --git a/src/test/java/org/fisco/bcos/sdk/test/crypto/KeyManagerTest.java b/src/test/java/org/fisco/bcos/sdk/test/crypto/KeyManagerTest.java new file mode 100644 index 000000000..dfd549531 --- /dev/null +++ b/src/test/java/org/fisco/bcos/sdk/test/crypto/KeyManagerTest.java @@ -0,0 +1,181 @@ +/** + * 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.test.crypto; + +import java.security.KeyPair; +import org.fisco.bcos.sdk.crypto.CryptoInterface; +import org.fisco.bcos.sdk.crypto.exceptions.LoadKeyStoreException; +import org.fisco.bcos.sdk.crypto.keypair.CryptoKeyPair; +import org.fisco.bcos.sdk.crypto.keystore.KeyManager; +import org.fisco.bcos.sdk.crypto.keystore.P12Manager; +import org.fisco.bcos.sdk.crypto.keystore.PEMManager; +import org.junit.Assert; +import org.junit.Test; + +public class KeyManagerTest { + @Test + public void testECDSALoadPEMFile() { + String keyStoreFile = "keystore/ecdsa/0x0fc3c4bb89bd90299db4c62be0174c4966286c00.pem"; + CryptoKeyPair cryptoKeyPair = + testLoadPEMFile( + keyStoreFile, + CryptoInterface.ECDSA_TYPE, + "0fc3c4bb89bd90299db4c62be0174c4966286c00"); + // check the public key and the privateKey + // Note the 04 prefix + Assert.assertEquals( + "04dbbfee4f76f5a3bc3dbc2e6127c4a1f50b7614bff4138a44a79aed3d42f67f9c7aa70570205f9b60a5888c6415b6a830012677b4415a79ccd1533fe5637861df", + cryptoKeyPair.getHexPublicKey()); + Assert.assertEquals( + "bc516b2600eec3a216f457dc14cf83a01ed22d0fc2149fc911dc2ec486fe57a3", + cryptoKeyPair.getHexPrivateKey()); + } + + @Test + public void testSMLoadPEMFile() { + String keyStoreFile = "keystore/gm/0x40b3558746e8f9a47a474774e8c4a9e67d4e3174.pem"; + CryptoKeyPair cryptoKeyPair = + testLoadPEMFile( + keyStoreFile, + CryptoInterface.SM_TYPE, + "40b3558746e8f9a47a474774e8c4a9e67d4e3174"); + Assert.assertEquals( + "043b72cd28244c856d3d89b67d1c5ff22e1f26835bafcd63e9a4ad3424a2a57f2b759149f46c696df08b9d9473686675fc6dade744d0c82bdc5598d759e015fd96", + cryptoKeyPair.getHexPublicKey()); + Assert.assertEquals( + "901744c34e2adffc9fd7fb12e8cba2d88a79aaf54be9b4e11660153287489f13", + cryptoKeyPair.getHexPrivateKey()); + } + + @Test(expected = LoadKeyStoreException.class) + public void testECDSALoadInvalidPEMFile() { + String keyStoreFile = "keystore/ecdsa/invalid.pem"; + testLoadPEMFile( + keyStoreFile, + CryptoInterface.ECDSA_TYPE, + "0fc3c4bb89bd90299db4c62be0174c4966286c00"); + } + + @Test(expected = LoadKeyStoreException.class) + public void testSMLoadInvalidPEMFile() { + String keyStoreFile = "keystore/gm/invalid.pem"; + testLoadPEMFile( + keyStoreFile, CryptoInterface.SM_TYPE, "40b3558746e8f9a47a474774e8c4a9e67d4e3174"); + } + + @Test + public void testLoadECDSAP12File() { + String keyStoreFile = "keystore/ecdsa/0x45e14c53197adbcb719d915fb93342c25600faaf.p12"; + CryptoKeyPair cryptoKeyPair = + testLoadP12File( + keyStoreFile, + CryptoInterface.ECDSA_TYPE, + "123456", + "45e14c53197adbcb719d915fb93342c25600faaf"); + Assert.assertEquals( + "04d7b9e00f56d3f79305359fa2d7db166021e73086bdcd2e7a28d6ed27345e1f2ddecf85db7438e8457fd474ef9c4ceb89abb7d5fa60a22f2902ec26dca52ad5e5", + cryptoKeyPair.getHexPublicKey()); + Assert.assertEquals( + "c0c8b4d96aa4aefaeeafa157789d528b6010f65059dee796d8757e1171bbcd2c", + cryptoKeyPair.getHexPrivateKey()); + } + + @Test + public void testLoadSMP12File() { + String keyStoreFile = "keystore/gm/0x6f68461309925093236df82b51df630a55d32377.p12"; + CryptoKeyPair cryptoKeyPair = + testLoadP12File( + keyStoreFile, + CryptoInterface.SM_TYPE, + "abcd123", + "6f68461309925093236df82b51df630a55d32377"); + Assert.assertEquals( + "04a809a0176dc24432490697b6ed74995a6716a122a0fa5c73429a259cd73f14995934522288f226a049bbbb803d78f296289bee8fb4f5d7821514e731a57c9f2f", + cryptoKeyPair.getHexPublicKey()); + Assert.assertEquals( + "d0cbcdfea24e206688ce6c1a63171a24d9e1e0cf5331151ed5406e07fdb38256", + cryptoKeyPair.getHexPrivateKey()); + } + + @Test(expected = LoadKeyStoreException.class) + public void testInvalidECDSAP12Case() { + // error password + String keyStoreFile = "keystore/ecdsa/0x45e14c53197adbcb719d915fb93342c25600faaf.p12"; + testLoadP12File( + keyStoreFile, + CryptoInterface.ECDSA_TYPE, + "13456", + "45e14c53197adbcb719d915fb93342c25600faaf"); + } + + @Test(expected = LoadKeyStoreException.class) + public void testInvalidSMP12Case() { + String keyStoreFile = "keystore/gm/0x6f68461309925093236df82b51df630a55d32377.p12"; + testLoadP12File( + keyStoreFile, + CryptoInterface.SM_TYPE, + "abcd12e", + "6f68461309925093236df82b51df630a55d32377"); + } + + @Test(expected = LoadKeyStoreException.class) + public void testInvalidP12FileForECDSA() { + String keyStoreFile = "keystore/ecdsa/invalid.p12"; + testLoadP12File( + keyStoreFile, + CryptoInterface.ECDSA_TYPE, + "abcd123", + "6f68461309925093236df82b51df630a55d32377"); + } + + @Test(expected = LoadKeyStoreException.class) + public void testInvalidP12FileForSM() { + String keyStoreFile = "keystore/gm/invalid.p12"; + testLoadP12File( + keyStoreFile, + CryptoInterface.SM_TYPE, + "123456", + "45e14c53197adbcb719d915fb93342c25600faaf"); + } + + private String getFilePath(String fileName) { + return getClass().getClassLoader().getResource(fileName).getPath(); + } + + private CryptoKeyPair testLoadPEMFile( + String pemFileName, int cryptoType, String expectedAccount) { + // get KeyPair from the pem + KeyManager pem = new PEMManager(getFilePath(pemFileName)); + KeyPair keyPair = pem.getKeyPair(); + return testSignature(keyPair, cryptoType, expectedAccount); + } + + private CryptoKeyPair testLoadP12File( + String p12FileName, int cryptoType, String password, String expectedAccount) { + KeyManager p12 = new P12Manager(getFilePath(p12FileName), password); + KeyPair keyPair = p12.getKeyPair(); + return testSignature(keyPair, cryptoType, expectedAccount); + } + + private CryptoKeyPair testSignature(KeyPair keyPair, int cryptoType, String expectedAccount) { + CryptoInterface cryptoInterface = new CryptoInterface(cryptoType); + CryptoKeyPair cryptoKeyPair = cryptoInterface.createKeyPair(keyPair); + // check account + Assert.assertEquals(expectedAccount, cryptoKeyPair.getAddress()); + // test signature + SignatureTest signatureTest = new SignatureTest(); + signatureTest.testSignature(cryptoInterface, cryptoKeyPair); + return cryptoKeyPair; + } +} 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 c6162309f..a27b6144e 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,7 +13,9 @@ */ package org.fisco.bcos.sdk.test.crypto; +import java.math.BigInteger; import org.fisco.bcos.sdk.crypto.CryptoInterface; +import org.fisco.bcos.sdk.crypto.exceptions.KeyPairException; import org.fisco.bcos.sdk.crypto.keypair.CryptoKeyPair; import org.fisco.bcos.sdk.crypto.keypair.ECDSAKeyPair; import org.fisco.bcos.sdk.crypto.keypair.SM2KeyPair; @@ -21,6 +23,7 @@ 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.utils.Hex; import org.junit.Assert; import org.junit.Test; @@ -57,7 +60,125 @@ public void testSM2Signature() { testSignature(sm2Signature, keyPair); } - private void testSignature(Signature signature, CryptoKeyPair keyPair) { + @Test + public void testValidGetAddressForECDSA() { + CryptoKeyPair keyPair = (new ECDSAKeyPair()).generateKeyPair(); + String hexPublicKey = + "77a8f8d2f786f079bd661e774da3a9f430c76b9acbcd71f9976bff7456bb136a80cb97335cc929a531791970f8ce10c0ca6ffb391e9ef241a48cbd8f3db1a82e"; + String expectedHash = "deaa5343178c2be2cb5e9b13000ed951e302c15d"; + + String hexPublicKey2 = "00000"; + String expectedHash2 = "3f17f1962b36e491b30a40b2405849e597ba5fb5"; + testValidGetAddressForKeyPair( + keyPair, hexPublicKey, expectedHash, hexPublicKey2, expectedHash2); + + // create keyPair with cryptoInterface + CryptoInterface cryptoInterface = new CryptoInterface(CryptoInterface.ECDSA_TYPE); + keyPair = cryptoInterface.createKeyPair(); + testValidGetAddressForKeyPair( + keyPair, hexPublicKey, expectedHash, hexPublicKey2, expectedHash2); + + // test getAddress with generated KeyPair + keyPair.getAddress(); + } + + @Test + public void testValidGetAddressForSM() { + CryptoKeyPair keyPair = (new SM2KeyPair()).generateKeyPair(); + String hexPublicKey = + "77a8f8d2f786f079bd661e774da3a9f430c76b9acbcd71f9976bff7456bb136a80cb97335cc929a531791970f8ce10c0ca6ffb391e9ef241a48cbd8f3db1a82e"; + String expectedHash = "4b99a949a24f3dc8dc54b02d51ec0ae4c8bb7018"; + + String hexPublicKey2 = "00000"; + String expectedHash2 = "0ec7f82b659cc8c6b753f26d4e9ec85bc91c231e"; + testValidGetAddressForKeyPair( + keyPair, hexPublicKey, expectedHash, hexPublicKey2, expectedHash2); + + // create keyPair with cryptoInterface + CryptoInterface cryptoInterface = new CryptoInterface(CryptoInterface.SM_TYPE); + keyPair = cryptoInterface.createKeyPair(); + testValidGetAddressForKeyPair( + keyPair, hexPublicKey, expectedHash, hexPublicKey2, expectedHash2); + + // test getAddress with generated keyPair + keyPair.getAddress(); + } + + private void testValidGetAddressForKeyPair( + CryptoKeyPair keyPair, + String hexPublicKey, + String expectedHash, + String hexPublicKey2, + String expectedHash2) { + // case1: input public key is hexed string, without 0x prefix + testKeyPair(keyPair, hexPublicKey, expectedHash); + + // case2: input public key is bytes, without 0x prefix + byte[] publicKeyBytes = Hex.decode(hexPublicKey); + testKeyPair(keyPair, publicKeyBytes, expectedHash); + + // case3: input public key is hexed string, with 0x prefix + String hexPublicKeyWithPrefix = "0x" + hexPublicKey; + testKeyPair(keyPair, hexPublicKeyWithPrefix, expectedHash); + + // case4: input public key is bytes, with 0x prefix + publicKeyBytes = Hex.decode(hexPublicKey); + testKeyPair(keyPair, publicKeyBytes, expectedHash); + + // case5: input public key is bigInteger + BigInteger publicKeyValue = new BigInteger(hexPublicKey, 16); + testKeyPair(keyPair, publicKeyValue, expectedHash); + + // case6: input is 0 + testKeyPair(keyPair, hexPublicKey2, expectedHash2); + testKeyPair(keyPair, hexPublicKey2 + "00000", expectedHash2); + testKeyPair(keyPair, new BigInteger("0", 16), expectedHash2); + } + + private void testKeyPair(CryptoKeyPair keyPair, String publicKey, String expectedAddress) { + Assert.assertEquals(expectedAddress, keyPair.getAddress(publicKey)); + } + + private void testKeyPair(CryptoKeyPair keyPair, BigInteger publicKey, String expectedAddress) { + Assert.assertEquals(expectedAddress, Hex.toHexString(keyPair.getAddress(publicKey))); + } + + private void testKeyPair(CryptoKeyPair keyPair, byte[] publicKey, String expectedAddress) { + Assert.assertEquals(expectedAddress, Hex.toHexString(keyPair.getAddress(publicKey))); + } + + @Test(expected = KeyPairException.class) + public void testInvalidCaseForSM2KeyPair() { + CryptoKeyPair keyPair = (new SM2KeyPair()).generateKeyPair(); + testInvalidPublicKey(keyPair); + } + + @Test(expected = KeyPairException.class) + public void testInvalidCaseForECDSAKeyPair() { + CryptoKeyPair keyPair = (new ECDSAKeyPair()).generateKeyPair(); + testInvalidPublicKey(keyPair); + } + + @Test(expected = KeyPairException.class) + public void testInvalidCaseForECDSACryptoInterface() { + CryptoInterface cryptoInterface = new CryptoInterface(CryptoInterface.ECDSA_TYPE); + CryptoKeyPair keyPair = cryptoInterface.createKeyPair(); + testInvalidPublicKey(keyPair); + } + + @Test(expected = KeyPairException.class) + public void testInvalidCaseForSM2CryptoInterface() { + CryptoInterface cryptoInterface = new CryptoInterface(CryptoInterface.SM_TYPE); + CryptoKeyPair keyPair = cryptoInterface.createKeyPair(); + testInvalidPublicKey(keyPair); + } + + private void testInvalidPublicKey(CryptoKeyPair keyPair) { + // input is invalid hex string + keyPair.getAddress("123xyz"); + } + + public void testSignature(Signature signature, CryptoKeyPair keyPair) { String message = "abcde"; // check valid case for (int i = 0; i < 10; i++) { @@ -97,7 +218,7 @@ private void testSignature(Signature signature, CryptoKeyPair keyPair) { } } - private void testSignature(CryptoInterface signature, CryptoKeyPair keyPair) { + public void testSignature(CryptoInterface signature, CryptoKeyPair keyPair) { String message = "abcde"; // check valid case for (int i = 0; i < 10; i++) { diff --git a/src/test/resources/keystore/ecdsa/0x0fc3c4bb89bd90299db4c62be0174c4966286c00.pem b/src/test/resources/keystore/ecdsa/0x0fc3c4bb89bd90299db4c62be0174c4966286c00.pem new file mode 100644 index 000000000..b448c4cd7 --- /dev/null +++ b/src/test/resources/keystore/ecdsa/0x0fc3c4bb89bd90299db4c62be0174c4966286c00.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgvFFrJgDuw6IW9FfcFM+D +oB7SLQ/CFJ/JEdwuxIb+V6OhRANCAATbv+5PdvWjvD28LmEnxKH1C3YUv/QTikSn +mu09QvZ/nHqnBXAgX5tgpYiMZBW2qDABJne0QVp5zNFTP+VjeGHf +-----END PRIVATE KEY----- diff --git a/src/test/resources/keystore/ecdsa/0x45e14c53197adbcb719d915fb93342c25600faaf.p12 b/src/test/resources/keystore/ecdsa/0x45e14c53197adbcb719d915fb93342c25600faaf.p12 new file mode 100644 index 0000000000000000000000000000000000000000..09388e9a78ec05d6891dcc2240cf789b07f6f83f GIT binary patch literal 321 zcmXqLVzgyqWHxAGWM<>kYV&CO&dbQoxS;VXOXDYl#`gw|Z%~Bqu{7Q`XuM(2c$JMC zs+xz3k!eBW8iU4_1~P11FhQV^d@LN53ISJhIPHX)m^c_%8auvLEO!?1Oz8N#Q^Rtb z)Y`iCd6i24BWq88`zlj2U4qqgC-0nGqp+WCuaAc<6LVKt8`Av6kUMk1Zc`UcmJNOf zjz8Gr!E=h?@>!EhRlSuLqP!b9^O*0wN_DA9?D_Sm`KC|Ot`AYG&Y#`GBiDX_$EVt;YIQz?TWK(+buT7X_ t!Hh4iL|+s-({noXy_JfiQct)E@6S-OnQ*ZNafu{1t5XnbJMc$bYE zs+xz3k!eBW7K6r(1~P11FhQV^d@LN7isV_3zkfQNiHU=OrE$Vbe#=h|fr^JjzVT1H zBPAX_bAs#7`%4x$x(Driv(N6_lHc=}a@>nLpk=r9RtMWOiQLQm#uko!AByZQb8u(O z`?*#_$-Gb_YaPR^v#!7Eh52sWS{PbA`P`&X%XD2Q`|Z;=)Ecrm)o{o3FUyk3-0jc9 z5&1#h?q@)A?}WFHTsvc`KRwma+x+MFJ-G$e>v&e5J+EOXZXgQx9;b*Qha4M2HbW{y zrGcS=q5&rxt2Q4qlN2ihi%8cY)d@^HjalA>zjs@IrP#4WBqW=K7e*>US}F)3Rym;yEkZt1sJiolr6}Esw#9{YB~{$I6MO@q z&(n>&ZhRF_MBE9(%rM{l1OF(@u4R@KrZ3!tV^}tqOuz>96aS6OE>_dXtB6uOs{GK-Jil=*wFeY4%snCDbEi?OzcPgB3Cu;+aP DXdOaQ literal 0 HcmV?d00001 diff --git a/src/test/resources/keystore/gm/invalid.pem b/src/test/resources/keystore/gm/invalid.pem new file mode 100644 index 000000000..d63a94f80 --- /dev/null +++ b/src/test/resources/keystore/gm/invalid.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgkBdEw04q3/yf1/sS +6Mui2Ip5qvVL6bThFmAVModInxOhRANCAAQ7cs0oJEyFbT2Jtn0cX/IuHyaDW6/N +Y+mkrTQkoqV/K3WRSfRsaW3wi52Uc2hmdfxtredE0Mgr3FWY11ngFf2 +-----END PRIVATE KEY-----