Java實現常用加密演算法-SM4

有点懒惰的大青年發表於2024-11-11

參考部落格:https://blog.csdn.net/m0_46713218/article/details/143099878

參考部落格:sm4前後端加密整合

pom:

<!-- SM4加密依賴包 -->
<dependency>
   <groupId>org.bouncycastle</groupId>
   <artifactId>bcprov-jdk18on</artifactId>
   <version>1.78.1</version>
</dependency>

SM4加解密工具類:

package com.test.encrypt;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Base64;

/**
 * @description com.test.encrypt
 * @author: chengyu
 * @date: 2024-11-11 19:34
 */
public class EncryptUtils {

    /**
     * 指定加密演算法
     */
    private static final String ALGORITHM_NAME = "SM4";

    /**
     * BC中SM4預設使用ECB模式和PKCS5Padding填充方式
     */
    private static final String ALGORITHM_ECB_PKCS5PADDING = "SM4/ECB/PKCS5Padding";

    /**
     * SM4演算法目前只支援128位(即金鑰16位元組)
     */
    private static final int DEFAULT_KEY_SIZE = 128;

    static {
        // 防止記憶體中出現多次BouncyCastleProvider的例項
        if (null == Security.getProvider(BouncyCastleProvider.PROVIDER_NAME)) {
            Security.addProvider(new BouncyCastleProvider());
        }
    }

    /**
     * 生成金鑰
     * <p>建議使用org.bouncycastle.util.encoders.Hex將二進位制轉成HEX字串</p>
     *
     * @return 金鑰16位
     * @throws Exception 生成金鑰異常
     */
    public static String generateKey() throws Exception {
        KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME);
        kg.init(DEFAULT_KEY_SIZE, new SecureRandom());
        return Hex.toHexString(kg.generateKey().getEncoded());
    }

    /**
     * SM4對稱加解密  加密
     *
     * @param plainString 待加密的字串
     * @param key  金鑰
     * @return  密文
     */
    public static String sm4Encrypt(String plainString, String key) {
        String cipherString = null;
        try {
            // 建立金鑰規範
            SecretKeySpec secretKeySpec = new SecretKeySpec(hexStringToBytes(key), ALGORITHM_NAME);
            // 獲取Cipher物件例項
            Cipher cipher = Cipher.getInstance(ALGORITHM_ECB_PKCS5PADDING, BouncyCastleProvider.PROVIDER_NAME);
            // 初始化Cipher為加密模式
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
            // 獲取加密byte陣列
            byte[] cipherBytes = cipher.doFinal(plainString.getBytes(StandardCharsets.UTF_8));
            // 輸出為Base64編碼
            cipherString = Base64.getEncoder().encodeToString(cipherBytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return cipherString;
    }

    /**
     * SM4對稱加解密 解密
     * @param cipherString  密文
     * @param key   金鑰
     * @return  明文
     */
    public static String sm4Decrypt(String cipherString, String key) {
        String plainString = null;
        try {
            // 建立金鑰規範
            SecretKeySpec secretKeySpec = new SecretKeySpec(hexStringToBytes(key), ALGORITHM_NAME);
            // 獲取Cipher物件例項
            Cipher cipher = Cipher.getInstance(ALGORITHM_ECB_PKCS5PADDING, BouncyCastleProvider.PROVIDER_NAME);
            // 初始化Cipher為解密模式
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
            // 獲取加密byte陣列
            byte[] cipherBytes = cipher.doFinal(Base64.getDecoder().decode(cipherString));
            // 輸出為字串
            plainString = new String(cipherBytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return plainString;
    }

    public static byte[] hexStringToBytes(String hexString) {
        if (hexString != null && !hexString.equals("")) {
            hexString = hexString.toUpperCase();
            if (hexString.length() % 2 != 0) {
                hexString = "0" + hexString;
            }

            int length = hexString.length() / 2;
            char[] hexChars = hexString.toCharArray();
            byte[] d = new byte[length];

            for(int i = 0; i < length; ++i) {
                int pos = i * 2;
                d[i] = (byte)(charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
            }

            return d;
        } else {
            return null;
        }
    }

    private static byte charToByte(char c) {
        return (byte)"0123456789ABCDEF".indexOf(c);
    }
}

測試類:

package com.test.encrypt;

import java.util.Locale;

/**
 * @description com.test.encrypt
 * @author: chengyu
 * @date: 2024-11-11 19:32
 */
public class TestSm4 {

    /**
     * 預設國密sm4 key值,128bit=32位16進位制字串
     */
    public static final String SM4_HEX_KEY_NJTK = "A7C9D1A8D93E6CFD7A175D1505598B1E";

    public static void main(String[] args) throws Exception {
        String key = EncryptUtils.generateKey().toUpperCase();
        System.out.println("生成的金鑰key: " + key);

        String data = "P1sw@d4Dw";
        String encrypt = EncryptUtils.sm4Encrypt(data, SM4_HEX_KEY_NJTK);
        System.out.println("使用SM4開源包加密後:" + encrypt);
        String decrypt = EncryptUtils.sm4Decrypt(encrypt, SM4_HEX_KEY_NJTK);
        System.out.println("使用SM4開源包解密後:" + decrypt);

        String decrypt2 = AlgUtil.decryptBySm4(SM4_HEX_KEY_NJTK, encrypt);
        System.out.println("使用SM4 KL包解密後:" + decrypt2);
    }


}

--

相關文章