package com.security.util;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.util.LinkedList;
import org.bouncycastle.asn1.x9.X9IntegerConverter;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.signers.ECDSASigner;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.math.ec.ECAlgorithms;
import org.bouncycastle.math.ec.ECPoint;
public class Secp256k1Util {
private static final String RANDOM_NUMBER_ALGORITHM="SHA1PRNG";
private static final String RANDOM_NUMBER_ALGORITHM_PROVIDER="sun";
public static void main(String[] args) {
//16进制私钥
String hexPrivateKey = "25E9F2EDAAF9464E9FA0EFDB896835741EBE0F5E34F97CFB88457818B6681C32";
byte [] arrPrivateKey = ByteUtils.toByteArray(hexPrivateKey);
// 测试sign
byte[] arrStr = "ssss".getBytes();
byte[][] arr =Secp256k1Util.signTransaction(arrStr, arrPrivateKey);
String strSigR = ByteUtils.toHexString(arr[0]);
String strSigS = ByteUtils.toHexString(arr[1]);
String strSigV = ByteUtils.toHexString(arr[2]);
//
byte[] arrPublicKey = Secp256k1Util.recoverPublicKey(arr[0], arr[1], arr[2], arrStr);
String strPublicKey = ByteUtils.toHexString(arrPublicKey);
System.out.println(strPublicKey);
//
boolean flag =Secp256k1Util.verifySignature(arr[0], arr[1], arr[2], arrStr,arrPublicKey);
}
/**
* 随机生成私钥
* @param privateKey
* @return
*/
public static byte[] generatePrivateKey(byte[] privateKey) {
SecureRandom secureRandom = null;
try {
secureRandom = secureRandom.getInstance(RANDOM_NUMBER_ALGORITHM,
RANDOM_NUMBER_ALGORITHM_PROVIDER);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
}
BigInteger privateKeyCheck = BigInteger.ZERO;
//随机选取一个32字节的数、大小介于1-
//0xFFFF FFFF FFFF FFFF FFFF FFFF FFFF BAAE DCE6 AF48 A03B BFD2 5E8C D036 4141
BigInteger maxKey = new BigInteger("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140",16);
//产生实际字节数组
byte[] privateKeyAttempt = new byte[32];
secureRandom.nextBytes(privateKeyAttempt);
privateKeyCheck = new BigInteger(1,privateKeyAttempt);
while(privateKeyCheck.compareTo(BigInteger.ZERO)==0 || privateKeyCheck.compareTo(maxKey)==1) {
secureRandom.nextBytes(privateKeyAttempt);
privateKeyCheck = new BigInteger(1,privateKeyAttempt);
}
return privateKeyAttempt;
}
/**
* 利用私钥生成对应公钥
* @param privateKey
* @return
*/
public static byte[] generatePublicKey(byte[] privateKey) {
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1");
ECPoint pointQ = spec.getG().multiply(new BigInteger(1,privateKey));
return pointQ.getEncoded(true);
}
/**
* 用ECDSA算法对数据签名,返回二维字节数组
* @param 需要签名的数据、私钥
* @return
*/
public static byte[][] signTransaction(byte[] message,byte[] privateKey) {
try {
Security.addProvider(new BouncyCastleProvider());
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1");
ECDSASigner ecdsaSigner = new ECDSASigner();
ECDomainParameters domain = new ECDomainParameters(
spec.getCurve(),
spec.getG(),
spec.getN());
ECPrivateKeyParameters privateKeyParams =
new ECPrivateKeyParameters(new BigInteger(1,privateKey), domain);
//随机签名参数
ParametersWithRandom params = new ParametersWithRandom(privateKeyParams);
ecdsaSigner.init(true, params);
BigInteger[] sig = ecdsaSigner.generateSignature(message);
LinkedList<byte[]> sigData = new LinkedList<>();
byte[] publicKey =generatePublicKey(privateKey);
byte recoverId = getRecoveryId(sig[0].toByteArray(),sig[1].toByteArray(),message,publicKey);
for(BigInteger sigChunk: sig) {
sigData.add(sigChunk.toByteArray());
}
sigData.add(new byte[] {recoverId});
return sigData.toArray(new byte[][] {});
}catch (Exception e) {
StringWriter errors = new StringWriter();
e.printStackTrace(new PrintWriter(errors));
return new byte[0][0];
}
}
/**
* 根据私钥的签名推算对应得公钥
* @param privateKey
* @return
*/
public static byte[] recoverPublicKey(byte[] sigR,byte[] sigS, byte[] sigV,byte[] message) {
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1");
BigInteger pointN = spec.getN();
try {
BigInteger pointX = new BigInteger(1,sigR);
X9IntegerConverter x9 = new X9IntegerConverter();
byte[] compEnc =x9.integerToBytes(pointX, 1+x9.getByteLength(spec.getCurve()));
compEnc[0] = (byte)((sigV[0] &1)==1?0x03:0x02);
ECPoint pointR= spec.getCurve().decodePoint(compEnc);
if(!pointR.multiply(pointN).isInfinity()) {
return new byte[0];
}
BigInteger pointE = new BigInteger(1,message);
BigInteger pointEInv= BigInteger.ZERO.subtract(pointE).mod(pointN);
BigInteger pointRInv= new BigInteger(1,sigR).modInverse(pointN);
BigInteger srinv = pointRInv.multiply(new BigInteger(1,sigS)).mod(pointN);
BigInteger pointEInvRInv = pointRInv.multiply(pointEInv).mod(pointN);
ECPoint pointQ = ECAlgorithms.sumOfTwoMultiplies(spec.getG(), pointEInvRInv, pointR, srinv);
byte[] pointQBytes = pointQ.getEncoded(false);
return pointQBytes;
} catch (Exception e) {
//
}
return new byte[0];
}
/**
* 验证签名与公钥是否匹配
* @param 数字签名中的R S V 被签名的数据 公钥
* @return
*/
public static boolean verifySignature(byte[] sigR,byte[] sigS, byte[] sigV,byte[] message,byte[] pubKey) {
byte[] arrPubKey = recoverPublicKey(sigR, sigS, sigV, message);
if(ByteUtils.toHexString(arrPubKey).equals(ByteUtils.toHexString(pubKey))) {
return true;
}else {
return false;
}
}
/**
* 利用私钥对数据进行签名,返回der编码的字符串
*
* DER 可辨别编码规则
* 序列化格式包含9个元素
*
* @param privateKey
* @return
*/
public static String getSignatureDerString (byte[] message,byte[] privateKey) {
byte[][] signData =signTransaction(message, privateKey);
String sigR = ByteUtils.toHexString(signData[0]);
String sigS = ByteUtils.toHexString(signData[1]);
String sigV = ByteUtils.toHexString(signData[2]);
//获取R和S的长度
int lenSigR = signData[0].length;
int lenSigS = signData[1].length;
//计算序列的长度
int lenSequence = lenSigR +lenSigS +4;
//将长度转为16进制
String strLenSigR = Integer.toHexString(signData[0].length);
String strLenSigS = Integer.toHexString(signData[1].length);
String strLenSequence = Integer.toHexString(lenSequence);
//拼凑DER编码
StringBuilder sb= new StringBuilder();
sb.append("30");
sb.append(strLenSequence);
sb.append("02");
sb.append(strLenSigR);
sb.append(sigR);
sb.append("02");
sb.append(strLenSigS);
sb.append(sigS);
sb.append(sigV);
return sb.toString();
}
/**
* 解析DER签名获取签名中的R\S\V
*/
public static LinkedList<String> getSignDataRSV(String signDerString){
LinkedList<String> listSignData = new LinkedList<>();
//获取sigR
int lenSigR = Integer.parseInt(signDerString.substring(6,8),16);
String strSigR = signDerString.substring(8,lenSigR*2+8);
int lenSigS = Integer.parseInt(signDerString.substring(lenSigR*2+10,lenSigR*2+12),16);
String strSigS = signDerString.substring(signDerString);
}
}