最近在处理一个需求,使用国密SM2进行签名,在实际需求中有这样的一个场景:对PDF进行签名,其实是签署PDF sm3处理后的摘要,所以后端给到我们的就是sm3 hash,标准的做法是签署 signerinfo->AuthenticatedAttributeSet,而偏偏我们遇到的是直接对hash进行签名,所以不能用sm2sign_with_sm3,而BC库又不支持sm2sign算法。最后看源码找到了SM2Signer,它的内部实现其实还是sm2sign_with_sm3,所以我们可以进行修改SM2Signer
看过源码后发现只要修改generateSignature -> eHash就可以了,这样就可以直接跳过内部做过的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);
}
}
在使用的时候只需要传入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);
}
签名验签同理
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);
}
/**
* 验签
*
* @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);
}