hb_zhgd_screen/js/smutil/sm4.js

341 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 国密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);
};
}