SM2 裸签名

2021-08-18  本文已影响0人  jockie911

最近在处理一个需求,使用国密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);
}
上一篇下一篇

猜你喜欢

热点阅读