@@ -1,10 +1,178 @@ | |||
package com.jd.blockchain.crypto.utils.classic; | |||
import org.bouncycastle.asn1.sec.SECNamedCurves; | |||
import org.bouncycastle.asn1.x9.X9ECParameters; | |||
import org.bouncycastle.crypto.AsymmetricCipherKeyPair; | |||
import org.bouncycastle.crypto.CipherParameters; | |||
import org.bouncycastle.crypto.generators.ECKeyPairGenerator; | |||
import org.bouncycastle.crypto.params.*; | |||
import org.bouncycastle.crypto.signers.ECDSASigner; | |||
import org.bouncycastle.math.ec.ECCurve; | |||
import org.bouncycastle.math.ec.ECMultiplier; | |||
import org.bouncycastle.math.ec.ECPoint; | |||
import org.bouncycastle.math.ec.FixedPointCombMultiplier; | |||
import java.math.BigInteger; | |||
import java.security.SecureRandom; | |||
/** | |||
* @author zhanglin33 | |||
* @title: ECDSAUtils | |||
* @description: ECDSA signature algorithm | |||
* @description: ECDSA signature algorithm based on Curve secp256k1 with SHA256 | |||
* @date 2019-03-25, 17:21 | |||
*/ | |||
public class ECDSAUtils { | |||
private static final int R_SIZE =32; | |||
private static final int S_SIZE =32; | |||
// p = 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1 | |||
// the curve equation is y^2 = x^3 + 7. | |||
private static final X9ECParameters X9_PARAMS = SECNamedCurves.getByName("secp256k1"); | |||
private static final ECCurve CURVE = X9_PARAMS.getCurve(); | |||
private static final ECPoint ECDSA_G = X9_PARAMS.getG(); | |||
private static final BigInteger ECDSA_H = X9_PARAMS.getH(); | |||
private static final BigInteger ECDSA_N = X9_PARAMS.getN(); | |||
private static final ECDomainParameters DOMAIN_PARAMS = new ECDomainParameters(CURVE, ECDSA_G,ECDSA_N,ECDSA_H); | |||
//-----------------Key Generation Algorithm----------------- | |||
/** | |||
* key generation | |||
* | |||
* @return key pair | |||
*/ | |||
public static AsymmetricCipherKeyPair generateKeyPair(){ | |||
SecureRandom random = new SecureRandom(); | |||
return generateKeyPair(random); | |||
} | |||
public static AsymmetricCipherKeyPair generateKeyPair(SecureRandom random){ | |||
ECKeyGenerationParameters keyGenerationParams = new ECKeyGenerationParameters(DOMAIN_PARAMS,random); | |||
ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator(); | |||
// To generate the key pair | |||
keyPairGenerator.init(keyGenerationParams); | |||
return keyPairGenerator.generateKeyPair(); | |||
} | |||
/** | |||
* public key retrieval | |||
* | |||
* @param privateKey private key | |||
* @return publicKey | |||
*/ | |||
public static byte[] retrievePublicKey(byte[] privateKey) { | |||
ECMultiplier createBasePointMultiplier = new FixedPointCombMultiplier(); | |||
ECPoint publicKeyPoint = createBasePointMultiplier.multiply(DOMAIN_PARAMS.getG(), new BigInteger(1,privateKey)).normalize(); | |||
return publicKeyPoint.getEncoded(false); | |||
} | |||
//-----------------Digital Signature Algorithm----------------- | |||
/** | |||
* signature generation | |||
* | |||
* @param data data to be signed | |||
* @param privateKey private key | |||
* @return signature | |||
*/ | |||
public static byte[] sign(byte[] data, byte[] privateKey){ | |||
SecureRandom random = new SecureRandom(); | |||
ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(new BigInteger(1,privateKey), DOMAIN_PARAMS); | |||
CipherParameters params = new ParametersWithRandom(privKey,random); | |||
return sign(data,params); | |||
} | |||
public static byte[] sign(byte[] data, byte[] privateKey, SecureRandom random, String ID){ | |||
ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(new BigInteger(1,privateKey), DOMAIN_PARAMS); | |||
CipherParameters params = new ParametersWithID(new ParametersWithRandom(privKey,random),ID.getBytes()); | |||
return sign(data,params); | |||
} | |||
public static byte[] sign(byte[] data, CipherParameters params){ | |||
byte[] hashedMsg = SHA256Utils.hash(data); | |||
ECDSASigner signer = new ECDSASigner(); | |||
signer.init(true, params); | |||
BigInteger[] signature = signer.generateSignature(hashedMsg); | |||
// // To decode the signature | |||
// ASN1Sequence sig = ASN1Sequence.getInstance(encodedSignature); | |||
// byte[] rBytes = BigIntegerTo32Bytes(ASN1Integer.getInstance(sig.getObjectAt(0)).getValue()); | |||
// byte[] sBytes = BigIntegerTo32Bytes(ASN1Integer.getInstance(sig.getObjectAt(1)).getValue()); | |||
byte[] rBytes = BigIntegerTo32Bytes(signature[0]); | |||
byte[] sBytes = BigIntegerTo32Bytes(signature[1]); | |||
byte[] result = new byte[R_SIZE + S_SIZE]; | |||
System.arraycopy(rBytes,0,result,0,R_SIZE); | |||
System.arraycopy(sBytes,0,result,R_SIZE,S_SIZE); | |||
return result; | |||
} | |||
/** | |||
* verification | |||
* | |||
* @param data data to be signed | |||
* @param publicKey public key | |||
* @param signature signature to be verified | |||
* @return true or false | |||
*/ | |||
public static boolean verify(byte[] data, byte[] publicKey, byte[] signature){ | |||
ECPoint pubKeyPoint = resolvePubKeyBytes(publicKey); | |||
ECPublicKeyParameters pubKey = new ECPublicKeyParameters(pubKeyPoint, DOMAIN_PARAMS); | |||
return verify(data,pubKey,signature); | |||
} | |||
public static boolean verify(byte[] data, CipherParameters params, byte[] signature){ | |||
byte[] hashedMsg = SHA256Utils.hash(data); | |||
byte[] rBytes = new byte[R_SIZE]; | |||
byte[] sBytes = new byte[S_SIZE]; | |||
System.arraycopy(signature,0,rBytes,0,R_SIZE); | |||
System.arraycopy(signature,R_SIZE,sBytes,0,S_SIZE); | |||
BigInteger r = new BigInteger(1,rBytes); | |||
BigInteger s = new BigInteger(1,sBytes); | |||
ECDSASigner verifier = new ECDSASigner(); | |||
verifier.init(false,params); | |||
return verifier.verifySignature(hashedMsg,r,s); | |||
} | |||
// To convert BigInteger to byte[] whose length is 32 | |||
private static byte[] BigIntegerTo32Bytes(BigInteger b){ | |||
byte[] tmp = b.toByteArray(); | |||
byte[] result = new byte[32]; | |||
if (tmp.length > result.length) { | |||
System.arraycopy(tmp, tmp.length - result.length, result, 0, result.length); | |||
} | |||
else { | |||
System.arraycopy(tmp,0,result,result.length-tmp.length,tmp.length); | |||
} | |||
return result; | |||
} | |||
// To retrieve the public key point from publicKey in byte array mode | |||
private static ECPoint resolvePubKeyBytes(byte[] publicKey){ | |||
return CURVE.decodePoint(publicKey); | |||
} | |||
public static ECCurve getCurve(){return CURVE;} | |||
public static ECDomainParameters getDomainParams(){return DOMAIN_PARAMS;} | |||
} |
@@ -18,10 +18,10 @@ import java.security.SecureRandom; | |||
*/ | |||
public class ED25519Utils { | |||
//-----------------Key Pair Generation Algorithm----------------- | |||
//-----------------Key Generation Algorithm----------------- | |||
/** | |||
* key generation | |||
* key pair generation | |||
* | |||
* @return key pair | |||
*/ | |||
@@ -37,7 +37,7 @@ public class ED25519Utils { | |||
} | |||
/** | |||
* public retrieval | |||
* public key retrieval | |||
* | |||
* @param privateKey private key | |||
* @return publicKey | |||
@@ -0,0 +1,25 @@ | |||
package com.jd.blockchain.crypto.utils.classic; | |||
import org.bouncycastle.crypto.digests.SHA256Digest; | |||
/** | |||
* @author zhanglin33 | |||
* @title: SHA256Utils | |||
* @description: SHA256 hash algorithm | |||
* @date 2019-04-09, 14:28 | |||
*/ | |||
public class SHA256Utils { | |||
// The length of SHA256 output is 32 bytes | |||
private static final int SHA256DIGEST_LENGTH = 256 / 8; | |||
public static byte[] hash(byte[] data){ | |||
byte[] result = new byte[SHA256DIGEST_LENGTH]; | |||
SHA256Digest sha256Digest = new SHA256Digest(); | |||
sha256Digest.update(data,0,data.length); | |||
sha256Digest.doFinal(result,0); | |||
return result; | |||
} | |||
} |
@@ -0,0 +1,127 @@ | |||
package test.com.jd.blockchain.crypto.utils.classic; | |||
import com.jd.blockchain.crypto.utils.classic.ECDSAUtils; | |||
import org.bouncycastle.crypto.AsymmetricCipherKeyPair; | |||
import org.bouncycastle.crypto.params.ECDomainParameters; | |||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters; | |||
import org.bouncycastle.crypto.params.ECPublicKeyParameters; | |||
import org.bouncycastle.math.ec.ECMultiplier; | |||
import org.bouncycastle.math.ec.ECPoint; | |||
import org.bouncycastle.math.ec.FixedPointCombMultiplier; | |||
import org.bouncycastle.util.encoders.Hex; | |||
import org.junit.Test; | |||
import java.math.BigInteger; | |||
import java.util.Random; | |||
import static org.junit.Assert.*; | |||
/** | |||
* @author zhanglin33 | |||
* @title: ECDSAUtilsTest | |||
* @description: Tests for methods in ECDSAUtils | |||
* @date 2019-04-09, 14:58 | |||
*/ | |||
public class ECDSAUtilsTest { | |||
@Test | |||
public void generateKeyPairTest(){ | |||
AsymmetricCipherKeyPair keyPair = ECDSAUtils.generateKeyPair(); | |||
ECPrivateKeyParameters privKeyParams = (ECPrivateKeyParameters) keyPair.getPrivate(); | |||
ECPublicKeyParameters pubKeyParams = (ECPublicKeyParameters) keyPair.getPublic(); | |||
byte[] privKeyBytes = BigIntegerTo32Bytes(privKeyParams.getD()); | |||
byte[] pubKeyBytes = pubKeyParams.getQ().getEncoded(false); | |||
assertEquals(32,privKeyBytes.length); | |||
assertEquals(65,pubKeyBytes.length); | |||
} | |||
@Test | |||
public void retrievePublicKeyTest(){ | |||
AsymmetricCipherKeyPair keyPair = ECDSAUtils.generateKeyPair(); | |||
ECPrivateKeyParameters privKeyParams = (ECPrivateKeyParameters) keyPair.getPrivate(); | |||
ECPublicKeyParameters pubKeyParams = (ECPublicKeyParameters) keyPair.getPublic(); | |||
byte[] privKeyBytes = BigIntegerTo32Bytes(privKeyParams.getD()); | |||
byte[] pubKeyBytes = pubKeyParams.getQ().getEncoded(false); | |||
byte[] retrievedPubKeyBytes = ECDSAUtils.retrievePublicKey(privKeyBytes); | |||
assertArrayEquals(pubKeyBytes,retrievedPubKeyBytes); | |||
} | |||
@Test | |||
public void signTest(){ | |||
AsymmetricCipherKeyPair keyPair = ECDSAUtils.generateKeyPair(); | |||
ECPrivateKeyParameters privKeyParams = (ECPrivateKeyParameters) keyPair.getPrivate(); | |||
byte[] privKeyBytes = BigIntegerTo32Bytes(privKeyParams.getD()); | |||
Random random = new Random(); | |||
byte[] data = new byte[1024]; | |||
random.nextBytes(data); | |||
byte[] signatureDigestFromBytes = ECDSAUtils.sign(data,privKeyBytes); | |||
byte[] signatureDigestFromParams = ECDSAUtils.sign(data,privKeyBytes); | |||
assertEquals(64,signatureDigestFromParams.length); | |||
assertEquals(64,signatureDigestFromBytes.length); | |||
} | |||
@Test | |||
public void verifyTest(){ | |||
AsymmetricCipherKeyPair keyPair = ECDSAUtils.generateKeyPair(); | |||
ECPrivateKeyParameters privKeyParams = (ECPrivateKeyParameters) keyPair.getPrivate(); | |||
ECPublicKeyParameters pubKeyParams = (ECPublicKeyParameters) keyPair.getPublic(); | |||
byte[] pubKeyBytes = pubKeyParams.getQ().getEncoded(false); | |||
Random random = new Random(); | |||
byte[] data = new byte[1024]; | |||
random.nextBytes(data); | |||
byte[] signatureDigest = ECDSAUtils.sign(data,privKeyParams); | |||
assertTrue(ECDSAUtils.verify(data,pubKeyParams,signatureDigest)); | |||
assertTrue(ECDSAUtils.verify(data,pubKeyBytes,signatureDigest)); | |||
} | |||
@Test | |||
public void checkParams(){ | |||
// https://crypto.stackexchange.com/questions/784/are-there-any-secp256k1-ecdsa-test-examples-available | |||
ECDomainParameters params = ECDSAUtils.getDomainParams(); | |||
ECPoint ECDSA_G = params.getG(); | |||
BigInteger scalar = new BigInteger("AA5E28D6A97A2479A65527F7290311A3624D4CC0FA1578598EE3C2613BF99522",16); | |||
String xCoord = "34F9460F0E4F08393D192B3C5133A6BA099AA0AD9FD54EBCCFACDFA239FF49C6"; | |||
String yCoord = "0B71EA9BD730FD8923F6D25A7A91E7DD7728A960686CB5A901BB419E0F2CA232"; | |||
ECMultiplier createBasePointMultiplier = new FixedPointCombMultiplier(); | |||
ECPoint point = createBasePointMultiplier.multiply(ECDSA_G,scalar).normalize(); | |||
byte[] result = point.getEncoded(false); | |||
assertEquals("04" + xCoord + yCoord,Hex.toHexString(result).toUpperCase()); | |||
} | |||
// To convert BigInteger to byte[] whose length is 32 | |||
private static byte[] BigIntegerTo32Bytes(BigInteger b){ | |||
byte[] tmp = b.toByteArray(); | |||
byte[] result = new byte[32]; | |||
if (tmp.length > result.length) { | |||
System.arraycopy(tmp, tmp.length - result.length, result, 0, result.length); | |||
} | |||
else { | |||
System.arraycopy(tmp,0,result,result.length-tmp.length,tmp.length); | |||
} | |||
return result; | |||
} | |||
} |
@@ -0,0 +1,36 @@ | |||
package test.com.jd.blockchain.crypto.utils.classic; | |||
import com.jd.blockchain.crypto.utils.classic.SHA256Utils; | |||
import org.bouncycastle.util.encoders.Hex; | |||
import org.junit.Test; | |||
import static org.junit.Assert.assertEquals; | |||
/** | |||
* @author zhanglin33 | |||
* @title: SHA256UtilsTest | |||
* @description: Tests for the hash method in ECDSAUtils | |||
* @date 2019-04-09, 16:18 | |||
*/ | |||
public class SHA256UtilsTest { | |||
@Test | |||
public void hashTest() { | |||
byte[] data1 = "abc".getBytes(); | |||
byte[] data2 = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq".getBytes(); | |||
byte[] data3 = "aaaaaaaaaa".getBytes(); | |||
byte[] result1 = SHA256Utils.hash(data1); | |||
byte[] result2 = SHA256Utils.hash(data2); | |||
byte[] result3 = SHA256Utils.hash(data3); | |||
String respectedResult1 = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"; | |||
String respectedResult2 = "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"; | |||
String respectedResult3 = "bf2cb58a68f684d95a3b78ef8f661c9a4e5b09e82cc8f9cc88cce90528caeb27"; | |||
assertEquals(respectedResult1,Hex.toHexString(result1)); | |||
assertEquals(respectedResult2,Hex.toHexString(result2)); | |||
assertEquals(respectedResult3,Hex.toHexString(result3)); | |||
} | |||
} |
@@ -43,15 +43,14 @@ public class SM2Utils { | |||
private static final ECDomainParameters DOMAIN_PARAMS = new ECDomainParameters(CURVE, G, SM2_N); | |||
//-----------------Key Pair Generation Algorithm----------------- | |||
//-----------------Key Generation Algorithm----------------- | |||
/** | |||
* key generation | |||
* key pair generation | |||
* | |||
* @return key pair | |||
*/ | |||
public static AsymmetricCipherKeyPair generateKeyPair(){ | |||
SecureRandom random = new SecureRandom(); | |||
return generateKeyPair(random); | |||
} | |||
@@ -67,7 +66,7 @@ public class SM2Utils { | |||
} | |||
/** | |||
* public retrieval | |||
* public key retrieval | |||
* | |||
* @param privateKey private key | |||
* @return publicKey | |||
@@ -81,7 +80,6 @@ public class SM2Utils { | |||
//-----------------Digital Signature Algorithm----------------- | |||
/** | |||
* signature generation | |||
* | |||
@@ -93,17 +91,17 @@ public class SM2Utils { | |||
SecureRandom random = new SecureRandom(); | |||
ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(new BigInteger(1,privateKey), DOMAIN_PARAMS); | |||
CipherParameters param = new ParametersWithRandom(privKey,random); | |||
CipherParameters params = new ParametersWithRandom(privKey,random); | |||
return sign(data,param); | |||
return sign(data,params); | |||
} | |||
public static byte[] sign(byte[] data, byte[] privateKey, SecureRandom random, String ID){ | |||
ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(new BigInteger(1,privateKey), DOMAIN_PARAMS); | |||
CipherParameters param = new ParametersWithID(new ParametersWithRandom(privKey,random),ID.getBytes()); | |||
CipherParameters params = new ParametersWithID(new ParametersWithRandom(privKey,random),ID.getBytes()); | |||
return sign(data,param); | |||
return sign(data,params); | |||
} | |||
public static byte[] sign(byte[] data, CipherParameters params){ | |||
@@ -155,18 +153,18 @@ public class SM2Utils { | |||
ECPoint pubKeyPoint = resolvePubKeyBytes(publicKey); | |||
ECPublicKeyParameters pubKey = new ECPublicKeyParameters(pubKeyPoint, DOMAIN_PARAMS); | |||
ParametersWithID param = new ParametersWithID(pubKey,ID.getBytes()); | |||
ParametersWithID params = new ParametersWithID(pubKey,ID.getBytes()); | |||
return verify(data,param,signature); | |||
return verify(data,params,signature); | |||
} | |||
public static boolean verify(byte[] data, CipherParameters param, byte[] signature){ | |||
public static boolean verify(byte[] data, CipherParameters params, byte[] signature){ | |||
SM2Signer verifier = new SM2Signer(); | |||
// To get Z_A and prepare parameters | |||
verifier.init(false,param); | |||
verifier.init(false,params); | |||
// To fill the whole message | |||
verifier.update(data,0,data.length); | |||
// To verify the signature | |||
@@ -211,25 +209,25 @@ public class SM2Utils { | |||
ECPoint pubKeyPoint = resolvePubKeyBytes(publicKey); | |||
ECPublicKeyParameters pubKey = new ECPublicKeyParameters(pubKeyPoint, DOMAIN_PARAMS); | |||
ParametersWithRandom param = new ParametersWithRandom(pubKey,random); | |||
ParametersWithRandom params = new ParametersWithRandom(pubKey,random); | |||
return encrypt(plainBytes,param); | |||
return encrypt(plainBytes,params); | |||
} | |||
public static byte[] encrypt(byte[] plainBytes, ECPublicKeyParameters pubKey){ | |||
SecureRandom random = new SecureRandom(); | |||
ParametersWithRandom param = new ParametersWithRandom(pubKey,random); | |||
ParametersWithRandom params = new ParametersWithRandom(pubKey,random); | |||
return encrypt(plainBytes,param); | |||
return encrypt(plainBytes,params); | |||
} | |||
public static byte[] encrypt(byte[] plainBytes, CipherParameters param){ | |||
public static byte[] encrypt(byte[] plainBytes, CipherParameters params){ | |||
SM2Engine encryptor = new SM2Engine(); | |||
// To prepare parameters | |||
encryptor.init(true,param); | |||
encryptor.init(true,params); | |||
// To generate the twisted ciphertext c1c2c3. | |||
// The latest standard specification indicates that the correct ordering is c1c3c2 | |||
@@ -263,12 +261,12 @@ public class SM2Utils { | |||
return decrypt(cipherBytes,privKey); | |||
} | |||
public static byte[] decrypt(byte[] cipherBytes, CipherParameters param){ | |||
public static byte[] decrypt(byte[] cipherBytes, CipherParameters params){ | |||
SM2Engine decryptor = new SM2Engine(); | |||
// To prepare parameters | |||
decryptor.init(false,param); | |||
decryptor.init(false,params); | |||
// To get c1c2c3 from ciphertext whose ordering is c1c3c2 | |||
byte[] c1c2c3 = new byte[cipherBytes.length]; | |||