進行SM2簽名時,如何計算SM3摘要?
更新時間 2023-11-09 15:40:44
最近更新時間: 2023-11-09 15:40:44
分享文章
使用SM2密鑰簽名時,僅支持對消息摘要簽名。
根據GBT32918國家標準,計算SM2簽名值時,消息摘要不是對原始消息直接計算SM3摘要,而是對Z(A)和M的拼接值計算的摘要。其中M是待簽名的原始消息,Z(A)是GBT32918中定義的用戶A的雜湊值。
以JAVA為例,參考如下示例代碼:
public class Sm2SignDataPreprocessing {
private static final String ECC_A = "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC";
private static final String ECC_B = "28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93";
private static final String ECC_GX = "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7";
private static final String ECC_GY = "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0";
// 簽名者ID,本示例使用國密標準定義的缺省值"1234567812345678"
private static final String SM2_ID = "31323334353637383132333435363738";
public static void main(String[] args) throws Exception {
// SM2公鑰,BASE64編碼格式,僅做示例,實際使用請替換
String sm2PubKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEsuOq/EjQeYUD9h7lIyqi3pQ6SWL7hTXjUJWmSIZAcnj" +
"h9c0QcdbwzaCfI8iyyPCetX0QZl5NHrBoYLYxJpvbFg==";
byte[] pubKeyBytes = Base64.decodeBase64(sm2PubKey.getBytes(StandardCharsets.UTF_8));
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("EC", new BouncyCastleProvider());
ECPublicKey ecPublicKey = (ECPublicKey) keyFactory.generatePublic(keySpec);
// 待簽名的原始消息,僅做示例,實際使用請替換
byte[] dataToDigest = new byte[]{1, 2, 3, 4};
// Z(A)是GBT32918中定義的用戶A的雜湊值
byte[] zsm = getSm2Z(ecPublicKey.getQ().getAffineXCoord().getEncoded(),
ecPublicKey.getQ().getAffineYCoord().getEncoded());
// Z(A)和M的拼接值計算的摘要,并打印
byte[] dataToSign = getSm3SignData(zsm, dataToDigest);
System.out.println(Base64.encodeBase64String(dataToSign));
// 其他語言實現或執行此示例應有如下輸出:He+qiM2MmtNxlV/EB4vqkcP60XgG08z/8nWQdp/IS5c=
}
// 計算Z(A)
private static byte[] getSm2Z(byte[] x, byte[] y) throws DecoderException {
SM3Digest sm3 = new SM3Digest();
byte[] userId = Hex.decodeHex(SM2_ID.toCharArray());
byte[] byteEccA = Hex.decodeHex(ECC_A);
byte[] byteEccB = Hex.decodeHex(ECC_B);
byte[] byteEccGx = Hex.decodeHex(ECC_GX);
byte[] byteEccGy = Hex.decodeHex(ECC_GY);
int len = userId.length * 8;
sm3.update((byte) (len >> 8 & 255));
sm3.update((byte) (len & 255));
sm3.update(userId, 0, userId.length);
sm3.update(byteEccA, 0, byteEccA.length);
sm3.update(byteEccB, 0, byteEccB.length);
sm3.update(byteEccGx, 0, byteEccGx.length);
sm3.update(byteEccGy, 0, byteEccGy.length);
sm3.update(x, 0, x.length);
sm3.update(y, 0, y.length);
byte[] md = new byte[sm3.getDigestSize()];
sm3.doFinal(md, 0);
return md;
}
// 計算Z(A)和M的拼接值的摘要
private static byte[] getSm3SignData(byte[] z, byte[] sourceData) {
SM3Digest sm3 = new SM3Digest();
sm3.update(z, 0, z.length);
sm3.update(sourceData, 0, sourceData.length);
byte[] md = new byte[sm3.getDigestSize()];
sm3.doFinal(md, 0);
return md;
}
}