/** * 国密SM4加密算法 * 基于网上下载的sm4.js版本修改,网上版本有Number位溢出问题,所以加密结果不对, * 虽然自身能正常加解密,但不能与其它语言相互回加密秘 * 主要是修改为用BigInteger来计算,参考Java版的SM4.java计算方式来改造 * * @date 2018-10 */ function SM4Context() { this.mode = 1; this.isPadding = true; this.sk = new Array(32); } function SM4() { this.SM4_ENCRYPT = 1; this.SM4_DECRYPT = 0; this.S_BOX_TABLE = [0xd6,0x90,0xe9,0xfe,0xcc,0xe1,0x3d,0xb7,0x16,0xb6,0x14,0xc2,0x28,0xfb,0x2c,0x05, 0x2b,0x67,0x9a,0x76,0x2a,0xbe,0x04,0xc3,0xaa,0x44,0x13,0x26,0x49,0x86,0x06,0x99, 0x9c,0x42,0x50,0xf4,0x91,0xef,0x98,0x7a,0x33,0x54,0x0b,0x43,0xed,0xcf,0xac,0x62, 0xe4,0xb3,0x1c,0xa9,0xc9,0x08,0xe8,0x95,0x80,0xdf,0x94,0xfa,0x75,0x8f,0x3f,0xa6, 0x47,0x07,0xa7,0xfc,0xf3,0x73,0x17,0xba,0x83,0x59,0x3c,0x19,0xe6,0x85,0x4f,0xa8, 0x68,0x6b,0x81,0xb2,0x71,0x64,0xda,0x8b,0xf8,0xeb,0x0f,0x4b,0x70,0x56,0x9d,0x35, 0x1e,0x24,0x0e,0x5e,0x63,0x58,0xd1,0xa2,0x25,0x22,0x7c,0x3b,0x01,0x21,0x78,0x87, 0xd4,0x00,0x46,0x57,0x9f,0xd3,0x27,0x52,0x4c,0x36,0x02,0xe7,0xa0,0xc4,0xc8,0x9e, 0xea,0xbf,0x8a,0xd2,0x40,0xc7,0x38,0xb5,0xa3,0xf7,0xf2,0xce,0xf9,0x61,0x15,0xa1, 0xe0,0xae,0x5d,0xa4,0x9b,0x34,0x1a,0x55,0xad,0x93,0x32,0x30,0xf5,0x8c,0xb1,0xe3, 0x1d,0xf6,0xe2,0x2e,0x82,0x66,0xca,0x60,0xc0,0x29,0x23,0xab,0x0d,0x53,0x4e,0x6f, 0xd5,0xdb,0x37,0x45,0xde,0xfd,0x8e,0x2f,0x03,0xff,0x6a,0x72,0x6d,0x6c,0x5b,0x51, 0x8d,0x1b,0xaf,0x92,0xbb,0xdd,0xbc,0x7f,0x11,0xd9,0x5c,0x41,0x1f,0x10,0x5a,0xd8, 0x0a,0xc1,0x31,0x88,0xa5,0xcd,0x7b,0xbd,0x2d,0x74,0xd0,0x12,0xb8,0xe5,0xb4,0xb0, 0x89,0x69,0x97,0x4a,0x0c,0x96,0x77,0x7e,0x65,0xb9,0xf1,0x09,0xc5,0x6e,0xc6,0x84, 0x18,0xf0,0x7d,0xec,0x3a,0xdc,0x4d,0x20,0x79,0xee,0x5f,0x3e,0xd7,0xcb,0x39,0x48]; // var FK = [ 0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc ]; //字符串方便转BigInteger,(为与Java端一致,第1、4位转为有符号,Java端是用int有符号进行异或) this.FK = ['-5c4e453a', '56aa3350', '677d9197', '-4d8fdd24']; // var 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]; this.CK = ['70e15', '1c232a31', '383f464d', '545b6269', '70777e85', '-736c655f', '-57504943', '-3b342d27', '-1f18110b', '-3fcf5ef', '181f262d', '343b4249', '50575e65', '6c737a81', '-77706963', '-5b544d47', '-3f38312b', '-231c150f', '-700f9f3', '141b2229', '30373e45', '4c535a61', '686f767d', '-7b746d67', '-5f58514b', '-433c352f', '-27201913', '-b04fdf7', '10171e25', '2c333a41', '484f565d', '646b7279']; this.getUlongBE = function(b, i) { var b1 = new BigInteger(String(b[i] & 0xff)).shiftLeft(24); var b2 = new BigInteger(String(b[i + 1] & 0xff)).shiftLeft(16); var b3 = new BigInteger(String(b[i + 2] & 0xff)).shiftLeft(8); var b4 = new BigInteger(String(b[i + 3] & 0xff)).and(new BigInteger("FFFFFFFF", 16)); return b1.or(b2).or(b3).or(b4); //return (b[i] & 0xff) << 24 | ((b[i + 1] & 0xff) << 16) | ((b[i + 2] & 0xff) << 8) | (b[i + 3] & 0xff) & 0xffffffff; }; this.putUlongBE = function(n, b, i) { b[i] = n.shiftRight(24).and(new BigInteger("0xFF", 16)).byteValue(); b[i + 1] = n.shiftRight(16).and(new BigInteger("0xFF", 16)).byteValue(); b[i + 2] = n.shiftRight(8).and(new BigInteger("0xFF", 16)).byteValue(); b[i + 3] = n.and(new BigInteger("0xFF", 16)).byteValue(); // var t1=(0xFF & (n >> 24)); // var t2=(0xFF & (n >> 16)); // var t3=(0xFF & (n >> 8)); // var t4=(0xFF & (n)); // b[i] = t1>128?t1-256:t1; // b[i + 1] = t2>128?t2-256:t2; // b[i + 2] = t3>128?t3-256:t3; // b[i + 3] = t4>128?t4-256:t4; }; this.rotl = function(x, n) { //(x & 0xFFFFFFFF) << n; var b1 = x.and(new BigInteger("FFFFFFFF", 16)).shiftLeft(n); return b1.or(x.shiftRight(32 - n)); //return this.SHL(x, n) | x >> (32 - n); }; this.sm4Lt = function(ka) { var a = new Array(4); var b = new Array(4); this.putUlongBE(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]); var bb = this.getUlongBE(b, 0); var c = bb.xor(this.rotl(bb, 2)).xor(this.rotl(bb, 10)).xor(this.rotl(bb, 18)).xor(this.rotl(bb, 24)); // c = bb ^ this.rotl(bb, 2) ^ this.rotl(bb, 10) ^ this.rotl(bb, 18) ^ this.rotl(bb, 24); return c; }; this.sm4F = function(x0, x1, x2, x3, rk) { return x0.xor(this.sm4Lt(x1.xor(x2).xor(x3).xor(rk))); // return x0 ^ this.sm4Lt(x1 ^ x2 ^ x3 ^ rk); }; this.sm4CalciRK = function(ka) { var a = new Array(4); var b = new Array(4); this.putUlongBE(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]); var bb = this.getUlongBE(b, 0); // rk = bb ^ this.rotl(bb, 13) ^ this.rotl(bb, 23); var rk = bb.xor(this.rotl(bb, 13)).xor(this.rotl(bb, 23)); return rk; }; this.sm4Sbox = function(inch) { var i = inch & 0xFF; var retVal = this.S_BOX_TABLE[i]; return retVal > 128 ? retVal - 256 : retVal; }; this.sm4SetKeyEnc = function(ctx, key) { if (ctx == null) { alert("ctx is null!"); return false; } if (key == null || key.length != 16) { alert("key error!"); return false; } ctx.mode = this.SM4_ENCRYPT; this.sm4SetKey(ctx.sk, key); }; this.sm4SetKeyDec = function(ctx, key) { this.sm4SetKeyEnc(ctx, key); ctx.mode = this.SM4_DECRYPT; for (var i = 0; i < 16; i++) { // this.SWAP(ctx.sk, i); var t = ctx.sk[i]; ctx.sk[i] = ctx.sk[(31 - i)]; ctx.sk[(31 - i)] = t; } }; this.sm4SetKey = function(SK, key) { var MK = new Array(4);// bint var k = new Array(36);// bint MK[0] = this.getUlongBE(key, 0); MK[1] = this.getUlongBE(key, 4); MK[2] = this.getUlongBE(key, 8); MK[3] = this.getUlongBE(key, 12); k[0] = MK[0].xor(new BigInteger(this.FK[0], 16));//MK[0] ^ FK[0]; k[1] = MK[1].xor(new BigInteger(this.FK[1], 16));//MK[1] ^ FK[1]; k[2] = MK[2].xor(new BigInteger(this.FK[2], 16));//MK[2] ^ FK[2]; k[3] = MK[3].xor(new BigInteger(this.FK[3], 16));//MK[3] ^ FK[3]; for (var i = 0; i < 32; i++) { k[(i + 4)] = k[i].xor(this.sm4CalciRK(k[i + 1].xor(k[(i + 2)]).xor(k[(i + 3)]).xor(new BigInteger(this.CK[i], 16)))); // k[(i + 4)] = (k[i] ^ this.sm4CalciRK(k[(i + 1)] ^ k[(i + 2)] ^ k[(i + 3)] ^ CK[i])); SK[i] = k[(i + 4)]; } }; this.padding = function(input, mode) { if (input == null) { return null; } var ret = null; if (mode == this.SM4_ENCRYPT) { var p = parseInt(16 - input.length % 16); ret = input.slice(0); for (var i = 0; i < p; i++) { ret[input.length + i] = p; } } else { var p = input[input.length - 1]; ret = input.slice(0, input.length - p); } return ret; }; this.sm4OneRound = function(sk, input, output) { var i = 0; var ulbuf = new Array(36); ulbuf[0] = this.getUlongBE(input, 0); ulbuf[1] = this.getUlongBE(input, 4); ulbuf[2] = this.getUlongBE(input, 8); ulbuf[3] = this.getUlongBE(input, 12); while (i < 32) { ulbuf[(i + 4)] = this.sm4F(ulbuf[i], ulbuf[(i + 1)], ulbuf[(i + 2)], ulbuf[(i + 3)], sk[i]); i++; } this.putUlongBE(ulbuf[35], output, 0); this.putUlongBE(ulbuf[34], output, 4); this.putUlongBE(ulbuf[33], output, 8); this.putUlongBE(ulbuf[32], output, 12); }; this.sm4CryptEcb = function(ctx, input) { if (input == null) { alert("input is null!"); } if ((ctx.isPadding) && (ctx.mode == this.SM4_ENCRYPT)) { input = this.padding(input, this.SM4_ENCRYPT); } var i = 0; var length = input.length; var bous = new Array(); for (; length > 0; length -= 16) { var out = new Array(16); var ins = input.slice(i * 16, (16 * (i + 1))); this.sm4OneRound(ctx.sk, ins, out) bous = bous.concat(out); i++; } var output = bous; if (ctx.isPadding && ctx.mode == this.SM4_DECRYPT) { output = this.padding(output, this.SM4_DECRYPT); } for (var i = 0; i < output.length; i++) { if (output[i] < 0) { output[i] = output[i] + 256; } } return output; }; this.sm4CryptCbc = function(ctx, iv, input) { if (iv == null || iv.length != 16) { alert("iv error!"); } if (input == null) { alert("input is null!"); } if (ctx.isPadding && ctx.mode == this.SM4_ENCRYPT) { input = this.padding(input, this.SM4_ENCRYPT); } var i = 0; var length = input.length; var bous = new Array(); if (ctx.mode == this.SM4_ENCRYPT) { var k = 0; for (; length > 0; length -= 16) { var out = new Array(16); var out1 = new Array(16); var ins = input.slice(k * 16, (16 * (k + 1))); for (i = 0; i < 16; i++) { out[i] = (ins[i] ^ iv[i]); } this.sm4OneRound(ctx.sk, out, out1); iv = out1.slice(0, 16); bous = bous.concat(out1); k++; } } else { var temp = []; var k = 0; for (; length > 0; length -= 16) { var out = new Array(16); var out1 = new Array(16); var ins = input.slice(k * 16, (16 * (k + 1))); temp = ins.slice(0, 16); this.sm4OneRound(ctx.sk, ins, out); for (i = 0; i < 16; i++) { out1[i] = (out[i] ^ iv[i]); } iv = temp.slice(0, 16); bous = bous.concat(out1); k++; } } var output = bous; if (ctx.isPadding && ctx.mode == this.SM4_DECRYPT) { output = this.padding(output, this.SM4_DECRYPT); } for (var i = 0; i < output.length; i++) { if (output[i] < 0) { output[i] = output[i] + 256; } } return output; }; } function SM4Util() { var sm4 = new SM4(); this.sm4Encrypt = function(key, text) { var ctx = new SM4Context(); var keyBytes = strToUtf8Bytes(key); sm4.sm4SetKeyEnc(ctx, keyBytes); var encrypted = sm4.sm4CryptEcb(ctx, strToUtf8Bytes(text)); return bytesToHex(encrypted); }; this.sm4Decrypt = function(key, cipherText) { var ctx = new SM4Context(); var keyBytes = strToUtf8Bytes(key); sm4.sm4SetKeyDec(ctx, keyBytes); var encrypted = sm4.sm4CryptEcb(ctx, hexToBytes(cipherText)); return bytesToUtf8Str(encrypted); }; this.sm4EncryptCbc = function(key, iv, text) { var ctx = new SM4Context(); var keyBytes = strToUtf8Bytes(key); var ivBytes= strToUtf8Bytes(iv) ; sm4.sm4SetKeyEnc(ctx, keyBytes); var encrypted = sm4.sm4CryptCbc(ctx, ivBytes, strToUtf8Bytes(text)); return bytesToHex(encrypted); }; this.sm4DecryptCbc = function(key, iv, cipherText) { var ctx = new SM4Context(); var keyBytes = strToUtf8Bytes(key); var ivBytes= strToUtf8Bytes(iv) ; sm4.sm4SetKeyDec(ctx, keyBytes); var encrypted = sm4.sm4CryptCbc(ctx, ivBytes, hexToBytes(cipherText)); return bytesToUtf8Str(encrypted); }; }