基於BC庫的國密演算法 SM2演算法工具
阿新 • • 發佈:2022-03-25
import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.CryptoException; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.engines.SM2Engine; import org.bouncycastle.crypto.engines.SM2Engine.Mode; import org.bouncycastle.crypto.params.*; import org.bouncycastle.crypto.signers.SM2Signer; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.math.ec.custom.gm.SM2P256V1Curve; import java.math.BigInteger; import java.security.SecureRandom; /** * SM2演算法工具 * */ public final class SM2Utils { /** * Begin: SM2推薦曲線引數 */ private static final String SM2_ECC_GY_VAL = "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0"; private static final String SM2_ECC_GX_VAL = "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7"; private static final SM2P256V1Curve CURVE = new SM2P256V1Curve(); private final static BigInteger SM2_ECC_N = CURVE.getOrder(); private final static BigInteger SM2_ECC_H = CURVE.getCofactor(); private final static BigInteger SM2_ECC_GX = new BigInteger(SM2_ECC_GX_VAL, 16); private final static BigInteger SM2_ECC_GY = new BigInteger(SM2_ECC_GY_VAL, 16); private static final ECPoint G_POINT = CURVE.createPoint(SM2_ECC_GX, SM2_ECC_GY); private static final ECDomainParameters DOMAIN_PARAMS = new ECDomainParameters(CURVE, G_POINT, SM2_ECC_N, SM2_ECC_H); /** * End: SM2推薦曲線引數 */ private SM2Utils() { } /** * 生成ECC金鑰對 * * @return ECC金鑰對 */ public static AsymmetricCipherKeyPair generateKeyPairParameter() { final SecureRandom random = new SecureRandom(); return BCECUtils.generateKeyPairParameter(DOMAIN_PARAMS, random); } /** * @param pubKey * 公鑰 * @param srcData * 原文 * @return 預設輸出C1C3C2順序的密文。C1為65位元組第1位元組為壓縮標識,這裡固定為0x04,後面64位元組為xy分量各32位元組。C3為32位元組。C2長度與原文一致。 * @throws InvalidCipherTextException */ public static byte[] encrypt(final BCECPublicKey pubKey, final byte[] srcData) throws InvalidCipherTextException { return encrypt(Mode.C1C3C2, pubKey, srcData); } /** * @param pubKeyParams * 公鑰 * @param srcData * 原文 * @return 預設輸出C1C3C2順序的密文。C1為65位元組第1位元組為壓縮標識,這裡固定為0x04,後面64位元組為xy分量各32位元組。C3為32位元組。C2長度與原文一致。 * @throws InvalidCipherTextException */ public static byte[] encrypt(final ECPublicKeyParameters pubKeyParams, final byte[] srcData) throws InvalidCipherTextException { return encrypt(Mode.C1C3C2, pubKeyParams, srcData); } /** * @param priKey * 私鑰 * @param sm2Cipher * 預設輸入C1C3C2順序的密文。C1為65位元組第1位元組為壓縮標識,這裡固定為0x04,後面64位元組為xy分量各32位元組。C3為32位元組。C2長度與原文一致。 * @return 原文。SM2解密返回了資料則一定是原文,因為SM2自帶校驗,如果密文被篡改或者金鑰對不上,都是會直接報異常的。 * @throws InvalidCipherTextException */ public static byte[] decrypt(final BCECPrivateKey priKey, final byte[] sm2Cipher) throws InvalidCipherTextException { return decrypt(Mode.C1C3C2, priKey, sm2Cipher); } /** * @param priKeyParams * 私鑰 * @param sm2Cipher * 預設輸入C1C3C2順序的密文。C1為65位元組第1位元組為壓縮標識,這裡固定為0x04,後面64位元組為xy分量各32位元組。C3為32位元組。C2長度與原文一致。 * @return 原文。SM2解密返回了資料則一定是原文,因為SM2自帶校驗,如果密文被篡改或者金鑰對不上,都是會直接報異常的。 * @throws InvalidCipherTextException */ public static byte[] decrypt(final ECPrivateKeyParameters priKeyParams, final byte[] sm2Cipher) throws InvalidCipherTextException { return decrypt(Mode.C1C3C2, priKeyParams, sm2Cipher); } /** * 簽名 * * @param priKey * 私鑰 * @param srcData * 原文 * @return DER編碼後的簽名值 * @throws CryptoException */ public static byte[] sign(BCECPrivateKey priKey, byte[] srcData) throws CryptoException { return sign(priKey, null, srcData); } /** * 簽名,預設withId為位元組陣列:"1234567812345678".getBytes() * * @param priKeyParams * 私鑰 * @param srcData * 原文 * @return DER編碼後的簽名值 * @throws CryptoException */ public static byte[] sign(final ECPrivateKeyParameters priKeyParams, final byte[] srcData) throws CryptoException { return sign(priKeyParams, null, srcData); } /** * 驗籤 * * @param pubKey * 公鑰 * @param srcData * 原文 * @param sign * DER編碼的簽名值 * @return boolean */ public static boolean verify(final BCECPublicKey pubKey, final byte[] srcData, final byte[] sign) { return verify(pubKey, null, srcData, sign); } /** * 驗籤 不指定withId,則預設withId為位元組陣列:"1234567812345678".getBytes() * * @param pubKeyParams * 公鑰 * @param srcData * 原文 * @param sign * DER編碼的簽名值 * @return 驗籤成功返回true,失敗返回false */ public static boolean verify(final ECPublicKeyParameters pubKeyParams, final byte[] srcData, final byte[] sign) { return verify(pubKeyParams, null, srcData, sign); } /** * @param mode * 指定密文結構,新的[《SM2密碼演算法使用規範》 GM/T 0009-2012]標準為C1C3C2 * @param pubKey * 公鑰 * @param srcData * 原文 * @return 根據mode不同,輸出的密文C1C2C3排列順序不同。C1為65位元組第1位元組為壓縮標識,這裡固定為0x04,後面64位元組為xy分量各32位元組。C3為32位元組。C2長度與原文一致。 * @throws InvalidCipherTextException */ private static byte[] encrypt(final Mode mode, final BCECPublicKey pubKey, final byte[] srcData) throws InvalidCipherTextException { final ECPublicKeyParameters pubKeyParams = BCECUtils.convertPublicKeyToParameters(pubKey); return encrypt(mode, pubKeyParams, srcData); } /** * @param mode * 指定密文結構,新的[《SM2密碼演算法使用規範》 GM/T 0009-2012]標準為C1C3C2 * @param pubKeyParams * 公鑰 * @param srcData * 原文 * @return 根據mode不同,輸出的密文C1C2C3排列順序不同。C1為65位元組第1位元組為壓縮標識,這裡固定為0x04,後面64位元組為xy分量各32位元組。C3為32位元組。C2長度與原文一致。 * @throws InvalidCipherTextException */ private static byte[] encrypt(final Mode mode, final ECPublicKeyParameters pubKeyParams, final byte[] srcData) throws InvalidCipherTextException { final SM2Engine engine = new SM2Engine(mode); final ParametersWithRandom pwr = new ParametersWithRandom(pubKeyParams, new SecureRandom()); engine.init(true, pwr); return engine.processBlock(srcData, 0, srcData.length); } /** * 驗籤 * * @param pubKey * 公鑰 * @param withId * 可以為null,若為null,則預設withId為位元組陣列:"1234567812345678".getBytes() * @param srcData * 原文 * @param sign * DER編碼的簽名值 * @return boolean */ private static boolean verify(final BCECPublicKey pubKey, final byte[] withId, final byte[] srcData, final byte[] sign) { final ECPublicKeyParameters pubKeyParams = BCECUtils.convertPublicKeyToParameters(pubKey); return verify(pubKeyParams, withId, srcData, sign); } /** * @param mode * 指定密文結構,新的[《SM2密碼演算法使用規範》 GM/T 0009-2012]標準為C1C3C2 * @param priKey * 私鑰 * @param sm2Cipher * 根據mode不同,需要輸入的密文C1C2C3排列順序不同。C1為65位元組第1位元組為壓縮標識,這裡固定為0x04,後面64位元組為xy分量各32位元組。C3為32位元組。C2長度與原文一致。 * @return 原文。SM2解密返回了資料則一定是原文,因為SM2自帶校驗,如果密文被篡改或者金鑰對不上,都是會直接報異常的。 * @throws InvalidCipherTextException */ private static byte[] decrypt(final Mode mode, final BCECPrivateKey priKey, final byte[] sm2Cipher) throws InvalidCipherTextException { final ECPrivateKeyParameters priKeyParams = BCECUtils.convertPrivateKeyToParameters(priKey); return decrypt(mode, priKeyParams, sm2Cipher); } /** * @param mode * 指定密文結構,新的[《SM2密碼演算法使用規範》 GM/T 0009-2012]標準為C1C3C2 * @param priKeyParams * 私鑰 * @param sm2Cipher * 根據mode不同,需要輸入的密文C1C2C3排列順序不同。C1為65位元組第1位元組為壓縮標識,這裡固定為0x04,後面64位元組為xy分量各32位元組。C3為32位元組。C2長度與原文一致。 * @return 原文。SM2解密返回了資料則一定是原文,因為SM2自帶校驗,如果密文被篡改或者金鑰對不上,都是會直接報異常的。 * @throws InvalidCipherTextException */ private static byte[] decrypt(final Mode mode, final ECPrivateKeyParameters priKeyParams, final byte[] sm2Cipher) throws InvalidCipherTextException { final SM2Engine engine = new SM2Engine(mode); engine.init(false, priKeyParams); return engine.processBlock(sm2Cipher, 0, sm2Cipher.length); } /** * 私鑰簽名 * * @param priKey * 私鑰 * @param withId * 可以為null,若為null,則預設withId為位元組陣列:"1234567812345678".getBytes() * @param srcData * 原文 * @return DER編碼後的簽名值 * @throws CryptoException */ private static byte[] sign(final BCECPrivateKey priKey, final byte[] withId, final byte[] srcData) throws CryptoException { final ECPrivateKeyParameters priKeyParams = BCECUtils.convertPrivateKeyToParameters(priKey); return sign(priKeyParams, withId, srcData); } /** * 簽名 * * @param priKeyParams * 私鑰 * @param withId * 可以為null,若為null,則預設withId為位元組陣列:"1234567812345678".getBytes() * @param srcData * 源資料 * @return DER編碼後的簽名值 * @throws CryptoException */ private static byte[] sign(final ECPrivateKeyParameters priKeyParams, final byte[] withId, final byte[] srcData) throws CryptoException { SM2Signer signer = new SM2Signer(); CipherParameters param = null; ParametersWithRandom pwr = new ParametersWithRandom(priKeyParams, new SecureRandom()); if (withId != null) { param = new ParametersWithID(pwr, withId); } else { param = pwr; } signer.init(true, param); signer.update(srcData, 0, srcData.length); return signer.generateSignature(); } /** * 驗籤 * * @param pubKeyParams * 公鑰 * @param withId * 可以為null,若為null,則預設withId為位元組陣列:"1234567812345678".getBytes() * @param srcData * 原文 * @param sign * DER編碼的簽名值 * @return 驗籤成功返回true,失敗返回false */ private static boolean verify(final ECPublicKeyParameters pubKeyParams, final byte[] withId, final byte[] srcData, final byte[] sign) { final SM2Signer signer = new SM2Signer(); CipherParameters param; if (withId != null) { param = new ParametersWithID(pubKeyParams, withId); } else { param = pubKeyParams; } signer.init(false, param); signer.update(srcData, 0, srcData.length); return signer.verifySignature(sign); } }