SM2國密加解密一個類就夠了
<dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.65</version> </dependency>
版本庫經測試適用(1.61-1.68) 如有問題請留言糾正
本文參考博主 「RisenMyth」:https://blog.csdn.net/RisenMyth/article/details/107212156
若要使用老版本的寫法 可以參考 https://blog.csdn.net/fenglongmiao/article/details/79501757
import org.bouncycastle.asn1.gm.GMNamedCurves; import org.bouncycastle.asn1.gm.GMObjectIdentifiers; import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; import org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.spec.ECParameterSpec; import org.bouncycastle.jce.spec.ECPrivateKeySpec; import org.bouncycastle.jce.spec.ECPublicKeySpec; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.encoders.Hex; import org.springframework.stereotype.Component; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import java.io.ByteArrayInputStream; import java.math.BigInteger; import java.security.*; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.spec.ECGenParameterSpec; import java.security.spec.InvalidKeySpecException; import java.util.Base64; /** * bcprov-jdk15on 版本適用(1.61-1.68) * @author dashou * @date 2021-4-13 */ @Component public class SM2Util { private BouncyCastleProvider provider; // 獲取SM2相關參數 private X9ECParameters parameters; // 橢圓曲線參數規格 private ECParameterSpec ecParameterSpec; // 獲取橢圓曲線KEY生成器 private KeyFactory keyFactory; private SM2Util(){ try { provider = new BouncyCastleProvider(); parameters = GMNamedCurves.getByName("sm2p256v1"); ecParameterSpec = new ECParameterSpec(parameters.getCurve(), parameters.getG(), parameters.getN(), parameters.getH()); keyFactory = KeyFactory.getInstance("EC", provider); } catch (Exception e) { e.printStackTrace(); } } /** * SM2算法生成密鑰對 * * @return 密鑰對信息 */ public KeyPair generateSm2KeyPair() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException { final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1"); // 獲取一個橢圓曲線類型的密鑰對生成器 final KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", provider); SecureRandom random = new SecureRandom(); // 使用SM2的算法區域初始化密鑰生成器 kpg.initialize(sm2Spec, random); // 獲取密鑰對 KeyPair keyPair = kpg.generateKeyPair(); return keyPair; } /** * 加密 * * @param input 待加密文本 * @param pubKey 公鑰 * @return */ public String encode(String input, String pubKey) throws NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException, InvalidKeySpecException, InvalidKeyException { // 獲取SM2相關參數 X9ECParameters parameters = GMNamedCurves.getByName("sm2p256v1"); // 橢圓曲線參數規格 ECParameterSpec ecParameterSpec = new ECParameterSpec(parameters.getCurve(), parameters.getG(), parameters.getN(), parameters.getH()); // 將公鑰HEX字符串轉換為橢圓曲線對應的點 ECPoint ecPoint = parameters.getCurve().decodePoint(Hex.decode(pubKey)); // 獲取橢圓曲線KEY生成器 KeyFactory keyFactory = KeyFactory.getInstance("EC", provider); BCECPublicKey key = (BCECPublicKey) keyFactory.generatePublic(new ECPublicKeySpec(ecPoint, ecParameterSpec)); // 獲取SM2加密器 Cipher cipher = Cipher.getInstance("SM2", provider); // 初始化為加密模式 cipher.init(Cipher.ENCRYPT_MODE, key); // 加密並編碼為base64格式 return Base64.getEncoder().encodeToString(cipher.doFinal(input.getBytes())); } /** * 解密 * * @param input 待解密文本 * @param prvKey 私鑰 * @return */ public byte[] decoder(String input, String prvKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { // 獲取SM2加密器 Cipher cipher = Cipher.getInstance("SM2", provider); // 將私鑰HEX字符串轉換為X值 BigInteger bigInteger = new BigInteger(prvKey, 16); BCECPrivateKey privateKey = (BCECPrivateKey) keyFactory.generatePrivate(new ECPrivateKeySpec(bigInteger, ecParameterSpec)); // 初始化為解密模式 cipher.init(Cipher.DECRYPT_MODE, privateKey); // 解密 return cipher.doFinal(Base64.getDecoder().decode(input)); } /** * 簽名 * * @param plainText 待簽名文本 * @param prvKey 私鑰 * @return * @throws NoSuchAlgorithmException * @throws InvalidKeySpecException * @throws InvalidKeyException * @throws SignatureException */ public String sign(String plainText, String prvKey) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException { // 創建簽名對象 Signature signature = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), provider); // 將私鑰HEX字符串轉換為X值 BigInteger bigInteger = new BigInteger(prvKey, 16); BCECPrivateKey privateKey = (BCECPrivateKey) keyFactory.generatePrivate(new ECPrivateKeySpec(bigInteger, ecParameterSpec)); // 初始化為簽名狀態 signature.initSign(privateKey); // 傳入簽名字節 signature.update(plainText.getBytes()); // 簽名 return Base64.getEncoder().encodeToString(signature.sign()); } public boolean verify(String plainText, String signatureValue, String pubKey) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException { // 創建簽名對象 Signature signature = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), provider); // 將公鑰HEX字符串轉換為橢圓曲線對應的點 ECPoint ecPoint = parameters.getCurve().decodePoint(Hex.decode(pubKey)); BCECPublicKey key = (BCECPublicKey) keyFactory.generatePublic(new ECPublicKeySpec(ecPoint, ecParameterSpec)); // 初始化為驗簽狀態 signature.initVerify(key); signature.update(plainText.getBytes()); return signature.verify(Base64.getDecoder().decode(signatureValue)); } /** * 證書驗簽 * * @param certStr 證書串 * @param plaintext 簽名原文 * @param signValueStr 簽名產生簽名值 此處的簽名值實際上就是 R和S的sequence * @return * @throws NoSuchAlgorithmException * @throws InvalidKeyException * @throws SignatureException */ public boolean certVerify(String certStr, String plaintext, String signValueStr) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, CertificateException { byte[] signValue = Base64.getDecoder().decode(signValueStr); /* * 解析證書 */ CertificateFactory factory = new CertificateFactory(); X509Certificate certificate = (X509Certificate) factory .engineGenerateCertificate(new ByteArrayInputStream(Base64.getDecoder().decode(certStr))); // 驗證簽名 Signature signature = Signature.getInstance(certificate.getSigAlgName(), provider); signature.initVerify(certificate); signature.update(plaintext.getBytes()); return signature.verify(signValue); } public static void main(String[] args) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException { String str = "看看能不能一次通過"; SM2Util sm2 = new SM2Util(); KeyPair keyPair = sm2.generateSm2KeyPair(); BCECPrivateKey privateKey = (BCECPrivateKey) keyPair.getPrivate(); BCECPublicKey publicKey = (BCECPublicKey) keyPair.getPublic(); // 拿到密鑰 String pubKey = new String(Hex.encode(publicKey.getQ().getEncoded(true))); String prvKey = privateKey.getD().toString(16); System.out.println("Private Key: " + prvKey); System.out.println("Public Key: " + pubKey); // 加解密測試 try { System.out.println("加密前:" + str); String encode = sm2.encode(str, pubKey); System.out.println("加密后:" + encode); String decoder = new String(sm2.decoder(encode, prvKey)); System.out.println("解密后:" + decoder); } catch (Exception e) { System.out.println("加解密測試錯誤"); } // 簽名和驗簽測試 try { System.out.println("簽名源數據:" + str); String signStr = sm2.sign(str, prvKey); System.out.println("簽名后數據:" + signStr); boolean verify = sm2.verify(str, signStr, pubKey); System.out.println("簽名驗證結果:" + verify); } catch (Exception e) { System.out.println("簽名和驗簽測試錯誤"); } } }