一、SM4算法介绍

2012年3月,国家密码管理局正式公布了包含SM4分组密码算法在内的《祖冲之序列密码算法》等6项密码行业标准。与DES和AES算法类似,SM4算法是一种分组密码算法。其分组长度为128bit,密钥长度也为128bit。加密算法与密钥扩展算法均采用32轮非线性迭代结构,以字(32位)为单位进行加密运算,每一次迭代运算均为一轮变换函数F。SM4算法加/解密算法的结构相同,只是使用轮密钥相反,其中解密轮密钥是加密轮密钥的逆序。
SM4有很高的灵活性,所采用的S盒可以灵活地被替换,以应对突发性的安全威胁。算法的32轮迭代采用串行处理,这与AES中每轮使用代换和混淆并行地处理整个分组有很大不同。

  1. public class Sm4Util {
  2. /**
  3. * 加密标识
  4. */
  5. private static final int ENCRYPT = 1;
  6. /**
  7. * 解密标识
  8. */
  9. private static final int DECRYPT = 0;
  10. /**
  11. * F轮函数循环次数
  12. */
  13. private static final int ROUND = 32;
  14. private static final int BLOCK = 16;
  15. private static final int FOUR = 4;
  16. private static final int ZERO = 0;
  17. /**
  18. * s盒
  19. */
  20. private byte[] sbox = {(byte) 0xd6, (byte) 0x90, (byte) 0xe9, (byte) 0xfe,
  21. (byte) 0xcc, (byte) 0xe1, 0x3d, (byte) 0xb7, 0x16, (byte) 0xb6,
  22. 0x14, (byte) 0xc2, 0x28, (byte) 0xfb, 0x2c, 0x05, 0x2b, 0x67,
  23. (byte) 0x9a, 0x76, 0x2a, (byte) 0xbe, 0x04, (byte) 0xc3,
  24. (byte) 0xaa, 0x44, 0x13, 0x26, 0x49, (byte) 0x86, 0x06,
  25. (byte) 0x99, (byte) 0x9c, 0x42, 0x50, (byte) 0xf4, (byte) 0x91,
  26. (byte) 0xef, (byte) 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43,
  27. (byte) 0xed, (byte) 0xcf, (byte) 0xac, 0x62, (byte) 0xe4,
  28. (byte) 0xb3, 0x1c, (byte) 0xa9, (byte) 0xc9, 0x08, (byte) 0xe8,
  29. (byte) 0x95, (byte) 0x80, (byte) 0xdf, (byte) 0x94, (byte) 0xfa,
  30. 0x75, (byte) 0x8f, 0x3f, (byte) 0xa6, 0x47, 0x07, (byte) 0xa7,
  31. (byte) 0xfc, (byte) 0xf3, 0x73, 0x17, (byte) 0xba, (byte) 0x83,
  32. 0x59, 0x3c, 0x19, (byte) 0xe6, (byte) 0x85, 0x4f, (byte) 0xa8,
  33. 0x68, 0x6b, (byte) 0x81, (byte) 0xb2, 0x71, 0x64, (byte) 0xda,
  34. (byte) 0x8b, (byte) 0xf8, (byte) 0xeb, 0x0f, 0x4b, 0x70, 0x56,
  35. (byte) 0x9d, 0x35, 0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, (byte) 0xd1,
  36. (byte) 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, (byte) 0x87,
  37. (byte) 0xd4, 0x00, 0x46, 0x57, (byte) 0x9f, (byte) 0xd3, 0x27,
  38. 0x52, 0x4c, 0x36, 0x02, (byte) 0xe7, (byte) 0xa0, (byte) 0xc4,
  39. (byte) 0xc8, (byte) 0x9e, (byte) 0xea, (byte) 0xbf, (byte) 0x8a,
  40. (byte) 0xd2, 0x40, (byte) 0xc7, 0x38, (byte) 0xb5, (byte) 0xa3,
  41. (byte) 0xf7, (byte) 0xf2, (byte) 0xce, (byte) 0xf9, 0x61, 0x15,
  42. (byte) 0xa1, (byte) 0xe0, (byte) 0xae, 0x5d, (byte) 0xa4,
  43. (byte) 0x9b, 0x34, 0x1a, 0x55, (byte) 0xad, (byte) 0x93, 0x32,
  44. 0x30, (byte) 0xf5, (byte) 0x8c, (byte) 0xb1, (byte) 0xe3, 0x1d,
  45. (byte) 0xf6, (byte) 0xe2, 0x2e, (byte) 0x82, 0x66, (byte) 0xca,
  46. 0x60, (byte) 0xc0, 0x29, 0x23, (byte) 0xab, 0x0d, 0x53, 0x4e, 0x6f,
  47. (byte) 0xd5, (byte) 0xdb, 0x37, 0x45, (byte) 0xde, (byte) 0xfd,
  48. (byte) 0x8e, 0x2f, 0x03, (byte) 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b,
  49. 0x51, (byte) 0x8d, 0x1b, (byte) 0xaf, (byte) 0x92, (byte) 0xbb,
  50. (byte) 0xdd, (byte) 0xbc, 0x7f, 0x11, (byte) 0xd9, 0x5c, 0x41,
  51. 0x1f, 0x10, 0x5a, (byte) 0xd8, 0x0a, (byte) 0xc1, 0x31,
  52. (byte) 0x88, (byte) 0xa5, (byte) 0xcd, 0x7b, (byte) 0xbd, 0x2d,
  53. 0x74, (byte) 0xd0, 0x12, (byte) 0xb8, (byte) 0xe5, (byte) 0xb4,
  54. (byte) 0xb0, (byte) 0x89, 0x69, (byte) 0x97, 0x4a, 0x0c,
  55. (byte) 0x96, 0x77, 0x7e, 0x65, (byte) 0xb9, (byte) 0xf1, 0x09,
  56. (byte) 0xc5, 0x6e, (byte) 0xc6, (byte) 0x84, 0x18, (byte) 0xf0,
  57. 0x7d, (byte) 0xec, 0x3a, (byte) 0xdc, 0x4d, 0x20, 0x79,
  58. (byte) 0xee, 0x5f, 0x3e, (byte) 0xd7, (byte) 0xcb, 0x39, 0x48};
  59. /**
  60. * 固定的ck参数
  61. */
  62. private int[] ck = {0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
  63. 0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9, 0xe0e7eef5,
  64. 0xfc030a11, 0x181f262d, 0x343b4249, 0x50575e65, 0x6c737a81,
  65. 0x888f969d, 0xa4abb2b9, 0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d,
  66. 0x141b2229, 0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
  67. 0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209, 0x10171e25,
  68. 0x2c333a41, 0x484f565d, 0x646b7279};
  69. /**
  70. * 固定的fk参数
  71. */
  72. private int[] fk = {0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc};
  73. /**
  74. * @Description 将传入的x参数循环左移num位
  75. * @Author tanp
  76. */
  77. private int leftMove(int x, int num) {
  78. //假如数据为无符号的数,长度为N,需要循环移动n位。可以用下面的公式:
  79. //循环左移n位: (x>>(N - n) ) | (x<<n);
  80. return x << num | x >>> (32 - num);
  81. }
  82. /**
  83. * @Description s盒的非线性变化函数
  84. * @Author tanp
  85. */
  86. private int byteSub(int x) {
  87. return (sbox[x >>> 24 & 0xFF] & 0xFF) << 24
  88. | (sbox[x >>> 16 & 0xFF] & 0xFF) << 16
  89. | (sbox[x >>> 8 & 0xFF] & 0xFF) << 8
  90. | (sbox[x & 0xFF] & 0xFF);
  91. }
  92. /**
  93. * @Description 轮函数中T函数的线性变化L函数
  94. * @Author tanp
  95. */
  96. private int transLf(int x) {
  97. //B^(B<<2|B>>>30)^(B<<10|B>>>22)^(B<<18|B>>>14)^(B<<24|B>>>8);
  98. return x ^ leftMove(x, 2) ^ leftMove(x, 10) ^ leftMove(x, 18) ^ leftMove(x, 24);
  99. }
  100. /**
  101. * @Description 秘钥扩展算法中的T'函数的线性变化L函数
  102. * @Author tanp
  103. */
  104. private int transTf(int x) {
  105. // B^(B<<13|B>>>19)^(B<<23|B>>>9);
  106. return x ^ leftMove(x, 13) ^ leftMove(x, 23);
  107. }
  108. /**
  109. * @Description 秘钥扩展算法T置换
  110. * @Author tanp
  111. */
  112. private int transTh(int in) {
  113. return transTf(byteSub(in));
  114. }
  115. /**
  116. * @Description 轮函数T置换
  117. * @Author tanp
  118. */
  119. private int transLth(int in) {
  120. return transLf(byteSub(in));
  121. }
  122. /**
  123. * @param key 秘钥
  124. * @param cryptFlag 加解密标识
  125. * @Description 获取轮秘钥
  126. * @Author tanp
  127. */
  128. private int[] sm4KeyExt(byte[] key, int cryptFlag) {
  129. int r, mid;
  130. int[] x = new int[4];
  131. int[] tmp = new int[4];
  132. int[] rk = new int[ROUND];
  133. getValue(key, x, tmp);
  134. //获取轮秘钥之前,先将秘钥与系统函数FK异或
  135. for (r = ZERO; r < FOUR; r++) {
  136. x[r] = x[r] ^ (fk[r]);
  137. }
  138. //轮训执行T置换
  139. for (r = ZERO; r < ROUND; r += FOUR) {
  140. rk[r] = x[0] ^= transTh(x[1] ^ x[2] ^ x[3] ^ ck[r]);
  141. rk[r + 1] = x[1] ^= transTh(x[2] ^ x[3] ^ x[0] ^ ck[r + 1]);
  142. rk[r + 2] = x[2] ^= transTh(x[3] ^ x[0] ^ x[1] ^ ck[r + 2]);
  143. rk[r + 3] = x[3] ^= transTh(x[0] ^ x[1] ^ x[2] ^ ck[r + 3]);
  144. }
  145. // 解密时轮密钥使用顺序:rk31,rk30,...,rk0
  146. if (cryptFlag == DECRYPT) {
  147. for (r = 0; r < BLOCK; r++) {
  148. mid = rk[r];
  149. rk[r] = rk[31 - r];
  150. rk[31 - r] = mid;
  151. }
  152. }
  153. return rk;
  154. }
  155. /**
  156. * @Description 轮函数F
  157. * @param input 加密解密的字节数组
  158. * @param rk 轮秘钥
  159. * @Author tanp
  160. */
  161. private void sms4Crypt(byte[] input, byte[] output, int[] rk) {
  162. int r;
  163. int[] x = new int[4];
  164. int[] tmp = new int[4];
  165. getValue(input, x, tmp);
  166. for (r = ZERO; r < ROUND; r += FOUR) {
  167. x[0] = x[0] ^ transLth(x[1] ^ x[2] ^ x[3] ^ rk[r]);
  168. x[1] = x[1] ^ transLth(x[2] ^ x[3] ^ x[0] ^ rk[r + 1]);
  169. x[2] = x[2] ^ transLth(x[3] ^ x[0] ^ x[1] ^ rk[r + 2]);
  170. x[3] = x[3] ^ transLth(x[0] ^ x[1] ^ x[2] ^ rk[r + 3]);
  171. }
  172. // Reverse
  173. for (int j = ZERO; j < BLOCK; j += FOUR) {
  174. output[j] = (byte) (x[3 - j / 4] >>> 24 & 0xFF);
  175. output[j + 1] = (byte) (x[3 - j / 4] >>> 16 & 0xFF);
  176. output[j + 2] = (byte) (x[3 - j / 4] >>> 8 & 0xFF);
  177. output[j + 3] = (byte) (x[3 - j / 4] & 0xFF);
  178. }
  179. }
  180. private void getValue(byte[] input, int[] x, int[] tmp) {
  181. for (int i = ZERO; i < FOUR; i++) {
  182. tmp[0] = input[4 * i] & 0xff;
  183. tmp[1] = input[1 + 4 * i] & 0xff;
  184. tmp[2] = input[2 + 4 * i] & 0xff;
  185. tmp[3] = input[3 + 4 * i] & 0xff;
  186. x[i] = tmp[0] << 24 | tmp[1] << 16 | tmp[2] << 8 | tmp[3];
  187. }
  188. }
  189. /**
  190. * @param in 需加密或者解密的字节数组
  191. * @param inLen 加密解密的字节长度
  192. * @param key 秘钥
  193. * @param out 加密解密后的字节数组,用来返回
  194. * @param cryptFlag 加解密标识
  195. * @Description 加密 解密方法
  196. * @Author tanp
  197. */
  198. private byte[] sms4(byte[] in, int inLen, byte[] key, byte[] out, int cryptFlag) {
  199. int point = 0;
  200. //获取轮秘钥
  201. int[] roundKey = sm4KeyExt(key, cryptFlag);
  202. byte[] input;
  203. byte[] output = new byte[16];
  204. while (inLen >= BLOCK) {
  205. input = Arrays.copyOfRange(in, point, point + 16);
  206. //每16个字节执行一次轮函数
  207. sms4Crypt(input, output, roundKey);
  208. System.arraycopy(output, 0, out, point, BLOCK);
  209. inLen -= BLOCK;
  210. point += BLOCK;
  211. }
  212. return out;
  213. }
  214. /**
  215. * 字符串加密
  216. *
  217. * @param plaintext 明文
  218. * @param key 秘钥
  219. * @return 加密后的明文字符串
  220. * @Author tanp
  221. */
  222. private static String encodeSms4ToHex(String plaintext, byte[] key) {
  223. if (plaintext == null || plaintext.length() <= 0) {
  224. return null;
  225. }
  226. StringBuilder plaintextBuilder = new StringBuilder(plaintext);
  227. for (int i = plaintextBuilder.toString().getBytes().length % BLOCK; i < BLOCK; i++) {
  228. plaintextBuilder.append('\0');
  229. }
  230. plaintext = plaintextBuilder.toString();
  231. return bytesToHexString(encodeSms4(plaintext.getBytes(), key));
  232. }
  233. /**
  234. * 字符串形式的密文解密成明文
  235. *
  236. * @param enHex 密文
  237. * @param key 秘钥
  238. * @return 解密后的明文
  239. * @Author tanp
  240. */
  241. private static String decodeSms4HexToString(String enHex, byte[] key) {
  242. byte[] plaintext = decodeSms4(hexStringToBytes(enHex),key);
  243. return new String(plaintext).trim();
  244. }
  245. /**
  246. * SMS4加密,加密字符数组
  247. *
  248. * @param plaintext 字节数组形式的明文
  249. * @param key 秘钥
  250. * @return 明文加密后的字接数组
  251. */
  252. private static byte[] encodeSms4(byte[] plaintext, byte[] key) {
  253. byte[] ciphering = new byte[plaintext.length];
  254. int k = 0;
  255. int plainLen = plaintext.length;
  256. while (k + BLOCK <= plainLen) {
  257. byte[] cellPlain = new byte[16];
  258. System.arraycopy(plaintext, k, cellPlain, 0, 16);
  259. byte[] cellCipher = encode16(cellPlain, key);
  260. System.arraycopy(cellCipher, 0, ciphering, k, cellCipher.length);
  261. k += 16;
  262. }
  263. return ciphering;
  264. }
  265. /**
  266. * 不限明文长度的SMS4解密
  267. *
  268. * @param ciphering 需要解密的字节数组
  269. * @param key 秘钥
  270. * @return 解密后的字节数组
  271. */
  272. private static byte[] decodeSms4(byte[] ciphering, byte[] key) {
  273. byte[] plaintext = new byte[ciphering.length];
  274. int k = 0;
  275. int cipherLen = ciphering.length;
  276. while (k + BLOCK <= cipherLen) {
  277. byte[] cellCipher = new byte[16];
  278. System.arraycopy(ciphering, k, cellCipher, 0, 16);
  279. byte[] cellPlain = decode16(cellCipher, key);
  280. System.arraycopy(cellPlain, 0, plaintext, k, cellPlain.length);
  281. k += BLOCK;
  282. }
  283. return plaintext;
  284. }
  285. /**
  286. * 只加密16位明文
  287. *
  288. * @param plaintext 明文字节数组
  289. * @param key 秘钥
  290. * @return 加密后的字节数组
  291. */
  292. private static byte[] encode16(byte[] plaintext, byte[] key) {
  293. byte[] cipher = new byte[16];
  294. Sm4Util sm4 = new Sm4Util();
  295. //调用加密方法
  296. return sm4.sms4(plaintext, 16, key, cipher, ENCRYPT);
  297. }
  298. /**
  299. * 只解密16位密文
  300. *
  301. * @param ciphering 需解密的密文
  302. * @param key 秘钥
  303. * @return 解密后的明文字节
  304. */
  305. private static byte[] decode16(byte[] ciphering, byte[] key) {
  306. byte[] plain = new byte[16];
  307. Sm4Util sm4 = new Sm4Util();
  308. sm4.sms4(ciphering, 16, key, plain, DECRYPT);
  309. return plain;
  310. }
  311. /**
  312. * 只加密32位明文
  313. *
  314. * @param plaintext 明文字节数组
  315. * @param key 秘钥
  316. * @return 加密后的字节数组
  317. */
  318. private static byte[] encode32(byte[] plaintext, byte[] key) {
  319. byte[] cipher = new byte[32];
  320. Sm4Util sm4 = new Sm4Util();
  321. return sm4.sms4(plaintext, 32, key, cipher, ENCRYPT);
  322. }
  323. /**
  324. * 只解密32位密文
  325. *
  326. * @param ciphering 需解密的密文
  327. * @param key 秘钥
  328. * @return 解密后的明文字节
  329. */
  330. private static byte[] decode32(byte[] ciphering, byte[] key) {
  331. byte[] plain = new byte[32];
  332. Sm4Util sm4 = new Sm4Util();
  333. sm4.sms4(ciphering, 32, key, plain, DECRYPT);
  334. return plain;
  335. }
  336. /**
  337. * 字节数组转字符串
  338. *
  339. * @param src 字节数组
  340. * @return String
  341. */
  342. private static String bytesToHexString(byte[] src) {
  343. StringBuilder stringBuilder = new StringBuilder();
  344. if (src == null || src.length <= 0) {
  345. return null;
  346. }
  347. for (byte aSrc : src) {
  348. int v = aSrc & 0xFF;
  349. String hv = Integer.toHexString(v);
  350. if (hv.length() < 2) {
  351. stringBuilder.append(0);
  352. }
  353. stringBuilder.append(hv);
  354. }
  355. return stringBuilder.toString();
  356. }
  357. /**
  358. * 字符串转字节数组
  359. *
  360. * @param hexString the hex string
  361. * @return byte[]
  362. */
  363. private static byte[] hexStringToBytes(String hexString) {
  364. if (hexString == null || hexString.length() <= 0) {
  365. return null;
  366. }
  367. hexString = hexString.toUpperCase();
  368. int length = hexString.length() / 2;
  369. char[] hexChars = hexString.toCharArray();
  370. byte[] d = new byte[length];
  371. for (int i = 0; i < length; i++) {
  372. int pos = i * 2;
  373. d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
  374. }
  375. return d;
  376. }
  377. /**
  378. * Convert char to byte
  379. *
  380. * @param c char
  381. * @return byte
  382. */
  383. private static byte charToByte(char c) {
  384. return (byte) "0123456789ABCDEF".indexOf(c);
  385. }
  386. /**
  387. * @Description 随机获取中文
  388. * @Date 2020/5/9 14:17
  389. * @Author tanp
  390. */
  391. private static char getRandomChar() {
  392. String str = "";
  393. int hightPos;
  394. int lowPos;
  395. Random random = new Random();
  396. hightPos = (176 + Math.abs(random.nextInt(39)));
  397. lowPos = (161 + Math.abs(random.nextInt(93)));
  398. byte[] b = new byte[2];
  399. b[0] = (Integer.valueOf(hightPos)).byteValue();
  400. b[1] = (Integer.valueOf(lowPos)).byteValue();
  401. try {
  402. str = new String(b, "GBK");
  403. } catch (UnsupportedEncodingException e) {
  404. e.printStackTrace();
  405. System.out.println("错误");
  406. }
  407. return str.charAt(0);
  408. }
  409. public static void main(String[] args) {
  410. final byte[] key = {0x01, 0x23, 0x45, 0x67, (byte) 0x89, (byte) 0xab,
  411. (byte) 0xcd, (byte) 0xef, (byte) 0xfe, (byte) 0xdc,
  412. (byte) 0xba, (byte) 0x98, 0x76, 0x54, 0x32, 0x10};
  413. StringBuilder msg = new StringBuilder();
  414. for(int a=0;a<ROUND;a++){
  415. for (int i = 1; i < ROUND; i++) {
  416. msg.append(getRandomChar());
  417. }
  418. System.out.println("加密前:" + msg.toString());
  419. String enStr = encodeSms4ToHex(msg.toString(), key);
  420. System.out.println("加密后:" + enStr);
  421. String deStr = decodeSms4HexToString(enStr, key);
  422. System.out.println("解密后:" + deStr);
  423. //查看经过加密再解密后的字符串是否与最开始的原生字符串是否一致
  424. System.out.println(msg.toString().equals(deStr));
  425. }
  426. }
  427. }