js的国密sm3加密sm4加解密base64加解密javascript
程序员文章站
2024-03-14 14:21:16
...
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/jquery-3.3.1.min.js"></script>
<script src="js/hex.js"></script>
<script src="js/sm3.js"></script>
<script src="js/sm4.js"></script>
<script src="js/base64.js"></script>
</head>
<body>
<div style="text-align:center;">
<form action="#" method="post">
<table style="margin-left: auto;margin-right: auto;">
<tr>
<td style="width:auto;text-align: right;">
输入消息体:
</td>
<td style="text-align: left;" valign="middle">
<textarea rows="5" cols="50" name="inputtext" id="inputtext"></textarea>
</td>
</tr>
<tr>
<td style="width:auto;text-align: right;">
输入key:
</td>
<td style="text-align: left;" valign="middle">
<textarea rows="5" cols="50" name="keytext" id="keytext"></textarea>
</td>
</tr>
<tr>
<td style="width:auto;text-align: right;">
加密结果:
</td>
<td style="text-align: left;" valign="middle">
<textarea rows="5" cols="50" name="crypttext" id="crypttext"></textarea>
</td>
</tr>
<tr>
<td style="width:auto;text-align: right;">
解密结果:
</td>
<td style="text-align: left;" valign="middle">
<textarea rows="5" cols="50" name="crypttext" id="jiemitext"></textarea>
</td>
</tr>
<tr>
<td colspan="2" style="width:auto;text-align: center;">
<input type="button" value="点击生成" id="btn_enc" />
</td>
</tr>
</table>
</form>
</div>
<script>
var result;
var jiemiresult;
var length;
$("#btn_enc").click(function () {
let inputtext = $("#inputtext").val();
let keytext = $("#keytext").val();
jiami(inputtext, keytext);
jiemi(result, keytext);
$("#crypttext").val(result);
$("#jiemitext").val(jiemiresult);
});
function jiami(inputtext, keytext) {
/*
* sm3加密
*/
let sm3Data = sm3_encrypt(inputtext)
console.log(sm3Data);
let sm3Arr = []
for (let i = 0; i < sm3Data.length; i++) {
let aa = sm3Data.slice(i, i + 2)
aa = hex2int(aa)
sm3Arr.push(aa)
i++
}
length = sm3Arr.length
console.log("sm3 jiami:" + sm3Arr);
// /*
// * sm4加密
// */
let input_arr = Hex.utf8StrToBytes(inputtext)
let data = sm3Arr.concat(input_arr);
console.log("sm3加密+content:" + data);
var re = data.length % 16;
if (re > 0) {
for (var i = 0; i < 16 - re; i++) {
data.push(0x00);
}
}
let hex_key = Hex.utf8StrToBytes(keytext)
var s = sm4_encrypt(data, hex_key);
console.log("sm4加密:" + s);
// /*
// * base64加密
// */
let u8s = new Uint8Array(s)
result = Base64.fromUint8Array(u8s);
console.log("base64加密:" + result);
};
function jiemi(result, keytext) {
// /*
// * base64解密
// */
let zz = Base64.toUint8Array(result)
console.log("base64解密:" + zz);
// /*
// * sm4解密
// */
let hex_key = Hex.utf8StrToBytes(keytext)
let aa = sm4_decrypt(zz, hex_key)
for (let i = aa.length - 1; i > aa.length - 16; i--) {
if (aa[i] == 0) aa.splice(i, 1)
}
console.log("sm4解密:" + aa);
// /*
// * 消息体
// */
let content = aa.splice(length, aa.length - 1)
jiemiresult = Hex.bytesToUtf8Str(content)
console.log("消息体:" + content);
}
</script>
</body>
</html>
**base64.js**
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ?
module.exports = factory() :
typeof define === 'function' && define.amd ?
define(factory) :
(function () {
const _Base64 = global.Base64;
const gBase64 = factory();
gBase64.noConflict = () => {
global.Base64 = _Base64;
return gBase64;
};
if (global.Meteor) { // Meteor.js
Base64 = gBase64;
}
global.Base64 = gBase64;
})();
}((typeof self !== 'undefined' ? self :
typeof window !== 'undefined' ? window :
typeof global !== 'undefined' ? global :
this
), function () {
'use strict';
const version = '3.6.0';
const VERSION = version;
const _hasatob = typeof atob === 'function';
const _hasbtoa = typeof btoa === 'function';
const _hasBuffer = typeof Buffer === 'function';
const _TD = typeof TextDecoder === 'function' ? new TextDecoder() : undefined;
const _TE = typeof TextEncoder === 'function' ? new TextEncoder() : undefined;
const b64ch = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
const b64chs = [...b64ch];
const b64tab = ((a) => {
let tab = {};
a.forEach((c, i) => tab[c] = i);
return tab;
})(b64chs);
const b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/;
const _fromCC = String.fromCharCode.bind(String);
const _U8Afrom = typeof Uint8Array.from === 'function' ?
Uint8Array.from.bind(Uint8Array) :
(it, fn = (x) => x) => new Uint8Array(Array.prototype.slice.call(it, 0).map(fn));
const _mkUriSafe = (src) => src
.replace(/[+\/]/g, (m0) => m0 == '+' ? '-' : '_')
.replace(/=+$/m, '');
const _tidyB64 = (s) => s.replace(/[^A-Za-z0-9\+\/]/g, '');
const btoaPolyfill = (bin) => {
let u32, c0, c1, c2, asc = '';
const pad = bin.length % 3;
for (let i = 0; i < bin.length;) {
if ((c0 = bin.charCodeAt(i++)) > 255 ||
(c1 = bin.charCodeAt(i++)) > 255 ||
(c2 = bin.charCodeAt(i++)) > 255)
throw new TypeError('invalid character found');
u32 = (c0 << 16) | (c1 << 8) | c2;
asc += b64chs[u32 >> 18 & 63] +
b64chs[u32 >> 12 & 63] +
b64chs[u32 >> 6 & 63] +
b64chs[u32 & 63];
}
return pad ? asc.slice(0, pad - 3) + "===".substring(pad) : asc;
};
const _btoa = _hasbtoa ? (bin) => btoa(bin) :
_hasBuffer ? (bin) => Buffer.from(bin, 'binary').toString('base64') :
btoaPolyfill;
const _fromUint8Array = _hasBuffer ?
(u8a) => Buffer.from(u8a).toString('base64') :
(u8a) => {
const maxargs = 0x1000;
let strs = [];
for (let i = 0, l = u8a.length; i < l; i += maxargs) {
strs.push(_fromCC.apply(null, u8a.subarray(i, i + maxargs)));
}
return _btoa(strs.join(''));
};
const fromUint8Array = (u8a, urlsafe = false) => urlsafe ? _mkUriSafe(_fromUint8Array(u8a)) : _fromUint8Array(u8a);
const cb_utob = (c) => {
if (c.length < 2) {
var cc = c.charCodeAt(0);
return cc < 0x80 ? c :
cc < 0x800 ? (_fromCC(0xc0 | (cc >>> 6)) +
_fromCC(0x80 | (cc & 0x3f))) :
(_fromCC(0xe0 | ((cc >>> 12) & 0x0f)) +
_fromCC(0x80 | ((cc >>> 6) & 0x3f)) +
_fromCC(0x80 | (cc & 0x3f)));
} else {
var cc = 0x10000 +
(c.charCodeAt(0) - 0xD800) * 0x400 +
(c.charCodeAt(1) - 0xDC00);
return (_fromCC(0xf0 | ((cc >>> 18) & 0x07)) +
_fromCC(0x80 | ((cc >>> 12) & 0x3f)) +
_fromCC(0x80 | ((cc >>> 6) & 0x3f)) +
_fromCC(0x80 | (cc & 0x3f)));
}
};
const re_utob = /[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g;
const utob = (u) => u.replace(re_utob, cb_utob);
const _encode = _hasBuffer ?
(s) => Buffer.from(s, 'utf8').toString('base64') :
_TE ?
(s) => _fromUint8Array(_TE.encode(s)) :
(s) => _btoa(utob(s));
const encode = (src, urlsafe = false) => urlsafe ?
_mkUriSafe(_encode(src)) :
_encode(src);
const encodeURI = (src) => encode(src, true);
const re_btou = /[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/g;
const cb_btou = (cccc) => {
switch (cccc.length) {
case 4:
var cp = ((0x07 & cccc.charCodeAt(0)) << 18) |
((0x3f & cccc.charCodeAt(1)) << 12) |
((0x3f & cccc.charCodeAt(2)) << 6) |
(0x3f & cccc.charCodeAt(3)),
offset = cp - 0x10000;
return (_fromCC((offset >>> 10) + 0xD800) +
_fromCC((offset & 0x3FF) + 0xDC00));
case 3:
return _fromCC(((0x0f & cccc.charCodeAt(0)) << 12) |
((0x3f & cccc.charCodeAt(1)) << 6) |
(0x3f & cccc.charCodeAt(2)));
default:
return _fromCC(((0x1f & cccc.charCodeAt(0)) << 6) |
(0x3f & cccc.charCodeAt(1)));
}
};
const btou = (b) => b.replace(re_btou, cb_btou);
const atobPolyfill = (asc) => {
asc = asc.replace(/\s+/g, '');
if (!b64re.test(asc))
throw new TypeError('malformed base64.');
asc += '=='.slice(2 - (asc.length & 3));
let u24, bin = '',
r1, r2;
for (let i = 0; i < asc.length;) {
u24 = b64tab[asc.charAt(i++)] << 18 |
b64tab[asc.charAt(i++)] << 12 |
(r1 = b64tab[asc.charAt(i++)]) << 6 |
(r2 = b64tab[asc.charAt(i++)]);
bin += r1 === 64 ? _fromCC(u24 >> 16 & 255) :
r2 === 64 ? _fromCC(u24 >> 16 & 255, u24 >> 8 & 255) :
_fromCC(u24 >> 16 & 255, u24 >> 8 & 255, u24 & 255);
}
return bin;
};
const _atob = _hasatob ? (asc) => atob(_tidyB64(asc)) :
_hasBuffer ? (asc) => Buffer.from(asc, 'base64').toString('binary') :
atobPolyfill;
const _toUint8Array = _hasBuffer ?
(a) => _U8Afrom(Buffer.from(a, 'base64')) :
(a) => _U8Afrom(_atob(a), c => c.charCodeAt(0));
const toUint8Array = (a) => _toUint8Array(_unURI(a));
const _decode = _hasBuffer ?
(a) => Buffer.from(a, 'base64').toString('utf8') :
_TD ?
(a) => _TD.decode(_toUint8Array(a)) :
(a) => btou(_atob(a));
const _unURI = (a) => _tidyB64(a.replace(/[-_]/g, (m0) => m0 == '-' ? '+' : '/'));
const decode = (src) => _decode(_unURI(src));
const isValid = (src) => {
if (typeof src !== 'string')
return false;
const s = src.replace(/\s+/g, '').replace(/=+$/, '');
return !/[^\s0-9a-zA-Z\+/]/.test(s) || !/[^\s0-9a-zA-Z\-_]/.test(s);
};
const _noEnum = (v) => {
return {
value: v,
enumerable: false,
writable: true,
configurable: true
};
};
const extendString = function () {
const _add = (name, body) => Object.defineProperty(String.prototype, name, _noEnum(body));
_add('fromBase64', function () {
return decode(this);
});
_add('toBase64', function (urlsafe) {
return encode(this, urlsafe);
});
_add('toBase64URI', function () {
return encode(this, true);
});
_add('toBase64URL', function () {
return encode(this, true);
});
_add('toUint8Array', function () {
return toUint8Array(this);
});
};
const extendUint8Array = function () {
const _add = (name, body) => Object.defineProperty(Uint8Array.prototype, name, _noEnum(body));
_add('toBase64', function (urlsafe) {
return fromUint8Array(this, urlsafe);
});
_add('toBase64URI', function () {
return fromUint8Array(this, true);
});
_add('toBase64URL', function () {
return fromUint8Array(this, true);
});
};
const extendBuiltins = () => {
extendString();
extendUint8Array();
};
const gBase64 = {
version: version,
VERSION: VERSION,
atob: _atob,
atobPolyfill: atobPolyfill,
btoa: _btoa,
btoaPolyfill: btoaPolyfill,
fromBase64: decode,
toBase64: encode,
encode: encode,
encodeURI: encodeURI,
encodeURL: encodeURI,
utob: utob,
btou: btou,
decode: decode,
isValid: isValid,
fromUint8Array: fromUint8Array,
toUint8Array: toUint8Array,
extendString: extendString,
extendUint8Array: extendUint8Array,
extendBuiltins: extendBuiltins,
};
gBase64.Base64 = {};
Object.keys(gBase64).forEach(k => gBase64.Base64[k] = gBase64[k]);
return gBase64;
}));
**hex.js**
function Hex() {
}
Hex.encode = function (b, pos, len) {
var hexCh = new Array(len * 2);
var hexCode = new Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F');
for (var i = pos, j = 0; i < len + pos; i++, j++) {
hexCh[j] = hexCode[(b[i] & 0xFF) >> 4];
hexCh[++j] = hexCode[(b[i] & 0x0F)];
}
return hexCh.join('');
}
Hex.decode = function (hex) {
if (hex == null || hex == '') {
return null;
}
if (hex.length % 2 != 0) {
return null;
}
var ascLen = hex.length / 2;
var hexCh = this.toCharCodeArray(hex);
var asc = new Array(ascLen);
for (var i = 0; i < ascLen; i++) {
if (hexCh[2 * i] >= 0x30 && hexCh[2 * i] <= 0x39) {
asc[i] = ((hexCh[2 * i] - 0x30) << 4);
} else if (hexCh[2 * i] >= 0x41 && hexCh[2 * i] <= 0x46) { //A-F : 0x41-0x46
asc[i] = ((hexCh[2 * i] - 0x41 + 10) << 4);
} else if (hexCh[2 * i] >= 0x61 && hexCh[2 * i] <= 0x66) { //a-f : 0x61-0x66
asc[i] = ((hexCh[2 * i] - 0x61 + 10) << 4);
} else {
return null;
}
if (hexCh[2 * i + 1] >= 0x30 && hexCh[2 * i + 1] <= 0x39) {
asc[i] = (asc[i] | (hexCh[2 * i + 1] - 0x30));
} else if (hexCh[2 * i + 1] >= 0x41 && hexCh[2 * i + 1] <= 0x46) {
asc[i] = (asc[i] | (hexCh[2 * i + 1] - 0x41 + 10));
} else if (hexCh[2 * i + 1] >= 0x61 && hexCh[2 * i + 1] <= 0x66) {
asc[i] = (asc[i] | (hexCh[2 * i + 1] - 0x61 + 10));
} else {
return null;
}
}
return asc;
}
Hex.utf8StrToHex = function (utf8Str) {
var ens = encodeURIComponent(utf8Str);
var es = unescape(ens);
var esLen = es.length;
// Convert
var words = [];
for (var i = 0; i < esLen; i++) {
words[i] = (es.charCodeAt(i).toString(16));
}
return words.join('');
}
Hex.utf8StrToBytes = function (utf8Str) {
var ens = encodeURIComponent(utf8Str);
var es = unescape(ens);
var esLen = es.length;
// Convert
var words = [];
for (var i = 0; i < esLen; i++) {
words[i] = es.charCodeAt(i);
}
return words;
}
Hex.hexToUtf8Str = function (utf8Str) {
var utf8Byte = Hex.decode(utf8Str);
var latin1Chars = [];
for (var i = 0; i < utf8Byte.length; i++) {
latin1Chars.push(String.fromCharCode(utf8Byte[i]));
}
return decodeURIComponent(escape(latin1Chars.join('')));
}
Hex.bytesToUtf8Str = function (bytesArray) {
var utf8Byte = bytesArray;
var latin1Chars = [];
for (var i = 0; i < utf8Byte.length; i++) {
latin1Chars.push(String.fromCharCode(utf8Byte[i]));
}
return decodeURIComponent(escape(latin1Chars.join('')));
}
Hex.toCharCodeArray = function (chs) {
var chArr = new Array(chs.length);
for (var i = 0; i < chs.length; i++) {
chArr[i] = chs.charCodeAt(i);
}
return chArr;
}
hex2int = function (hex) {
var len = hex.length,
a = new Array(len),
code;
for (var i = 0; i < len; i++) {
code = hex.charCodeAt(i);
if (48 <= code && code < 58) {
code -= 48;
} else {
code = (code & 0xdf) - 65 + 10;
}
a[i] = code;
}
return a.reduce(function (acc, c) {
acc = 16 * acc + c;
return acc;
}, 0);
}
**sm3.js**
/**
* 左补0到指定长度
*/
function leftPad(input, num) {
if (input.length >= num) return input;
return (new Array(num - input.length + 1)).join('0') + input
}
/**
* 二进制转化为十六进制
*/
function binary2hex(binary) {
const binaryLength = 8;
let hex = '';
for (let i = 0; i < binary.length / binaryLength; i++) {
hex += leftPad(parseInt(binary.substr(i * binaryLength, binaryLength), 2).toString(16), 2);
}
return hex;
}
/**
* 十六进制转化为二进制
*/
function hex2binary(hex) {
const hexLength = 2;
let binary = '';
for (let i = 0; i < hex.length / hexLength; i++) {
binary += leftPad(parseInt(hex.substr(i * hexLength, hexLength), 16).toString(2), 8);
}
return binary;
}
/**
* 普通字符串转化为二进制
*/
function str2binary(str) {
let binary = '';
for (const ch of str) {
binary += leftPad(ch.codePointAt(0).toString(2), 8);
}
return binary;
}
/**
* 循环左移
*/
function rol(str, n) {
return str.substring(n % str.length) + str.substr(0, n % str.length);
}
/**
* 二进制运算
*/
function binaryCal(x, y, method) {
const a = x || '';
const b = y || '';
const result = [];
let prevResult;
for (let i = a.length - 1; i >= 0; i--) { // 大端
prevResult = method(a[i], b[i], prevResult);
result[i] = prevResult[0];
}
return result.join('');
}
/**
* 二进制异或运算
*/
function xor(x, y) {
return binaryCal(x, y, (a, b) => [(a === b ? '0' : '1')]);
}
/**
* 二进制与运算
*/
function and(x, y) {
return binaryCal(x, y, (a, b) => [(a === '1' && b === '1' ? '1' : '0')]);
}
/**
* 二进制或运算
*/
function or(x, y) {
return binaryCal(x, y, (a, b) => [(a === '1' || b === '1' ? '1' : '0')]); // a === '0' && b === '0' ? '0' : '1'
}
/**
* 二进制与运算
*/
function add(x, y) {
const result = binaryCal(x, y, (a, b, prevResult) => {
const carry = prevResult ? prevResult[1] : '0' || '0';
// a,b不等时,carry不变,结果与carry相反
// a,b相等时,结果等于原carry,新carry等于a
if (a !== b) return [carry === '0' ? '1' : '0', carry];
return [carry, a];
});
return result;
}
/**
* 二进制非运算
*/
function not(x) {
return binaryCal(x, undefined, a => [a === '1' ? '0' : '1']);
}
function calMulti(method) {
return (...arr) => arr.reduce((prev, curr) => method(prev, curr));
}
/**
* 压缩函数中的置换函数 P1(X) = X xor (X <<< 9) xor (X <<< 17)
*/
function P0(X) {
return calMulti(xor)(X, rol(X, 9), rol(X, 17));
}
/**
* 消息扩展中的置换函数 P1(X) = X xor (X <<< 15) xor (X <<< 23)
*/
function P1(X) {
return calMulti(xor)(X, rol(X, 15), rol(X, 23));
}
function FF(X, Y, Z, j) {
return j >= 0 && j <= 15 ? calMulti(xor)(X, Y, Z) : calMulti(or)(and(X, Y), and(X, Z), and(Y, Z));
}
function GG(X, Y, Z, j) {
return j >= 0 && j <= 15 ? calMulti(xor)(X, Y, Z) : or(and(X, Y), and(not(X), Z));
}
function T(j) {
return j >= 0 && j <= 15 ? hex2binary('79cc4519') : hex2binary('7a879d8a');
}
/**
* 压缩函数
*/
function CF(V, Bi) {
// 消息扩展
const wordLength = 32;
const W = [];
const M = []; // W'
// 将消息分组B划分为16个字W0, W1,…… ,W15 (字为长度为32的比特串)
for (let i = 0; i < 16; i++) {
W.push(Bi.substr(i * wordLength, wordLength));
}
// W[j] <- P1(W[j−16] xor W[j−9] xor (W[j−3] <<< 15)) xor (W[j−13] <<< 7) xor W[j−6]
for (let j = 16; j < 68; j++) {
W.push(calMulti(xor)(
P1(calMulti(xor)(W[j - 16], W[j - 9], rol(W[j - 3], 15))),
rol(W[j - 13], 7),
W[j - 6]
));
}
// W′[j] = W[j] xor W[j+4]
for (let j = 0; j < 64; j++) {
M.push(xor(W[j], W[j + 4]));
}
// 压缩
const wordRegister = []; // 字寄存器
for (let j = 0; j < 8; j++) {
wordRegister.push(V.substr(j * wordLength, wordLength));
}
let A = wordRegister[0];
let B = wordRegister[1];
let C = wordRegister[2];
let D = wordRegister[3];
let E = wordRegister[4];
let F = wordRegister[5];
let G = wordRegister[6];
let H = wordRegister[7];
// 中间变量
let SS1;
let SS2;
let TT1;
let TT2;
for (let j = 0; j < 64; j++) {
SS1 = rol(calMulti(add)(rol(A, 12), E, rol(T(j), j)), 7);
SS2 = xor(SS1, rol(A, 12));
TT1 = calMulti(add)(FF(A, B, C, j), D, SS2, M[j]);
TT2 = calMulti(add)(GG(E, F, G, j), H, SS1, W[j]);
D = C;
C = rol(B, 9);
B = A;
A = TT1;
H = G;
G = rol(F, 19);
F = E;
E = P0(TT2);
}
return xor([A, B, C, D, E, F, G, H].join(''), V);
}
function sm3_encrypt(str) {
const binary = str2binary(str);
// 填充
const len = binary.length;
// k是满足len + 1 + k = 448mod512的最小的非负整数
let k = len % 512;
// 如果 448 <= (512 % len) < 512,需要多补充 (len % 448) 比特'0'以满足总比特长度为512的倍数
k = k >= 448 ? 512 - (k % 448) - 1 : 448 - k - 1;
const m = `${binary}1${leftPad('', k)}${leftPad(len.toString(2), 64)}`.toString(); // k个0
// 迭代压缩
const n = (len + k + 65) / 512;
let V = hex2binary('7380166f4914b2b9172442d7da8a0600a96f30bc163138aae38dee4db0fb0e4e');
for (let i = 0; i <= n - 1; i++) {
const B = m.substr(512 * i, 512);
V = CF(V, B);
}
return binary2hex(V);
};
**sm4.js**
const DECRYPT = 0;
const ROUND = 32;
const BLOCK = 16;
const Sbox = [
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
];
const 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
];
function rotl(x, y) {
return x << y | x >>> (32 - y);
}
function byteSub(a) {
return (Sbox[a >>> 24 & 0xFF] & 0xFF) << 24 | (Sbox[a >>> 16 & 0xFF] & 0xFF) << 16 | (Sbox[a >>> 8 & 0xFF] & 0xFF) << 8 | (Sbox[a & 0xFF] & 0xFF);
}
function l1(b) {
return b ^ rotl(b, 2) ^ rotl(b, 10) ^ rotl(b, 18) ^ rotl(b, 24);
}
function l2(b) {
return b ^ rotl(b, 13) ^ rotl(b, 23);
}
function sms4Crypt(input, output, roundKey) {
let r;
let mid;
let x = new Array(4);
let tmp = new Array(4);
for (let i = 0; i < 4; i++) {
tmp[0] = input[0 + 4 * i] & 0xff;
tmp[1] = input[1 + 4 * i] & 0xff;
tmp[2] = input[2 + 4 * i] & 0xff;
tmp[3] = input[3 + 4 * i] & 0xff;
x[i] = tmp[0] << 24 | tmp[1] << 16 | tmp[2] << 8 | tmp[3];
}
for (r = 0; r < 32; r += 4) {
mid = x[1] ^ x[2] ^ x[3] ^ roundKey[r + 0];
mid = byteSub(mid);
x[0] = x[0] ^ l1(mid); // x4
mid = x[2] ^ x[3] ^ x[0] ^ roundKey[r + 1];
mid = byteSub(mid);
x[1] = x[1] ^ l1(mid); // x5
mid = x[3] ^ x[0] ^ x[1] ^ roundKey[r + 2];
mid = byteSub(mid);
x[2] = x[2] ^ l1(mid); // x6
mid = x[0] ^ x[1] ^ x[2] ^ roundKey[r + 3];
mid = byteSub(mid);
x[3] = x[3] ^ l1(mid); // x7
}
//Reverse
for (let j = 0; j < 16; j += 4) {
output[j] = x[3 - j / 4] >>> 24 & 0xff;
output[j + 1] = x[3 - j / 4] >>> 16 & 0xff;
output[j + 2] = x[3 - j / 4] >>> 8 & 0xff;
output[j + 3] = x[3 - j / 4] & 0xff;
}
}
function sms4KeyExt(key, roundKey, cryptFlag) {
let r;
let mid;
let x = new Array(4);
let tmp = new Array(4);
for (let i = 0; i < 4; i++) {
tmp[0] = key[0 + 4 * i] & 0xff;
tmp[1] = key[1 + 4 * i] & 0xff;
tmp[2] = key[2 + 4 * i] & 0xff;
tmp[3] = key[3 + 4 * i] & 0xff;
x[i] = tmp[0] << 24 | tmp[1] << 16 | tmp[2] << 8 | tmp[3];
}
x[0] ^= 0xa3b1bac6;
x[1] ^= 0x56aa3350;
x[2] ^= 0x677d9197;
x[3] ^= 0xb27022dc;
for (r = 0; r < 32; r += 4) {
mid = x[1] ^ x[2] ^ x[3] ^ CK[r + 0];
mid = byteSub(mid);
roundKey[r + 0] = x[0] ^= l2(mid); // roundKey0 = K4
mid = x[2] ^ x[3] ^ x[0] ^ CK[r + 1];
mid = byteSub(mid);
roundKey[r + 1] = x[1] ^= l2(mid); // roundKey1 = K5
mid = x[3] ^ x[0] ^ x[1] ^ CK[r + 2];
mid = byteSub(mid);
roundKey[r + 2] = x[2] ^= l2(mid); // roundKey2 = K6
mid = x[0] ^ x[1] ^ x[2] ^ CK[r + 3];
mid = byteSub(mid);
roundKey[r + 3] = x[3] ^= l2(mid); // roundKey3 = K7
}
// 解密时轮**使用顺序:roundKey31, roundKey30, ..., roundKey0
if (cryptFlag === DECRYPT) {
for (r = 0; r < 16; r++) {
mid = roundKey[r];
roundKey[r] = roundKey[31 - r];
roundKey[31 - r] = mid;
}
}
}
function sm4(inArray, key, cryptFlag) {
let outArray = [];
let point = 0;
let roundKey = new Array(ROUND);
sms4KeyExt(key, roundKey, cryptFlag);
let input = new Array(16);
let output = new Array(16);
let inLen = inArray.length;
while (inLen >= BLOCK) {
input = inArray.slice(point, point + 16);
sms4Crypt(input, output, roundKey);
for (let i = 0; i < BLOCK; i++) {
outArray[point + i] = output[i];
}
inLen -= BLOCK;
point += BLOCK;
}
return outArray;
}
function sm4_encrypt(inArray, key) {
return sm4(inArray, key, 1);
};
function sm4_decrypt(inArray, key) {
return sm4(inArray, key, 0);
};
下一篇: AES加密/解密