Java加解密-SM4国密算法
- SM4国密算法简介
- SM4依赖包
- SM4类
- SM4_Context类
- SecuritySM4类
=================================== SM4国密算法简介
与DES和AES算法相似,国密SM4算法是一种分组加密算法。SM4分组密码算法是一种迭代分组密码算法,由加解密算法和密钥扩展算法组成。
SM4是一种Feistel结构的分组密码算法,其分组长度和密钥长度均为128bits。加密算法和密钥扩展算法迭代轮数均为32轮。SM4加解密过程的算法相同但是轮密钥的使用顺序相反。
SM4密码算法使用模2加和循环移位作为基本运算。
密钥扩展算法:SM4算法使用128位的加密密钥,并采用32轮迭代加密结构,每一轮加密使用一个32位的轮密钥,总共使用32个轮密钥。因此需要使用密钥扩展算法,从加密密钥中产生32个轮密钥。
=================================== SM4依赖包
<dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.56</version> </dependency>
=================================== SM4类
package com.taoxw.utils.security.SM4; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; public class SM4 { protected static final int SM4_ENCRYPT = 1; protected static final int SM4_DECRYPT = 0; protected static final byte[] SboxTable = new byte[]{-42, -112, -23, -2, -52, -31, 61, -73, 22, -74, 20, -62, 40, -5, 44, 5, 43, 103, -102, 118, 42, -66, 4, -61, -86, 68, 19, 38, 73, -122, 6, -103, -100, 66, 80, -12, -111, -17, -104, 122, 51, 84, 11, 67, -19, -49, -84, 98, -28, -77, 28, -87, -55, 8, -24, -107, -128, -33, -108, -6, 117, -113, 63, -90, 71, 7, -89, -4, -13, 115, 23, -70, -125, 89, 60, 25, -26, -123, 79, -88, 104, 107, -127, -78, 113, 100, -38, -117, -8, -21, 15, 75, 112, 86, -99, 53, 30, 36, 14, 94, 99, 88, -47, -94, 37, 34, 124, 59, 1, 33, 120, -121, -44, 0, 70, 87, -97, -45, 39, 82, 76, 54, 2, -25, -96, -60, -56, -98, -22, -65, -118, -46, 64, -57, 56, -75, -93, -9, -14, -50, -7, 97, 21, -95, -32, -82, 93, -92, -101, 52, 26, 85, -83, -109, 50, 48, -11, -116, -79, -29, 29, -10, -30, 46, -126, 102, -54, 96, -64, 41, 35, -85, 13, 83, 78, 111, -43, -37, 55, 69, -34, -3, -114, 47, 3, -1, 106, 114, 109, 108, 91, 81, -115, 27, -81, -110, -69, -35, -68, 127, 17, -39, 92, 65, 31, 16, 90, -40, 10, -63, 49, -120, -91, -51, 123, -67, 45, 116, -48, 18, -72, -27, -76, -80, -119, 105, -105, 74, 12, -106, 119, 126, 101, -71, -15, 9, -59, 110, -58, -124, 24, -16, 125, -20, 58, -36, 77, 32, 121, -18, 95, 62, -41, -53, 57, 72}; protected static final int[] FK = new int[]{-1548633402, 1453994832, 1736282519, -1301273892}; protected static final int[] CK = new int[]{462357, 472066609, 943670861, 1415275113, 1886879365, -1936483679, -1464879427, -993275175, -521670923, -66909679, 404694573, 876298825, 1347903077, 1819507329, -2003855715, -1532251463, -1060647211, -589042959, -117504499, 337322537, 808926789, 1280531041, 1752135293, -2071227751, -1599623499, -1128019247, -656414995, -184876535, 269950501, 741554753, 1213159005, 1684763257}; public SM4() { } private long GET_ULONG_BE(byte[] b, int i) { long n = (long)(b[i] & 255) << 24 | (long)((b[i + 1] & 255) << 16) | (long)((b[i + 2] & 255) << 8) | (long)(b[i + 3] & 255) & 4294967295L; return n; } private void PUT_ULONG_BE(long n, byte[] b, int i) { b[i] = (byte)((int)(255L & n >> 24)); b[i + 1] = (byte)((int)(255L & n >> 16)); b[i + 2] = (byte)((int)(255L & n >> 8)); b[i + 3] = (byte)((int)(255L & n)); } private long SHL(long x, int n) { return (x & -1L) << n; } private long ROTL(long x, int n) { return this.SHL(x, n) | x >> 32 - n; } private void SWAP(long[] sk, int i) { long t = sk[i]; sk[i] = sk[31 - i]; sk[31 - i] = t; } private byte sm4Sbox(byte inch) { int i = inch & 255; byte retVal = SboxTable[i]; return retVal; } private long sm4Lt(long ka) { long bb = 0L; long c = 0L; byte[] a = new byte[4]; byte[] b = new byte[4]; this.PUT_ULONG_BE(ka, a, 0); b[0] = this.sm4Sbox(a[0]); b[1] = this.sm4Sbox(a[1]); b[2] = this.sm4Sbox(a[2]); b[3] = this.sm4Sbox(a[3]); bb = this.GET_ULONG_BE(b, 0); c = bb ^ this.ROTL(bb, 2) ^ this.ROTL(bb, 10) ^ this.ROTL(bb, 18) ^ this.ROTL(bb, 24); return c; } private long sm4F(long x0, long x1, long x2, long x3, long rk) { return x0 ^ this.sm4Lt(x1 ^ x2 ^ x3 ^ rk); } private long sm4CalciRK(long ka) { long bb = 0L; long rk = 0L; byte[] a = new byte[4]; byte[] b = new byte[4]; this.PUT_ULONG_BE(ka, a, 0); b[0] = this.sm4Sbox(a[0]); b[1] = this.sm4Sbox(a[1]); b[2] = this.sm4Sbox(a[2]); b[3] = this.sm4Sbox(a[3]); bb = this.GET_ULONG_BE(b, 0); rk = bb ^ this.ROTL(bb, 13) ^ this.ROTL(bb, 23); return rk; } private void sm4_setkey(long[] SK, byte[] key) { long[] MK = new long[4]; long[] k = new long[36]; int i = 0; MK[0] = this.GET_ULONG_BE(key, 0); MK[1] = this.GET_ULONG_BE(key, 4); MK[2] = this.GET_ULONG_BE(key, 8); MK[3] = this.GET_ULONG_BE(key, 12); k[0] = MK[0] ^ (long)FK[0]; k[1] = MK[1] ^ (long)FK[1]; k[2] = MK[2] ^ (long)FK[2]; for(k[3] = MK[3] ^ (long)FK[3]; i < 32; ++i) { k[i + 4] = k[i] ^ this.sm4CalciRK(k[i + 1] ^ k[i + 2] ^ k[i + 3] ^ (long)CK[i]); SK[i] = k[i + 4]; } } private void sm4_one_round(long[] sk, byte[] input, byte[] output) { int i = 0; long[] ulbuf = new long[36]; ulbuf[0] = this.GET_ULONG_BE(input, 0); ulbuf[1] = this.GET_ULONG_BE(input, 4); ulbuf[2] = this.GET_ULONG_BE(input, 8); for(ulbuf[3] = this.GET_ULONG_BE(input, 12); i < 32; ++i) { ulbuf[i + 4] = this.sm4F(ulbuf[i], ulbuf[i + 1], ulbuf[i + 2], ulbuf[i + 3], sk[i]); } this.PUT_ULONG_BE(ulbuf[35], output, 0); this.PUT_ULONG_BE(ulbuf[34], output, 4); this.PUT_ULONG_BE(ulbuf[33], output, 8); this.PUT_ULONG_BE(ulbuf[32], output, 12); } private byte[] padding(byte[] input, int mode) { if (input == null) { return null; } else if (input.length % 16 == 0) { return input; } else { byte[] ret = (byte[])null; if (mode == 1) { int p = 16 - input.length % 16; ret = new byte[input.length + p]; System.arraycopy(input, 0, ret, 0, input.length); for(int i = 0; i < p; ++i) { ret[input.length + i] = 0; } } else { int p = input[input.length - 1]; ret = new byte[input.length - p]; System.arraycopy(input, 0, ret, 0, input.length - p); } return ret; } } protected void sm4_setkey_enc(SM4_Context ctx, byte[] key) throws Exception { if (ctx == null) { throw new Exception("ctx is null!"); } else if (key != null && key.length == 16) { ctx.mode = 1; this.sm4_setkey(ctx.sk, key); } else { throw new Exception("key error!"); } } protected void sm4_setkey_dec(SM4_Context ctx, byte[] key) throws Exception { if (ctx == null) { throw new Exception("ctx is null!"); } else if (key != null && key.length == 16) { ctx.mode = 0; this.sm4_setkey(ctx.sk, key); for(int i = 0; i < 16; ++i) { this.SWAP(ctx.sk, i); } } else { throw new Exception("key error!"); } } protected byte[] sm4_crypt_ecb(SM4_Context ctx, byte[] input) throws Exception { if (input == null) { throw new Exception("input is null!"); } else { if (ctx.isPadding && ctx.mode == 1) { input = this.padding(input, 1); } int length = input.length; ByteArrayInputStream bins = new ByteArrayInputStream(input); ByteArrayOutputStream bous; byte[] output; for(bous = new ByteArrayOutputStream(); length > 0; length -= 16) { output = new byte[16]; byte[] out = new byte[16]; bins.read(output); this.sm4_one_round(ctx.sk, output, out); bous.write(out); } output = bous.toByteArray(); if (ctx.isPadding && ctx.mode == 0) { output = this.padding(output, 0); } bins.close(); bous.close(); return output; } } protected byte[] sm4_crypt_cbc(SM4_Context ctx, byte[] iv, byte[] input) throws Exception { if (iv != null && iv.length == 16) { if (input == null) { throw new Exception("input is null!"); } else { if (ctx.isPadding && ctx.mode == 1) { input = this.padding(input, 1); } int length = input.length; ByteArrayInputStream bins = new ByteArrayInputStream(input); ByteArrayOutputStream bous = new ByteArrayOutputStream(); byte[] temp; byte[] out; int i; if (ctx.mode != 1) { for(temp = new byte[16]; length > 0; length -= 16) { out = new byte[16]; out = new byte[16]; byte[] out1 = new byte[16]; bins.read(out); System.arraycopy(out, 0, temp, 0, 16); this.sm4_one_round(ctx.sk, out, out); for(i = 0; i < 16; ++i) { out1[i] = (byte)(out[i] ^ iv[i]); } System.arraycopy(temp, 0, iv, 0, 16); bous.write(out1); } } else { while(length > 0) { temp = new byte[16]; out = new byte[16]; out = new byte[16]; bins.read(temp); for(i = 0; i < 16; ++i) { out[i] = (byte)(temp[i] ^ iv[i]); } this.sm4_one_round(ctx.sk, out, out); System.arraycopy(out, 0, iv, 0, 16); bous.write(out); length -= 16; } } temp = bous.toByteArray(); // if (ctx.isPadding) { // int var10000 = ctx.mode; // } bins.close(); bous.close(); return temp; } } else { throw new Exception("iv error!"); } } }
=================================== SM4_Context类
package com.taoxw.utils.security.SM4; public class SM4_Context { public int mode = 1; public long[] sk = new long[32]; public boolean isPadding = true; protected SM4_Context() { } /** * 加密 * @param plainText 参数 * @param keyBytes 随机码 * @return */ public byte[] EncryptByte(byte[] plainText,byte[] keyBytes) { if (keyBytes != null && keyBytes.length == 16) { if (plainText != null && plainText.length > 0) { try { SM4_Context ctx = new SM4_Context(); ctx.isPadding = true; ctx.mode = 1; SM4 sm4 = new SM4(); sm4.sm4_setkey_enc(ctx, keyBytes); byte[] encrypted = sm4.sm4_crypt_ecb(ctx, plainText); return encrypted; } catch (Exception var6) { var6.printStackTrace(); return null; } } else { return null; } } else { return null; } } /** * 解密 * @param cipherText 密文 * @param keyBytes 随机码 * @return */ public byte[] DecryptStrByte(byte[] cipherText,byte[] keyBytes) { if (keyBytes != null && keyBytes.length == 16) { if (cipherText != null && cipherText.length > 0 && cipherText.length % 16 == 0) { try { SM4_Context ctx = new SM4_Context(); ctx.isPadding = true; ctx.mode = 0; SM4 sm4 = new SM4(); sm4.sm4_setkey_dec(ctx, keyBytes); byte[] decrypted = sm4.sm4_crypt_ecb(ctx, cipherText); int decryptedLen = decrypted.length; for (int i = decrypted.length - 1; i >= 0 && decrypted[i] == 0; --decryptedLen) { --i; } byte[] temp = new byte[decryptedLen]; System.arraycopy(decrypted, 0, temp, 0, decryptedLen); return temp; } catch (Exception var8) { var8.printStackTrace(); return null; } } else { return null; } } else { return null; } } protected byte[] encryptData_CBC(byte[] ivBytes, byte[] keyBytes, byte[] plainText) { if (keyBytes != null && keyBytes.length != 0 && keyBytes.length % 16 == 0) { if (plainText != null && plainText.length > 0) { if (ivBytes != null && ivBytes.length > 0) { try { SM4_Context ctx = new SM4_Context(); ctx.isPadding = true; ctx.mode = 1; SM4 sm4 = new SM4(); sm4.sm4_setkey_enc(ctx, keyBytes); byte[] encrypted = sm4.sm4_crypt_cbc(ctx, ivBytes, plainText); return encrypted; } catch (Exception var7) { var7.printStackTrace(); return null; } } else { return null; } } else { return null; } } else { return null; } } protected byte[] decryptData_CBC(byte[] ivBytes, byte[] keyBytes, byte[] cipherText) { if (keyBytes != null && keyBytes.length != 0 && keyBytes.length % 16 == 0) { if (cipherText != null && cipherText.length > 0) { if (ivBytes != null && ivBytes.length > 0) { try { SM4_Context ctx = new SM4_Context(); ctx.isPadding = true; ctx.mode = 0; SM4 sm4 = new SM4(); sm4.sm4_setkey_dec(ctx, keyBytes); byte[] decrypted = sm4.sm4_crypt_cbc(ctx, ivBytes, cipherText); return decrypted; } catch (Exception var7) { var7.printStackTrace(); return null; } } else { return null; } } else { return null; } } else { return null; } } }
=================================== SecuritySM4类
package com.taoxw.utils.security.SM4; import javax.xml.bind.DatatypeConverter; import org.bouncycastle.util.encoders.Hex; public class SecuritySM4 { /** * SM4加密 * @param str 参数 * @param key 随机码 * @return */ public static String EncryptStr(String str,String key) { return DatatypeConverter.printHexBinary(new SM4_Context().EncryptByte(str.getBytes(),key.getBytes())); } /** * 解密 * @param cipherStrings 密文 * @param keyStr 随机码 * @return */ public static String DecryptStr(String cipherStrings,String keyStr) { String result = ""; SM4_Context aa=new SM4_Context(); // byte[] cc="00".getBytes(); byte[] dd="00".getBytes(); byte[] cipherText = Hex.decode(cipherStrings); byte[] keyBytes = keyStr.getBytes(); try { dd= aa.DecryptStrByte(cipherText,keyBytes); result = new String(dd); } catch (Exception e) { } return result; } }
=================================== SecuritySM4单元测试
parms.put("custName", "张三"); parms.put("certNum", "411322000000000000"); String str = parms.toJSONString(); System.out.println("参数:" + str); //key必须是16位 String key ="TAxH5p1vs3ePYABC"; String ncECBData = SecuritySM4.EncryptStr(str, key); System.out.println("加密:"+ncECBData); String plainTextEncripted = SecuritySM4.DecryptStr(ncECBData,key); System.out.println("解密:"+plainTextEncripted);