bouncycastle(BC) 實現SM2國密加解密、簽名、驗簽


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("簽名和驗簽測試錯誤");
        }
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM