Browse Source

add implementations of ECDSA and SHA256 based on BouncyCastle

tags/1.0.0
zhanglin33 5 years ago
parent
commit
8063bcd055
6 changed files with 379 additions and 25 deletions
  1. +169
    -1
      source/crypto/crypto-classic/src/main/java/com/jd/blockchain/crypto/utils/classic/ECDSAUtils.java
  2. +3
    -3
      source/crypto/crypto-classic/src/main/java/com/jd/blockchain/crypto/utils/classic/ED25519Utils.java
  3. +25
    -0
      source/crypto/crypto-classic/src/main/java/com/jd/blockchain/crypto/utils/classic/SHA256Utils.java
  4. +127
    -0
      source/crypto/crypto-classic/src/test/java/test/com/jd/blockchain/crypto/utils/classic/ECDSAUtilsTest.java
  5. +36
    -0
      source/crypto/crypto-classic/src/test/java/test/com/jd/blockchain/crypto/utils/classic/SHA256UtilsTest.java
  6. +19
    -21
      source/crypto/crypto-sm/src/main/java/com/jd/blockchain/crypto/utils/sm/SM2Utils.java

+ 169
- 1
source/crypto/crypto-classic/src/main/java/com/jd/blockchain/crypto/utils/classic/ECDSAUtils.java View File

@@ -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;}
}

+ 3
- 3
source/crypto/crypto-classic/src/main/java/com/jd/blockchain/crypto/utils/classic/ED25519Utils.java View File

@@ -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


+ 25
- 0
source/crypto/crypto-classic/src/main/java/com/jd/blockchain/crypto/utils/classic/SHA256Utils.java View File

@@ -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;
}
}

+ 127
- 0
source/crypto/crypto-classic/src/test/java/test/com/jd/blockchain/crypto/utils/classic/ECDSAUtilsTest.java View File

@@ -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;
}


}

+ 36
- 0
source/crypto/crypto-classic/src/test/java/test/com/jd/blockchain/crypto/utils/classic/SHA256UtilsTest.java View File

@@ -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));
}
}

+ 19
- 21
source/crypto/crypto-sm/src/main/java/com/jd/blockchain/crypto/utils/sm/SM2Utils.java View File

@@ -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];


Loading…
Cancel
Save