Java實(shí)現(xiàn)國產(chǎn)加密算法SM4的示例詳解
國產(chǎn)SM4加密解密算法概念
SMS4算法是在國內(nèi)廣泛使用的WAPI無線網(wǎng)絡(luò)標(biāo)準(zhǔn)中使用的加密算法,是一種32輪的迭代非平衡Feistel結(jié)構(gòu)的分組加密算法,其密鑰長(zhǎng)度和分組長(zhǎng)度均為128。SMS4算法的加解密過程中使用的算法是完全相同的,唯一不同點(diǎn)在于該算法的解密密鑰是由它的加密密鑰進(jìn)行逆序變換后得到的。
SMS4分組加密算法是中國無線標(biāo)準(zhǔn)中使用的分組加密算法,在2012年已經(jīng)被國家商用密碼管理局確定為國家密碼行業(yè)標(biāo)準(zhǔn),標(biāo)準(zhǔn)編號(hào)GM/T 0002-2012并且改名為SM4算法,與SM2橢圓曲線公鑰密碼算法,SM3密碼雜湊算法共同作為國家密碼的行業(yè)標(biāo)準(zhǔn),在我國密碼行業(yè)中有著極其重要的位置。
SMS4算法的分組長(zhǎng)度為128bit,密鑰長(zhǎng)度也是128bit。加解密算法均采用32輪非平衡Feistel迭代結(jié)構(gòu),該結(jié)構(gòu)最先出現(xiàn)在分組密碼LOKI的密鑰擴(kuò)展算法中。
SMS4通過32輪非線性迭代后加上一個(gè)反序變換,這樣只需要解密密鑰是加密密鑰的逆序,就能使得解密算法與加密算法保持一致。SMS4加解密算法的結(jié)構(gòu)完全相同,只是在使用輪密鑰時(shí)解密密鑰是加密密鑰的逆序。
S盒是一種利用非線性變換構(gòu)造的分組密碼的一個(gè)組件,主要是為了實(shí)現(xiàn)分組密碼過程中的混淆的特性和設(shè)計(jì)的。SMS4算法中的S盒在設(shè)計(jì)之初完全按照歐美分組密碼的設(shè)計(jì)標(biāo)準(zhǔn)進(jìn)行,它采用的方法是能夠很好抵抗差值攻擊的仿射函數(shù)逆映射復(fù)合法。
1.SM4/ECB/PKCS5Padding
實(shí)現(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個(gè)字節(jié)為一組進(jìn)行分組加密
// 定義分組加密模式使用:PKCS5Padding
public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";
// 128-32位16進(jìn)制;256-64位16進(jìn)制
public static final int DEFAULT_KEY_SIZE = 128;
/**
* 自動(dòng)生成密鑰
*
* @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暗號(hào)
*
* @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進(jìn)制密鑰(忽略大小寫)
* @param paramStr 待加密字符串
* @return 返回16進(jìn)制的加密字符串
* @explain 加密模式:ECB
* 密文長(zhǎng)度不固定,會(huì)隨著被加密字符串長(zhǎng)度的變化而變化
*/
public static String encryptEcb(String hexKey, String paramStr) {
try {
String cipherText = "";
// 16進(jìn)制字符串-->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進(jìn)制密鑰
* @param cipherText 16進(jìn)制的加密字符串(忽略大小寫)
* @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);
}
/**
* 校驗(yàn)加密前后的字符串是否為同一數(shù)據(jù)
*
* @param hexKey 16進(jìn)制密鑰(忽略大小寫)
* @param cipherText 16進(jìn)制加密后的字符串
* @param paramStr 加密前的字符串
* @return 是否為同一數(shù)據(jù)
* @throws Exception
* @explain
*/
public static boolean verifyEcb(String hexKey, String cipherText, String paramStr) throws Exception {
// 用于接收校驗(yàn)結(jié)果
boolean flag = false;
// hexString-->byte[]
byte[] keyData = ByteUtils.fromHexString(hexKey);
// 將16進(jìn)制字符串轉(zhuǎn)換成數(shù)組
byte[] cipherData = ByteUtils.fromHexString(cipherText);
// 解密
byte[] decryptData = decryptEcbPadding(keyData, cipherData);
// 將原字符串轉(zhuǎn)換成byte[]
byte[] srcData = paramStr.getBytes(ENCODING);
// 判斷2個(gè)數(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進(jìn)制密鑰
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個(gè)字節(jié)為一組進(jìn)行分組加密
// 定義分組加密模式使用:PKCS5Padding
public static final String ALGORITHM_NAME_CBC_PADDING = "SM4/CBC/PKCS5Padding";
// 128-32位16進(jìn)制;256-64位16進(jìn)制
public static final int DEFAULT_KEY_SIZE = 128;
/**
* 自動(dòng)生成密鑰
*
* @return
* @explain
*/
public static byte[] generateKey() throws Exception {
return generateKey(DEFAULT_KEY_SIZE);
}
/**
* 自動(dòng)生成密鑰
* @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進(jìn)制密鑰(忽略大小寫)
* @param paramStr 待加密字符串
* @return 返回16進(jìn)制的加密字符串
* @throws Exception
* @explain 加密模式:CBC
*/
public static String encrypt(String hexKey, String paramStr) throws Exception {
String result = "";
// 16進(jìn)制字符串-->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 為一個(gè) 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進(jìn)制密鑰
* @param text 16進(jìn)制的加密字符串(忽略大小寫)
* @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實(shí)現(xiàn)國產(chǎn)加密算法SM4的示例詳解的文章就介紹到這了,更多相關(guān)Java加密算法SM4內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于Spring Boot保護(hù)Web應(yīng)用程序
這篇文章主要介紹了基于Spring Boot保護(hù)Web應(yīng)用程序,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03
Java保留兩位小數(shù)的實(shí)現(xiàn)方法
這篇文章主要介紹了 Java保留兩位小數(shù)的實(shí)現(xiàn)方法的相關(guān)資料,需要的朋友可以參考下2017-06-06
Java讀取txt文件中的數(shù)據(jù)賦給String變量方法
今天小編就為大家分享一篇Java讀取txt文件中的數(shù)據(jù)賦給String變量方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-07-07
SpringBoot中驗(yàn)證用戶上傳的圖片資源的方法
這篇文章主要介紹了在SpringBoot中驗(yàn)證用戶上傳的圖片資源,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09
java循環(huán)練習(xí)的簡(jiǎn)單代碼實(shí)例
本篇文章介紹了,java中循環(huán)練習(xí)的一些簡(jiǎn)單代碼實(shí)例。需要的朋友參考下2013-04-04
SpringBoot創(chuàng)建線程池的六種方式小結(jié)
本文主要介紹了SpringBoot創(chuàng)建線程池的六種方式小結(jié),包括自定義線程池,固定長(zhǎng)度線程池,單一線程池,共享線程池,定時(shí)線程池,SpringBoot中注入異步線程池,感興趣的可以了解一下2023-11-11
Java編程實(shí)現(xiàn)游戲中的簡(jiǎn)單碰撞檢測(cè)功能示例
這篇文章主要介紹了Java編程中的簡(jiǎn)單碰撞檢測(cè)功能,涉及java針對(duì)坐標(biāo)點(diǎn)的相關(guān)數(shù)學(xué)運(yùn)算操作技巧,需要的朋友可以參考下2017-10-10

