最近在處理一個(gè)需求试幽,使用國(guó)密SM2進(jìn)行簽名氏义,在實(shí)際需求中有這樣的一個(gè)場(chǎng)景:對(duì)PDF進(jìn)行簽名究西,其實(shí)是簽署PDF sm3處理后的摘要窗慎,所以后端給到我們的就是sm3 hash,標(biāo)準(zhǔn)的做法是簽署 signerinfo->AuthenticatedAttributeSet,而偏偏我們遇到的是直接對(duì)hash進(jìn)行簽名,所以不能用sm2sign_with_sm3遮斥,而BC庫(kù)又不支持sm2sign算法峦失。最后看源碼找到了SM2Signer,它的內(nèi)部實(shí)現(xiàn)其實(shí)還是sm2sign_with_sm3,所以我們可以進(jìn)行修改SM2Signer
看過源碼后發(fā)現(xiàn)只要修改generateSignature -> eHash就可以了,這樣就可以直接跳過內(nèi)部做過的sm3算法
public byte[] generateSignature(byte[] sm3Hash)
throws CryptoException
{
byte[] eHash = sm3Hash;
BigInteger n = ecParams.getN();
BigInteger e = calculateE(eHash);
BigInteger d = ((ECPrivateKeyParameters)ecKey).getD();
BigInteger r, s;
ECMultiplier basePointMultiplier = createBasePointMultiplier();
// 5.2.1 Draft RFC: SM2 Public Key Algorithms
do // generate s
{
BigInteger k;
do // generate r
{
// A3
k = kCalculator.nextK();
// A4
ECPoint p = basePointMultiplier.multiply(ecParams.getG(), k).normalize();
// A5
r = e.add(p.getAffineXCoord().toBigInteger()).mod(n);
}
while (r.equals(ZERO) || r.add(k).equals(n));
// A6
BigInteger dPlus1ModN = d.add(ONE).modInverse(n);
s = k.subtract(r.multiply(d)).mod(n);
s = dPlus1ModN.multiply(s).mod(n);
}
while (s.equals(ZERO));
// A7
try
{
return derEncode(r, s);
}
catch (IOException ex)
{
throw new CryptoException("unable to encode signature: " + ex.getMessage(), ex);
}
}
在使用的時(shí)候只需要傳入sm3后的hash
/**
* 簽名
*
* @param priKey 私鑰
* @param srcData 原文
* @return DER編碼后的簽名值
* @throws CryptoException
*/
public static byte[] sign(BCECPrivateKey priKey, byte[] srcData) throws CryptoException {
ECPrivateKeyParameters priKeyParameters = BCECUtil.convertPrivateKeyToParameters(priKey);
BCSM2Signer signer = new BCSM2Signer();
CipherParameters param = new ParametersWithRandom(priKeyParameters, new SecureRandom());
signer.init(true, param);
return signer.generateSignature(srcData);
}
簽名驗(yàn)簽同理
public boolean verifySignature(byte[] sm3Hash,byte[] signature)
{
this.sm3Hash = sm3Hash;
try
{
BigInteger[] rs = derDecode(signature);
if (rs != null)
{
return verifySignature(rs[0], rs[1]);
}
}
catch (IOException e)
{
}
return false;
}
private boolean verifySignature(BigInteger r, BigInteger s)
{
BigInteger n = ecParams.getN();
// 5.3.1 Draft RFC: SM2 Public Key Algorithms
// B1
if (r.compareTo(ONE) < 0 || r.compareTo(n) >= 0)
{
return false;
}
// B2
if (s.compareTo(ONE) < 0 || s.compareTo(n) >= 0)
{
return false;
}
// B3
byte[] eHash = sm3Hash;
// B4
BigInteger e = calculateE(eHash);
// B5
BigInteger t = r.add(s).mod(n);
if (t.equals(ZERO))
{
return false;
}
// B6
ECPoint q = ((ECPublicKeyParameters)ecKey).getQ();
ECPoint x1y1 = ECAlgorithms.sumOfTwoMultiplies(ecParams.getG(), s, q, t).normalize();
if (x1y1.isInfinity())
{
return false;
}
// B7
BigInteger expectedR = e.add(x1y1.getAffineXCoord().toBigInteger()).mod(n);
return expectedR.equals(r);
}
/**
* 驗(yàn)簽
*
* @param pubKey 公鑰
* @param sm3Hash 原文sm3hash
* @param sign DER編碼的簽名值
* @return
*/
public static boolean verify(BCECPublicKey pubKey, byte[] sm3Hash, byte[] sign) {
ECPublicKeyParameters pubKeyParameters = BCECUtil.convertPublicKeyToParameters(pubKey);
BCSM2Signer signer = new BCSM2Signer();
signer.init(false, pubKeyParameters);
return signer.verifySignature(sm3Hash,sign);
}