1. 场景
浏览器原生支持了AES-GCM。
参考资料:https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/encrypt
2. 代码
/**
* AES-GCM helper
*/
var aesGcmHelper = {
_keyMap: new Map(),
getIv(keyRaw) {
if (this._keyMap.has(keyRaw)) {
return this._keyMap.get(keyRaw).iv;
}
let iv = crypto.getRandomValues(new Uint8Array(12));
this._keyMap.set(keyRaw, Object.assign({}, this._keyMap.get(keyRaw), { iv }));
return iv;
},
/**
* hex to ArrayBuffer
* @param {String} hex
*/
hex2Buf(hex) {
let buf = new Uint8Array(
hex.match(/[\da-f]{2}/gi).map(function (h) {
return parseInt(h, 16);
})
);
return buf;
},
/**
* base64 to ArrayBuffer
* @param {String} hex
*/
base64ToBuf(base64) {
var binaryString = window.atob(base64);
var bytes = new Uint8Array(binaryString.length);
for (var i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
},
/** * ArrayBuffer to hex * @param {ArrayBuffer} buffer */ buf2Hex(buffer) {
let binary = [...new Uint8Array(buffer)].map((x) => x.toString(16).padStart(2, '0')).join('');
return binary;
},
/**
* ArrayBuffer to base64
* @param {ArrayBuffer} buffer
*/
buf2Base64(buffer) {
let binary = [...new Uint8Array(buffer)].map((x) => String.fromCharCode(x)).join('');
return window.btoa(binary);
},
/**
* encrypt
* @param {String} data data
* @param {String} keyRaw key raw
* @param {Uint8Array} iv iv
* @return {Promise}
*/
async encrypt(data, keyRaw, iv = null) {
if (iv == null) {
iv = this.getIv(keyRaw);
} else {
this._keyMap.set(keyRaw, Object.assign({}, this._keyMap.get(keyRaw), { iv }));
}
let encoded = new TextEncoder().encode(data);
let key = await crypto.subtle.importKey('raw', new TextEncoder().encode(keyRaw), 'AES-GCM', true, ['encrypt', 'decrypt']);
let ciphertext = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, encoded);
return ciphertext;
},
/**
* decrypt
* @param {ArrayBuffer} encrypted encrypted data
* @param {String} keyRaw key raw
* @param {Uint8Array} iv iv
* @return {Promise}
*/
async decrypt(encrypted, keyRaw, iv = null) {
if (iv == null) {
iv = this.getIv(keyRaw);
}
if (!ArrayBuffer.isView(encrypted) && toString.apply(encrypted) != '[object ArrayBuffer]') {
throw new Error('encrypted must is ArrayBuffer');
}
let key = await crypto.subtle.importKey('raw', new TextEncoder().encode(keyRaw), 'AES-GCM', true, ['encrypt', 'decrypt']);
let decrypted = '';
try {
decrypted = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, encrypted);
} catch (error) {
throw new Error(error);
return '';
}
let decoded = new TextDecoder().decode(decrypted);
return decoded;
}
};
3. 示例
(async () => {
let iv = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
let keyRaw = 'IHieUI!%9awQqszp';
// 1.encrypt test
let ciphertext = await aesGcmHelper.encrypt('hello, Tom', keyRaw, iv);
// - hex
let ciphertextOfHex = aesGcmHelper.buf2Hex(ciphertext);
console.log(ciphertextOfHex); // => b85320c50c256bc27dc1113a92a83614d60161f6af4d1ce237b0
// - base64
let ciphertextOfBase64 = aesGcmHelper.buf2Base64(ciphertext);
console.log(ciphertextOfBase64); // => uFMgxQwla8J9wRE6kqg2FNYBYfavTRziN7A=
// 2.decrypt test
// - hex
let decodedData = await aesGcmHelper.decrypt(aesGcmHelper.hex2Buf(ciphertextOfHex), keyRaw, iv);
console.log(decodedData); // => hello, Tom
// - base64
decodedData = await aesGcmHelper.decrypt(aesGcmHelper.base64ToBuf(ciphertextOfBase64), keyRaw, iv);
console.log(decodedData); // => hello, Tom
})();
JavaScript aes-gcm 加密与解密方式。
浙公网安备 33010602011771号