sm4算法加密解密
一、基本知识
- 分组加密(英语:Block cipher),又称分块加密或块密码,是一种对称密钥算法。它将明文分成多个等长的模块(block),使用确定的算法和对称密钥对每组分别加密解密。
- 对称加密、加密算法主要可以分为两种,一个是非对称加密算法,另一个就是对称加密算法。对称加密简单来说就是有一个明文,通过一个密钥加密之后得到一个密文,这个密文可以通过相同的密要解密得出和原来相同的明文
二、sm4算法
- 算法定义:SM4算法是一种分组密码算法。其分组长度为128bit,密钥长度也为128bit。加密算法与密钥扩展算法均采用32轮非线性迭代结构,以字(32位)为单位进行加密运算,每一次迭代运算均为一轮变换函数F。SM4算法加/解密算法的结构相同,只是使用轮密钥相反,其中解密轮密钥是加密轮密钥的逆序。
- 基本运算
⊕ 异或
<<<i 循环左移i位
3.算法实现
即首先执行32次轮函数迭代运算,然后在对最后一轮数据反序变换并得到密文输出
三、步骤详解
1.轮函数F
示意图
SM4的轮函数将输入部分看做了4个32bit长度的数据,每轮的后3个部分都向左移动32bit的数据长度,这三组数据异或后进入非线性部分τ和线性部分L,运算后的结果与第一组数据异或置于最右面。如此循环往复32轮,也就是数据一共左移了8个周期,将其中的混乱因素不断扩散至每个bit位中
2.T函数
s盒如下:
使用规则:
3.秘钥扩展算法
系统参数FK的取值
FK0=(A3B1BAC6),FK1=(56AA3350),FK2=(677D9197),FK3=(B27022DC)
固定参数CK的取值
00070e15, 1c232a31, 383f464d, 545b6269,
70777e85, 8c939aa1, a8afb6bd, c4cbd2d9,
e0e7eef5, fc030a11, 181f262d, 343b4249,
50575e65, 6c737a81, 888f969d, a4abb2b9,
c0c7ced5, dce3eaf1, f8ff060d, 141b2229,
30373e45, 4c535a61, 686f767d, 848b9299,
a0a7aeb5, bcc3cad1, d8dfe6ed, f4fb0209,
10171e25, 2c333a41, 484f565d, 646b7279
四、java代码实现
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.Random;
/**
* @ClassName: Sm4Util
* @Author: tanp
* @Description: sm4加解密工具类
* @Date: 2020/5/8 15:24
*/
public class Sm4Util {
/**
* 加密标识
*/
private static final int ENCRYPT = 1;
/**
* 解密标识
*/
private static final int DECRYPT = 0;
/**
* F轮函数循环次数
*/
private static final int ROUND = 32;
private static final int BLOCK = 16;
private static final int FOUR = 4;
private static final int ZERO = 0;
/**
* s盒
*/
private byte[] sbox = {(byte) 0xd6, (byte) 0x90, (byte) 0xe9, (byte) 0xfe,
(byte) 0xcc, (byte) 0xe1, 0x3d, (byte) 0xb7, 0x16, (byte) 0xb6,
0x14, (byte) 0xc2, 0x28, (byte) 0xfb, 0x2c, 0x05, 0x2b, 0x67,
(byte) 0x9a, 0x76, 0x2a, (byte) 0xbe, 0x04, (byte) 0xc3,
(byte) 0xaa, 0x44, 0x13, 0x26, 0x49, (byte) 0x86, 0x06,
(byte) 0x99, (byte) 0x9c, 0x42, 0x50, (byte) 0xf4, (byte) 0x91,
(byte) 0xef, (byte) 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43,
(byte) 0xed, (byte) 0xcf, (byte) 0xac, 0x62, (byte) 0xe4,
(byte) 0xb3, 0x1c, (byte) 0xa9, (byte) 0xc9, 0x08, (byte) 0xe8,
(byte) 0x95, (byte) 0x80, (byte) 0xdf, (byte) 0x94, (byte) 0xfa,
0x75, (byte) 0x8f, 0x3f, (byte) 0xa6, 0x47, 0x07, (byte) 0xa7,
(byte) 0xfc, (byte) 0xf3, 0x73, 0x17, (byte) 0xba, (byte) 0x83,
0x59, 0x3c, 0x19, (byte) 0xe6, (byte) 0x85, 0x4f, (byte) 0xa8,
0x68, 0x6b, (byte) 0x81, (byte) 0xb2, 0x71, 0x64, (byte) 0xda,
(byte) 0x8b, (byte) 0xf8, (byte) 0xeb, 0x0f, 0x4b, 0x70, 0x56,
(byte) 0x9d, 0x35, 0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, (byte) 0xd1,
(byte) 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, (byte) 0x87,
(byte) 0xd4, 0x00, 0x46, 0x57, (byte) 0x9f, (byte) 0xd3, 0x27,
0x52, 0x4c, 0x36, 0x02, (byte) 0xe7, (byte) 0xa0, (byte) 0xc4,
(byte) 0xc8, (byte) 0x9e, (byte) 0xea, (byte) 0xbf, (byte) 0x8a,
(byte) 0xd2, 0x40, (byte) 0xc7, 0x38, (byte) 0xb5, (byte) 0xa3,
(byte) 0xf7, (byte) 0xf2, (byte) 0xce, (byte) 0xf9, 0x61, 0x15,
(byte) 0xa1, (byte) 0xe0, (byte) 0xae, 0x5d, (byte) 0xa4,
(byte) 0x9b, 0x34, 0x1a, 0x55, (byte) 0xad, (byte) 0x93, 0x32,
0x30, (byte) 0xf5, (byte) 0x8c, (byte) 0xb1, (byte) 0xe3, 0x1d,
(byte) 0xf6, (byte) 0xe2, 0x2e, (byte) 0x82, 0x66, (byte) 0xca,
0x60, (byte) 0xc0, 0x29, 0x23, (byte) 0xab, 0x0d, 0x53, 0x4e, 0x6f,
(byte) 0xd5, (byte) 0xdb, 0x37, 0x45, (byte) 0xde, (byte) 0xfd,
(byte) 0x8e, 0x2f, 0x03, (byte) 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b,
0x51, (byte) 0x8d, 0x1b, (byte) 0xaf, (byte) 0x92, (byte) 0xbb,
(byte) 0xdd, (byte) 0xbc, 0x7f, 0x11, (byte) 0xd9, 0x5c, 0x41,
0x1f, 0x10, 0x5a, (byte) 0xd8, 0x0a, (byte) 0xc1, 0x31,
(byte) 0x88, (byte) 0xa5, (byte) 0xcd, 0x7b, (byte) 0xbd, 0x2d,
0x74, (byte) 0xd0, 0x12, (byte) 0xb8, (byte) 0xe5, (byte) 0xb4,
(byte) 0xb0, (byte) 0x89, 0x69, (byte) 0x97, 0x4a, 0x0c,
(byte) 0x96, 0x77, 0x7e, 0x65, (byte) 0xb9, (byte) 0xf1, 0x09,
(byte) 0xc5, 0x6e, (byte) 0xc6, (byte) 0x84, 0x18, (byte) 0xf0,
0x7d, (byte) 0xec, 0x3a, (byte) 0xdc, 0x4d, 0x20, 0x79,
(byte) 0xee, 0x5f, 0x3e, (byte) 0xd7, (byte) 0xcb, 0x39, 0x48};
/**
* 固定的ck参数
*/
private int[] ck = {0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9, 0xe0e7eef5,
0xfc030a11, 0x181f262d, 0x343b4249, 0x50575e65, 0x6c737a81,
0x888f969d, 0xa4abb2b9, 0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d,
0x141b2229, 0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209, 0x10171e25,
0x2c333a41, 0x484f565d, 0x646b7279};
/**
* 固定的fk参数
*/
private int[] fk = {0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc};
/**
* @Description 将传入的x参数循环左移num位
* @Author tanp
*/
private int leftMove(int x, int num) {
//假如数据为无符号的数,长度为N,需要循环移动n位。可以用下面的公式:
//循环左移n位: (x>>(N - n) ) | (x<<n);
return x << num | x >>> (32 - num);
}
/**
* @Description s盒的非线性变化函数
* @Author tanp
*/
private int byteSub(int x) {
return (sbox[x >>> 24 & 0xFF] & 0xFF) << 24
| (sbox[x >>> 16 & 0xFF] & 0xFF) << 16
| (sbox[x >>> 8 & 0xFF] & 0xFF) << 8
| (sbox[x & 0xFF] & 0xFF);
}
/**
* @Description 轮函数中T函数的线性变化L函数
* @Author tanp
*/
private int transLf(int x) {
//B^(B<<2|B>>>30)^(B<<10|B>>>22)^(B<<18|B>>>14)^(B<<24|B>>>8);
return x ^ leftMove(x, 2) ^ leftMove(x, 10) ^ leftMove(x, 18) ^ leftMove(x, 24);
}
/**
* @Description 秘钥扩展算法中的T'函数的线性变化L函数
* @Author tanp
*/
private int transTf(int x) {
// B^(B<<13|B>>>19)^(B<<23|B>>>9);
return x ^ leftMove(x, 13) ^ leftMove(x, 23);
}
/**
* @Description 秘钥扩展算法T置换
* @Author tanp
*/
private int transTh(int in) {
return transTf(byteSub(in));
}
/**
* @Description 轮函数T置换
* @Author tanp
*/
private int transLth(int in) {
return transLf(byteSub(in));
}
/**
* @param key 秘钥
* @param cryptFlag 加解密标识
* @Description 获取轮秘钥
* @Author tanp
*/
private int[] sm4KeyExt(byte[] key, int cryptFlag) {
int r, mid;
int[] x = new int[4];
int[] tmp = new int[4];
int[] rk = new int[ROUND];
getValue(key, x, tmp);
//获取轮秘钥之前,先将秘钥与系统函数FK异或
for (r = ZERO; r < FOUR; r++) {
x[r] = x[r] ^ (fk[r]);
}
//轮训执行T置换
for (r = ZERO; r < ROUND; r += FOUR) {
rk[r] = x[0] ^= transTh(x[1] ^ x[2] ^ x[3] ^ ck[r]);
rk[r + 1] = x[1] ^= transTh(x[2] ^ x[3] ^ x[0] ^ ck[r + 1]);
rk[r + 2] = x[2] ^= transTh(x[3] ^ x[0] ^ x[1] ^ ck[r + 2]);
rk[r + 3] = x[3] ^= transTh(x[0] ^ x[1] ^ x[2] ^ ck[r + 3]);
}
// 解密时轮密钥使用顺序:rk31,rk30,...,rk0
if (cryptFlag == DECRYPT) {
for (r = 0; r < BLOCK; r++) {
mid = rk[r];
rk[r] = rk[31 - r];
rk[31 - r] = mid;
}
}
return rk;
}
/**
* @Description 轮函数F
* @param input 加密解密的字节数组
* @param rk 轮秘钥
* @Author tanp
*/
private void sms4Crypt(byte[] input, byte[] output, int[] rk) {
int r;
int[] x = new int[4];
int[] tmp = new int[4];
getValue(input, x, tmp);
for (r = ZERO; r < ROUND; r += FOUR) {
x[0] = x[0] ^ transLth(x[1] ^ x[2] ^ x[3] ^ rk[r]);
x[1] = x[1] ^ transLth(x[2] ^ x[3] ^ x[0] ^ rk[r + 1]);
x[2] = x[2] ^ transLth(x[3] ^ x[0] ^ x[1] ^ rk[r + 2]);
x[3] = x[3] ^ transLth(x[0] ^ x[1] ^ x[2] ^ rk[r + 3]);
}
// Reverse
for (int j = ZERO; j < BLOCK; j += FOUR) {
output[j] = (byte) (x[3 - j / 4] >>> 24 & 0xFF);
output[j + 1] = (byte) (x[3 - j / 4] >>> 16 & 0xFF);
output[j + 2] = (byte) (x[3 - j / 4] >>> 8 & 0xFF);
output[j + 3] = (byte) (x[3 - j / 4] & 0xFF);
}
}
private void getValue(byte[] input, int[] x, int[] tmp) {
for (int i = ZERO; i < FOUR; i++) {
tmp[0] = input[4 * i] & 0xff;
tmp[1] = input[1 + 4 * i] & 0xff;
tmp[2] = input[2 + 4 * i] & 0xff;
tmp[3] = input[3 + 4 * i] & 0xff;
x[i] = tmp[0] << 24 | tmp[1] << 16 | tmp[2] << 8 | tmp[3];
}
}
/**
* @param in 需加密或者解密的字节数组
* @param inLen 加密解密的字节长度
* @param key 秘钥
* @param out 加密解密后的字节数组,用来返回
* @param cryptFlag 加解密标识
* @Description 加密 解密方法
* @Author tanp
*/
private byte[] sms4(byte[] in, int inLen, byte[] key, byte[] out, int cryptFlag) {
int point = 0;
//获取轮秘钥
int[] roundKey = sm4KeyExt(key, cryptFlag);
byte[] input;
byte[] output = new byte[16];
while (inLen >= BLOCK) {
input = Arrays.copyOfRange(in, point, point + 16);
//每16个字节执行一次轮函数
sms4Crypt(input, output, roundKey);
System.arraycopy(output, 0, out, point, BLOCK);
inLen -= BLOCK;
point += BLOCK;
}
return out;
}
/**
* 字符串加密
*
* @param plaintext 明文
* @param key 秘钥
* @return 加密后的明文字符串
* @Author tanp
*/
private static String encodeSms4ToHex(String plaintext, byte[] key) {
if (plaintext == null || plaintext.length() <= 0) {
return null;
}
StringBuilder plaintextBuilder = new StringBuilder(plaintext);
for (int i = plaintextBuilder.toString().getBytes().length % BLOCK; i < BLOCK; i++) {
plaintextBuilder.append('\0');
}
plaintext = plaintextBuilder.toString();
return bytesToHexString(encodeSms4(plaintext.getBytes(), key));
}
/**
* 字符串形式的密文解密成明文
*
* @param enHex 密文
* @param key 秘钥
* @return 解密后的明文
* @Author tanp
*/
private static String decodeSms4HexToString(String enHex, byte[] key) {
byte[] plaintext = decodeSms4(hexStringToBytes(enHex),key);
return new String(plaintext).trim();
}
/**
* SMS4加密,加密字符数组
*
* @param plaintext 字节数组形式的明文
* @param key 秘钥
* @return 明文加密后的字接数组
*/
private static byte[] encodeSms4(byte[] plaintext, byte[] key) {
byte[] ciphering = new byte[plaintext.length];
int k = 0;
int plainLen = plaintext.length;
while (k + BLOCK <= plainLen) {
byte[] cellPlain = new byte[16];
System.arraycopy(plaintext, k, cellPlain, 0, 16);
byte[] cellCipher = encode16(cellPlain, key);
System.arraycopy(cellCipher, 0, ciphering, k, cellCipher.length);
k += 16;
}
return ciphering;
}
/**
* 不限明文长度的SMS4解密
*
* @param ciphering 需要解密的字节数组
* @param key 秘钥
* @return 解密后的字节数组
*/
private static byte[] decodeSms4(byte[] ciphering, byte[] key) {
byte[] plaintext = new byte[ciphering.length];
int k = 0;
int cipherLen = ciphering.length;
while (k + BLOCK <= cipherLen) {
byte[] cellCipher = new byte[16];
System.arraycopy(ciphering, k, cellCipher, 0, 16);
byte[] cellPlain = decode16(cellCipher, key);
System.arraycopy(cellPlain, 0, plaintext, k, cellPlain.length);
k += BLOCK;
}
return plaintext;
}
/**
* 只加密16位明文
*
* @param plaintext 明文字节数组
* @param key 秘钥
* @return 加密后的字节数组
*/
private static byte[] encode16(byte[] plaintext, byte[] key) {
byte[] cipher = new byte[16];
Sm4Util sm4 = new Sm4Util();
//调用加密方法
return sm4.sms4(plaintext, 16, key, cipher, ENCRYPT);
}
/**
* 只解密16位密文
*
* @param ciphering 需解密的密文
* @param key 秘钥
* @return 解密后的明文字节
*/
private static byte[] decode16(byte[] ciphering, byte[] key) {
byte[] plain = new byte[16];
Sm4Util sm4 = new Sm4Util();
sm4.sms4(ciphering, 16, key, plain, DECRYPT);
return plain;
}
/**
* 只加密32位明文
*
* @param plaintext 明文字节数组
* @param key 秘钥
* @return 加密后的字节数组
*/
private static byte[] encode32(byte[] plaintext, byte[] key) {
byte[] cipher = new byte[32];
Sm4Util sm4 = new Sm4Util();
return sm4.sms4(plaintext, 32, key, cipher, ENCRYPT);
}
/**
* 只解密32位密文
*
* @param ciphering 需解密的密文
* @param key 秘钥
* @return 解密后的明文字节
*/
private static byte[] decode32(byte[] ciphering, byte[] key) {
byte[] plain = new byte[32];
Sm4Util sm4 = new Sm4Util();
sm4.sms4(ciphering, 32, key, plain, DECRYPT);
return plain;
}
/**
* 字节数组转字符串
*
* @param src 字节数组
* @return String
*/
private static String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder();
if (src == null || src.length <= 0) {
return null;
}
for (byte aSrc : src) {
int v = aSrc & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
/**
* 字符串转字节数组
*
* @param hexString the hex string
* @return byte[]
*/
private static byte[] hexStringToBytes(String hexString) {
if (hexString == null || hexString.length() <= 0) {
return null;
}
hexString = hexString.toUpperCase();
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;
}
/**
* Convert char to byte
*
* @param c char
* @return byte
*/
private static byte charToByte(char c) {
return (byte) "0123456789ABCDEF".indexOf(c);
}
/**
* @Description 随机获取中文
* @Date 2020/5/9 14:17
* @Author tanp
*/
private static char getRandomChar() {
String str = "";
int hightPos;
int lowPos;
Random random = new Random();
hightPos = (176 + Math.abs(random.nextInt(39)));
lowPos = (161 + Math.abs(random.nextInt(93)));
byte[] b = new byte[2];
b[0] = (Integer.valueOf(hightPos)).byteValue();
b[1] = (Integer.valueOf(lowPos)).byteValue();
try {
str = new String(b, "GBK");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
System.out.println("错误");
}
return str.charAt(0);
}
public static void main(String[] args) {
final byte[] key = {0x01, 0x23, 0x45, 0x67, (byte) 0x89, (byte) 0xab,
(byte) 0xcd, (byte) 0xef, (byte) 0xfe, (byte) 0xdc,
(byte) 0xba, (byte) 0x98, 0x76, 0x54, 0x32, 0x10};
StringBuilder msg = new StringBuilder();
for(int a=0;a<ROUND;a++){
for (int i = 1; i < ROUND; i++) {
msg.append(getRandomChar());
}
System.out.println("加密前:" + msg.toString());
String enStr = encodeSms4ToHex(msg.toString(), key);
System.out.println("加密后:" + enStr);
String deStr = decodeSms4HexToString(enStr, key);
System.out.println("解密后:" + deStr);
//查看经过加密再解密后的字符串是否与最开始的原生字符串是否一致
System.out.println(msg.toString().equals(deStr));
}
}
}
五、参考文献
SM4分组密码算法:https://wenku.baidu.com/view/e66938055acfa1c7aa00cca7.html?from=search
SM4分组密码算法综述:https://wenku.baidu.com/view/5c900609001ca300a6c30c22590102020640f252.html?fr=search
SM4算法及实现方式:http://www.mamicode.com/info-detail-2603734.html
SM4加密解密:https://blog.csdn.net/Mr_ye931/article/details/105680359
六、总结
本篇博客为查看参考文献中的相关文档学习总结所得,有错误的地方大家多多指教
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/185344.html原文链接:https://javaforall.cn
相关文章
- 激光slam综述_SLAM算法
- 字符串匹配算法_多字符串匹配
- java——加密、解密算法
- Java基础算法详解
- ☆打卡算法☆LeetCode 201. 数字范围按位与 算法解析
- 【算法竞赛 - 数据结构】加权并查集的理解(以POJ1703为例)
- 哈希算法是对称算法还是非对称算法_对称加密和非对称加密原理
- Elgamal加密与签名算法简述
- 程序员进阶之算法练习(六十九)
- ARMA-EGARCH模型、集成预测算法对SPX实际波动率进行预测|附代码数据
- 斯坦福被炮轰:用算法分配5000支新冠疫苗,医护人员只有7支
- 【数据挖掘】聚类 Cluster 简介 ( 概念 | 应用场景 | 质量 | 相似度 | 算法要求 | 数据矩阵 | 相似度矩阵 | 二模矩阵 | 单模矩阵 )
- 【Android 安全】DEX 加密 ( Java 工具开发 | 加密解密算法 API | 编译代理 Application 依赖库 | 解压依赖库 aar 文件 )
- SQLServer 错误 33128 加密失败。 密钥使用了不推荐使用的算法“%.*ls”,这不再受支持。 故障 处理 修复 支持远程
- python AES算法模块进行加密解密的代码片段详解编程语言
- 秀尔算法:破解RSA加密的“不灭神话”
- 常用JS加密编码算法代码
- 微盾PHP脚本加密专家php解密算法
- 通过PHP的内置函数,通过DES算法对数据加密和解密
- 冒泡排序算法原理及JAVA实现代码
- c#冒泡排序算法示例
- Python算法之栈(stack)的实现