001package top.cenze.utils.crypt.sm.sm4;
002
003/**
004 * Created by $(USER) on $(DATE)
005 */
006
007import top.cenze.utils.ConvertUtil;
008
009import java.io.ByteArrayInputStream;
010import java.io.ByteArrayOutputStream;
011
012public class SM4 {
013    public static final int SM4_ENCRYPT = 1;
014
015    public static final int SM4_DECRYPT = 0;
016
017    private long GET_ULONG_BE(byte[] b, int i) {
018        long n = (long) (b[i] & 0xff) << 24 | (long) ((b[i + 1] & 0xff) << 16) | (long) ((b[i + 2] & 0xff) << 8) | (long) (b[i + 3] & 0xff) & 0xffffffffL;
019        return n;
020    }
021
022    private void PUT_ULONG_BE(long n, byte[] b, int i) {
023        b[i] = (byte) (int) (0xFF & n >> 24);
024        b[i + 1] = (byte) (int) (0xFF & n >> 16);
025        b[i + 2] = (byte) (int) (0xFF & n >> 8);
026        b[i + 3] = (byte) (int) (0xFF & n);
027    }
028
029    private long SHL(long x, int n) {
030        return (x & 0xFFFFFFFF) << n;
031    }
032
033    private long ROTL(long x, int n) {
034        return SHL(x, n) | x >> (32 - n);
035    }
036
037    private void SWAP(long[] sk, int i) {
038        long t = sk[i];
039        sk[i] = sk[(31 - i)];
040        sk[(31 - i)] = t;
041    }
042
043    public static final byte[] SboxTable = {(byte) 0xd6, (byte) 0x90, (byte) 0xe9, (byte) 0xfe,
044            (byte) 0xcc, (byte) 0xe1, 0x3d, (byte) 0xb7, 0x16, (byte) 0xb6,
045            0x14, (byte) 0xc2, 0x28, (byte) 0xfb, 0x2c, 0x05, 0x2b, 0x67,
046            (byte) 0x9a, 0x76, 0x2a, (byte) 0xbe, 0x04, (byte) 0xc3,
047            (byte) 0xaa, 0x44, 0x13, 0x26, 0x49, (byte) 0x86, 0x06,
048            (byte) 0x99, (byte) 0x9c, 0x42, 0x50, (byte) 0xf4, (byte) 0x91,
049            (byte) 0xef, (byte) 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43,
050            (byte) 0xed, (byte) 0xcf, (byte) 0xac, 0x62, (byte) 0xe4,
051            (byte) 0xb3, 0x1c, (byte) 0xa9, (byte) 0xc9, 0x08, (byte) 0xe8,
052            (byte) 0x95, (byte) 0x80, (byte) 0xdf, (byte) 0x94, (byte) 0xfa,
053            0x75, (byte) 0x8f, 0x3f, (byte) 0xa6, 0x47, 0x07, (byte) 0xa7,
054            (byte) 0xfc, (byte) 0xf3, 0x73, 0x17, (byte) 0xba, (byte) 0x83,
055            0x59, 0x3c, 0x19, (byte) 0xe6, (byte) 0x85, 0x4f, (byte) 0xa8,
056            0x68, 0x6b, (byte) 0x81, (byte) 0xb2, 0x71, 0x64, (byte) 0xda,
057            (byte) 0x8b, (byte) 0xf8, (byte) 0xeb, 0x0f, 0x4b, 0x70, 0x56,
058            (byte) 0x9d, 0x35, 0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, (byte) 0xd1,
059            (byte) 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, (byte) 0x87,
060            (byte) 0xd4, 0x00, 0x46, 0x57, (byte) 0x9f, (byte) 0xd3, 0x27,
061            0x52, 0x4c, 0x36, 0x02, (byte) 0xe7, (byte) 0xa0, (byte) 0xc4,
062            (byte) 0xc8, (byte) 0x9e, (byte) 0xea, (byte) 0xbf, (byte) 0x8a,
063            (byte) 0xd2, 0x40, (byte) 0xc7, 0x38, (byte) 0xb5, (byte) 0xa3,
064            (byte) 0xf7, (byte) 0xf2, (byte) 0xce, (byte) 0xf9, 0x61, 0x15,
065            (byte) 0xa1, (byte) 0xe0, (byte) 0xae, 0x5d, (byte) 0xa4,
066            (byte) 0x9b, 0x34, 0x1a, 0x55, (byte) 0xad, (byte) 0x93, 0x32,
067            0x30, (byte) 0xf5, (byte) 0x8c, (byte) 0xb1, (byte) 0xe3, 0x1d,
068            (byte) 0xf6, (byte) 0xe2, 0x2e, (byte) 0x82, 0x66, (byte) 0xca,
069            0x60, (byte) 0xc0, 0x29, 0x23, (byte) 0xab, 0x0d, 0x53, 0x4e, 0x6f,
070            (byte) 0xd5, (byte) 0xdb, 0x37, 0x45, (byte) 0xde, (byte) 0xfd,
071            (byte) 0x8e, 0x2f, 0x03, (byte) 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b,
072            0x51, (byte) 0x8d, 0x1b, (byte) 0xaf, (byte) 0x92, (byte) 0xbb,
073            (byte) 0xdd, (byte) 0xbc, 0x7f, 0x11, (byte) 0xd9, 0x5c, 0x41,
074            0x1f, 0x10, 0x5a, (byte) 0xd8, 0x0a, (byte) 0xc1, 0x31,
075            (byte) 0x88, (byte) 0xa5, (byte) 0xcd, 0x7b, (byte) 0xbd, 0x2d,
076            0x74, (byte) 0xd0, 0x12, (byte) 0xb8, (byte) 0xe5, (byte) 0xb4,
077            (byte) 0xb0, (byte) 0x89, 0x69, (byte) 0x97, 0x4a, 0x0c,
078            (byte) 0x96, 0x77, 0x7e, 0x65, (byte) 0xb9, (byte) 0xf1, 0x09,
079            (byte) 0xc5, 0x6e, (byte) 0xc6, (byte) 0x84, 0x18, (byte) 0xf0,
080            0x7d, (byte) 0xec, 0x3a, (byte) 0xdc, 0x4d, 0x20, 0x79,
081            (byte) 0xee, 0x5f, 0x3e, (byte) 0xd7, (byte) 0xcb, 0x39, 0x48};
082
083    public static final int[] FK = {0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc};
084
085    public static final int[] CK = {0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
086            0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
087            0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
088            0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
089            0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
090            0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
091            0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
092            0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279};
093
094    private byte sm4Sbox(byte inch) {
095        int i = inch & 0xFF;
096        byte retVal = SboxTable[i];
097        return retVal;
098    }
099
100    private long sm4Lt(long ka) {
101        long bb = 0L;
102        long c = 0L;
103        byte[] a = new byte[4];
104        byte[] b = new byte[4];
105        PUT_ULONG_BE(ka, a, 0);
106        b[0] = sm4Sbox(a[0]);
107        b[1] = sm4Sbox(a[1]);
108        b[2] = sm4Sbox(a[2]);
109        b[3] = sm4Sbox(a[3]);
110        bb = GET_ULONG_BE(b, 0);
111        c = bb ^ ROTL(bb, 2) ^ ROTL(bb, 10) ^ ROTL(bb, 18) ^ ROTL(bb, 24);
112        return c;
113    }
114
115    private long sm4F(long x0, long x1, long x2, long x3, long rk) {
116        return x0 ^ sm4Lt(x1 ^ x2 ^ x3 ^ rk);
117    }
118
119    private long sm4CalciRK(long ka) {
120        long bb = 0L;
121        long rk = 0L;
122        byte[] a = new byte[4];
123        byte[] b = new byte[4];
124        PUT_ULONG_BE(ka, a, 0);
125        b[0] = sm4Sbox(a[0]);
126        b[1] = sm4Sbox(a[1]);
127        b[2] = sm4Sbox(a[2]);
128        b[3] = sm4Sbox(a[3]);
129        bb = GET_ULONG_BE(b, 0);
130        rk = bb ^ ROTL(bb, 13) ^ ROTL(bb, 23);
131        return rk;
132    }
133
134    private void sm4_setkey(long[] SK, byte[] key) {
135        long[] MK = new long[4];
136        long[] k = new long[36];
137        int i = 0;
138        MK[0] = GET_ULONG_BE(key, 0);
139        MK[1] = GET_ULONG_BE(key, 4);
140        MK[2] = GET_ULONG_BE(key, 8);
141        MK[3] = GET_ULONG_BE(key, 12);
142        k[0] = MK[0] ^ (long) FK[0];
143        k[1] = MK[1] ^ (long) FK[1];
144        k[2] = MK[2] ^ (long) FK[2];
145        k[3] = MK[3] ^ (long) FK[3];
146        for (; i < 32; i++) {
147            k[(i + 4)] = (k[i] ^ sm4CalciRK(k[(i + 1)] ^ k[(i + 2)] ^ k[(i + 3)] ^ (long) CK[i]));
148            SK[i] = k[(i + 4)];
149        }
150    }
151
152    private void sm4_one_round(long[] sk, byte[] input, byte[] output) {
153        int i = 0;
154        long[] ulbuf = new long[36];
155        ulbuf[0] = GET_ULONG_BE(input, 0);
156        ulbuf[1] = GET_ULONG_BE(input, 4);
157        ulbuf[2] = GET_ULONG_BE(input, 8);
158        ulbuf[3] = GET_ULONG_BE(input, 12);
159        while (i < 32) {
160            ulbuf[(i + 4)] = sm4F(ulbuf[i], ulbuf[(i + 1)], ulbuf[(i + 2)], ulbuf[(i + 3)], sk[i]);
161            i++;
162        }
163        PUT_ULONG_BE(ulbuf[35], output, 0);
164        PUT_ULONG_BE(ulbuf[34], output, 4);
165        PUT_ULONG_BE(ulbuf[33], output, 8);
166        PUT_ULONG_BE(ulbuf[32], output, 12);
167    }
168    //修改了填充模式,为模式
169    private byte[] padding(byte[] input, int mode) {
170        if (input == null) {
171            return null;
172        }
173
174        byte[] ret = (byte[]) null;
175        if (mode == SM4_ENCRYPT) {
176            //填充:hex必须是32的整数倍填充 ,填充的是80  00 00 00
177            int p = 16 - input.length % 16;
178            String inputHex = ConvertUtil.byteToHex(input)+ "80";
179            StringBuffer stringBuffer =new StringBuffer(inputHex);
180            for (int i = 0; i <p-1 ; i++) {
181                stringBuffer.append("00");
182            }
183            ret= ConvertUtil.hexToByte(stringBuffer.toString());
184            //ret = new byte[input.length + p];
185            /*System.arraycopy(input, 0, ret, 0, input.length);
186            for (int i = 0; i < p; i++) {
187                ret[input.length + i] = (byte) '�';
188            }*/
189        } else {
190            /*int p = input[input.length - 1];
191            ret = new byte[input.length - p];
192            System.arraycopy(input, 0, ret, 0, input.length - p);*/
193            String inputHex =ConvertUtil.byteToHex(input);
194            int i = inputHex.lastIndexOf("80");
195            String substring = inputHex.substring(0, i);
196            ret= ConvertUtil.hexToByte(substring);
197        }
198        return ret;
199    }
200
201    public void sm4_setkey_enc(SM4_Context ctx, byte[] key) throws Exception {
202        if (ctx == null) {
203            throw new Exception("ctx is null!");
204        }
205
206        if (key == null || key.length != 16) {
207            throw new Exception("key error!");
208        }
209
210        ctx.mode = SM4_ENCRYPT;
211        sm4_setkey(ctx.sk, key);
212    }
213
214    public void sm4_setkey_dec(SM4_Context ctx, byte[] key) throws Exception {
215        if (ctx == null) {
216            throw new Exception("ctx is null!");
217        }
218
219        if (key == null || key.length != 16) {
220            throw new Exception("key error!");
221        }
222
223        int i = 0;
224        ctx.mode = SM4_DECRYPT;
225        sm4_setkey(ctx.sk, key);
226        for (i = 0; i < 16; i++) {
227            SWAP(ctx.sk, i);
228        }
229    }
230
231    public byte[] sm4_crypt_ecb(SM4_Context ctx, byte[] input) throws Exception {
232        if (input == null) {
233            throw new Exception("input is null!");
234        }
235
236        if ((ctx.isPadding) && (ctx.mode == SM4_ENCRYPT)) {
237            input = padding(input, SM4_ENCRYPT);
238        }
239
240        int length = input.length;
241        ByteArrayInputStream bins = new ByteArrayInputStream(input);
242        ByteArrayOutputStream bous = new ByteArrayOutputStream();
243        for (; length > 0; length -= 16) {
244            byte[] in = new byte[16];
245            byte[] out = new byte[16];
246            bins.read(in);
247            sm4_one_round(ctx.sk, in, out);
248            bous.write(out);
249        }
250
251        byte[] output = bous.toByteArray();
252        if (ctx.isPadding && ctx.mode == SM4_DECRYPT) {
253            output = padding(output, SM4_DECRYPT);
254        }
255        bins.close();
256        bous.close();
257        return output;
258    }
259
260    public byte[] sm4_crypt_cbc(SM4_Context ctx, byte[] iv, byte[] input) throws Exception {
261        if (iv == null || iv.length != 16) {
262            throw new Exception("iv error!");
263        }
264
265        if (input == null) {
266            throw new Exception("input is null!");
267        }
268
269        if (ctx.isPadding && ctx.mode == SM4_ENCRYPT) {
270            input = padding(input, SM4_ENCRYPT);
271        }
272
273        int i = 0;
274        int length = input.length;
275        ByteArrayInputStream bins = new ByteArrayInputStream(input);
276        ByteArrayOutputStream bous = new ByteArrayOutputStream();
277        if (ctx.mode == SM4_ENCRYPT) {
278            for (; length > 0; length -= 16) {
279                byte[] in = new byte[16];
280                byte[] out = new byte[16];
281                byte[] out1 = new byte[16];
282
283                bins.read(in);
284                for (i = 0; i < 16; i++) {
285                    out[i] = ((byte) (in[i] ^ iv[i]));
286                }
287                sm4_one_round(ctx.sk, out, out1);
288                System.arraycopy(out1, 0, iv, 0, 16);
289                bous.write(out1);
290            }
291        } else {
292            byte[] temp = new byte[16];
293            for (; length > 0; length -= 16) {
294                byte[] in = new byte[16];
295                byte[] out = new byte[16];
296                byte[] out1 = new byte[16];
297
298                bins.read(in);
299                System.arraycopy(in, 0, temp, 0, 16);
300                sm4_one_round(ctx.sk, in, out);
301                for (i = 0; i < 16; i++) {
302                    out1[i] = ((byte) (out[i] ^ iv[i]));
303                }
304                System.arraycopy(temp, 0, iv, 0, 16);
305                bous.write(out1);
306            }
307        }
308
309        byte[] output = bous.toByteArray();
310        if (ctx.isPadding && ctx.mode == SM4_DECRYPT) {
311            output = padding(output, SM4_DECRYPT);
312        }
313        bins.close();
314        bous.close();
315        return output;
316    }
317}