Java實現(xiàn)國產(chǎn)加密算法SM4的示例詳解
國產(chǎn)SM4加密解密算法概念
SMS4算法是在國內(nèi)廣泛使用的WAPI無線網(wǎng)絡(luò)標準中使用的加密算法,是一種32輪的迭代非平衡Feistel結(jié)構(gòu)的分組加密算法,其密鑰長度和分組長度均為128。SMS4算法的加解密過程中使用的算法是完全相同的,唯一不同點在于該算法的解密密鑰是由它的加密密鑰進行逆序變換后得到的。
SMS4分組加密算法是中國無線標準中使用的分組加密算法,在2012年已經(jīng)被國家商用密碼管理局確定為國家密碼行業(yè)標準,標準編號GM/T 0002-2012并且改名為SM4算法,與SM2橢圓曲線公鑰密碼算法,SM3密碼雜湊算法共同作為國家密碼的行業(yè)標準,在我國密碼行業(yè)中有著極其重要的位置。
SMS4算法的分組長度為128bit,密鑰長度也是128bit。加解密算法均采用32輪非平衡Feistel迭代結(jié)構(gòu),該結(jié)構(gòu)最先出現(xiàn)在分組密碼LOKI的密鑰擴展算法中。
SMS4通過32輪非線性迭代后加上一個反序變換,這樣只需要解密密鑰是加密密鑰的逆序,就能使得解密算法與加密算法保持一致。SMS4加解密算法的結(jié)構(gòu)完全相同,只是在使用輪密鑰時解密密鑰是加密密鑰的逆序。
S盒是一種利用非線性變換構(gòu)造的分組密碼的一個組件,主要是為了實現(xiàn)分組密碼過程中的混淆的特性和設(shè)計的。SMS4算法中的S盒在設(shè)計之初完全按照歐美分組密碼的設(shè)計標準進行,它采用的方法是能夠很好抵抗差值攻擊的仿射函數(shù)逆映射復(fù)合法。
1.SM4/ECB/PKCS5Padding
實現(xiàn)代碼
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; import org.bouncycastle.util.encoders.Hex; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.spec.SecretKeySpec; import java.security.Key; import java.security.SecureRandom; import java.security.Security; import java.util.Arrays; /** * Sm4 國密算法 * */ public final class Sm4Utils { static { Security.addProvider(new BouncyCastleProvider()); } private static final String ENCODING = "UTF-8"; public static final String ALGORITHM_NAME = "SM4"; // 加密算法/分組加密模式/分組填充方式 // PKCS5Padding-以8個字節(jié)為一組進行分組加密 // 定義分組加密模式使用:PKCS5Padding public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding"; // 128-32位16進制;256-64位16進制 public static final int DEFAULT_KEY_SIZE = 128; /** * 自動生成密鑰 * * @return * @explain */ public static String generateKey() throws Exception { return new String(Hex.encode(generateKey(DEFAULT_KEY_SIZE))); } /** * @param keySize * @return * @throws Exception * @explain */ public static byte[] generateKey(int keySize) throws Exception { KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME); kg.init(keySize, new SecureRandom()); return kg.generateKey().getEncoded(); } /** * 生成ECB暗號 * * @param algorithmName 算法名稱 * @param mode 模式 * @param key * @return * @throws Exception * @explain ECB模式(電子密碼本模式:Electronic codebook) */ private static Cipher generateEcbCipher(String algorithmName, int mode, byte[] key) throws Exception { Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME); Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME); cipher.init(mode, sm4Key); return cipher; } /** * sm4加密 * * @param hexKey 16進制密鑰(忽略大小寫) * @param paramStr 待加密字符串 * @return 返回16進制的加密字符串 * @explain 加密模式:ECB * 密文長度不固定,會隨著被加密字符串長度的變化而變化 */ public static String encryptEcb(String hexKey, String paramStr) { try { String cipherText = ""; // 16進制字符串-->byte[] byte[] keyData = ByteUtils.fromHexString(hexKey); // String-->byte[] byte[] srcData = paramStr.getBytes(ENCODING); // 加密后的數(shù)組 byte[] cipherArray = encryptEcbPadding(keyData, srcData); // byte[]-->hexString cipherText = ByteUtils.toHexString(cipherArray); return cipherText; } catch (Exception e) { return paramStr; } } /** * 加密模式之Ecb * * @param key * @param data * @return * @throws Exception * @explain */ public static byte[] encryptEcbPadding(byte[] key, byte[] data) throws Exception { Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key); return cipher.doFinal(data); } /** * sm4解密 * * @param hexKey 16進制密鑰 * @param cipherText 16進制的加密字符串(忽略大小寫) * @return 解密后的字符串 * @throws Exception * @explain 解密模式:采用ECB */ public static String decryptEcb(String hexKey, String cipherText) { // 用于接收解密后的字符串 String decryptStr = ""; // hexString-->byte[] byte[] keyData = ByteUtils.fromHexString(hexKey); // hexString-->byte[] byte[] cipherData = ByteUtils.fromHexString(cipherText); // 解密 byte[] srcData = new byte[0]; try { srcData = decryptEcbPadding(keyData, cipherData); // byte[]-->String decryptStr = new String(srcData, ENCODING); } catch (Exception e) { e.printStackTrace(); } return decryptStr; } /** * 解密 * * @param key * @param cipherText * @return * @throws Exception * @explain */ public static byte[] decryptEcbPadding(byte[] key, byte[] cipherText) throws Exception { Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key); return cipher.doFinal(cipherText); } /** * 校驗加密前后的字符串是否為同一數(shù)據(jù) * * @param hexKey 16進制密鑰(忽略大小寫) * @param cipherText 16進制加密后的字符串 * @param paramStr 加密前的字符串 * @return 是否為同一數(shù)據(jù) * @throws Exception * @explain */ public static boolean verifyEcb(String hexKey, String cipherText, String paramStr) throws Exception { // 用于接收校驗結(jié)果 boolean flag = false; // hexString-->byte[] byte[] keyData = ByteUtils.fromHexString(hexKey); // 將16進制字符串轉(zhuǎn)換成數(shù)組 byte[] cipherData = ByteUtils.fromHexString(cipherText); // 解密 byte[] decryptData = decryptEcbPadding(keyData, cipherData); // 將原字符串轉(zhuǎn)換成byte[] byte[] srcData = paramStr.getBytes(ENCODING); // 判斷2個數(shù)組是否一致 flag = Arrays.equals(decryptData, srcData); return flag; } public static void main(String[] args) { try { String paramStr = "Hello, world"; System.out.println("==========加密前源數(shù)據(jù)=========="); System.out.println(paramStr); // 生成32位16進制密鑰 String key = Sm4Utils.generateKey(); System.out.println("==========生成key=========="); System.out.println(key); String cipher = Sm4Utils.encryptEcb(key, paramStr); System.out.println("==========加密串=========="); System.out.println(cipher); System.out.println("==========是否為同一數(shù)據(jù)=========="); System.out.println(Sm4Utils.verifyEcb(key, cipher, paramStr)); paramStr = Sm4Utils.decryptEcb(key, cipher); System.out.println("==========解密后數(shù)據(jù)=========="); System.out.println(paramStr); } catch (Exception e) { e.printStackTrace(); } } }
結(jié)果如下:
2.SM4/CBC/PKCS5Padding
示例代碼
<dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15to18</artifactId> <version>1.68</version> </dependency>
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.security.AlgorithmParameters; import java.security.Key; import java.security.SecureRandom; import java.security.Security; import java.util.Arrays; /** * Sm4 國密算法 * */ public final class Sm4Util { static { Security.addProvider(new BouncyCastleProvider()); } private static final String ENCODING = "UTF-8"; public static final String ALGORITHM_NAME = "SM4"; // 加密算法/分組加密模式/分組填充方式 // PKCS5Padding-以8個字節(jié)為一組進行分組加密 // 定義分組加密模式使用:PKCS5Padding public static final String ALGORITHM_NAME_CBC_PADDING = "SM4/CBC/PKCS5Padding"; // 128-32位16進制;256-64位16進制 public static final int DEFAULT_KEY_SIZE = 128; /** * 自動生成密鑰 * * @return * @explain */ public static byte[] generateKey() throws Exception { return generateKey(DEFAULT_KEY_SIZE); } /** * 自動生成密鑰 * @return * @throws Exception */ public static String generateKeyString() throws Exception { return ByteUtils.toHexString(generateKey()); } /** * @param keySize * @return * @throws Exception * @explain */ public static byte[] generateKey(int keySize) throws Exception { KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME); kg.init(keySize, new SecureRandom()); return kg.generateKey().getEncoded(); } /** * sm4加密 * * @param hexKey 16進制密鑰(忽略大小寫) * @param paramStr 待加密字符串 * @return 返回16進制的加密字符串 * @throws Exception * @explain 加密模式:CBC */ public static String encrypt(String hexKey, String paramStr) throws Exception { String result = ""; // 16進制字符串-->byte[] byte[] keyData = ByteUtils.fromHexString(hexKey); // String-->byte[] byte[] srcData = paramStr.getBytes(ENCODING); // 加密后的數(shù)組 byte[] cipherArray = encryptCbcPadding(keyData, srcData); // byte[]-->hexString result = ByteUtils.toHexString(cipherArray); return result; } /** * 加密模式之CBC * * @param key * @param data * @return * @throws Exception * @explain */ public static byte[] encryptCbcPadding(byte[] key, byte[] data) throws Exception { Cipher cipher = generateCbcCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.ENCRYPT_MODE, key); return cipher.doFinal(data); } /** * 加密模式之CBC * @param algorithmName * @param mode * @param key * @return * @throws Exception */ private static Cipher generateCbcCipher(String algorithmName, int mode, byte[] key) throws Exception { Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME); Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME); cipher.init(mode, sm4Key, generateIV()); return cipher; } /** * 生成iv * @return * @throws Exception */ public static AlgorithmParameters generateIV() throws Exception { //iv 為一個 16 字節(jié)的數(shù)組,這里采用和 iOS 端一樣的構(gòu)造方法,數(shù)據(jù)全為0 byte[] iv = new byte[16]; Arrays.fill(iv, (byte) 0x00); AlgorithmParameters params = AlgorithmParameters.getInstance(ALGORITHM_NAME); params.init(new IvParameterSpec(iv)); return params; } /** * sm4解密 * * @param hexKey 16進制密鑰 * @param text 16進制的加密字符串(忽略大小寫) * @return 解密后的字符串 * @throws Exception * @explain 解密模式:采用CBC */ public static String decrypt(String hexKey, String text) throws Exception { // 用于接收解密后的字符串 String result = ""; // hexString-->byte[] byte[] keyData = ByteUtils.fromHexString(hexKey); // hexString-->byte[] byte[] resultData = ByteUtils.fromHexString(text); // 解密 byte[] srcData = decryptCbcPadding(keyData, resultData); // byte[]-->String result = new String(srcData, ENCODING); return result; } /** * 解密 * * @param key * @param cipherText * @return * @throws Exception * @explain */ public static byte[] decryptCbcPadding(byte[] key, byte[] cipherText) throws Exception { Cipher cipher = generateCbcCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.DECRYPT_MODE, key); return cipher.doFinal(cipherText); } public static void main(String[] args) throws Exception { String str = "Hello, world" ; System.out.println("==========生成密鑰=========="); String generateKey = generateKeyString(); System.out.println(generateKey); System.out.println("==========加密=========="); String encrypt = encrypt(generateKey, str); System.out.println(encrypt); System.out.println("==========解密=========="); String decrypt = decrypt(generateKey, encrypt); System.out.println(decrypt); } }
結(jié)果如下
到此這篇關(guān)于Java實現(xiàn)國產(chǎn)加密算法SM4的示例詳解的文章就介紹到這了,更多相關(guān)Java加密算法SM4內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java讀取txt文件中的數(shù)據(jù)賦給String變量方法
今天小編就為大家分享一篇Java讀取txt文件中的數(shù)據(jù)賦給String變量方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-07-07SpringBoot創(chuàng)建線程池的六種方式小結(jié)
本文主要介紹了SpringBoot創(chuàng)建線程池的六種方式小結(jié),包括自定義線程池,固定長度線程池,單一線程池,共享線程池,定時線程池,SpringBoot中注入異步線程池,感興趣的可以了解一下2023-11-11