node.js 实现 AES CTR 加解密
由于最近我们在做一款安全的文件分享 App, 所有文件均需要使用 aes ctr 来进行加密,aes key 还有一整套完整的许可体系在保护, 然后再通知各种阅读器进行打开。
关于 aes ctr 不在此做说明,如需了解,请访问 : AES CTR 详细介绍
正文
直接上代码
'use strict' var crypto = require('crypto') // 异或 key 的加解算法 let alg = 'aes-256-ecb'; // 加密的数据块大小,单位字节 let block_size = 16; class AES { /** * 构造函数 * @param key_buffer aes key 的 byte 数组 * @param iv_buffer 随机数据的 byte 数组 */ constructor(key_buffer,iv_buffer ){ this._key = key_buffer; this._iv = iv_buffer; } /** * 加密 * @param data_buffer 明文数据 * @param offset 偏移量 * @returns {Buffer} 加密后的数据 */ encryt(data_buffer, offset ){ return this._crypt(data_buffer, offset) } /** * 解密 * @param enc_data_buffer 加密后的数据 * @param offset 偏移量 * @returns {Buffer} 解密后的数据 */ decrypt(enc_data_buffer, offset){ return this._crypt(enc_data_buffer, offset); } /** * 加解密传的数据块 * @param data_buffer 数据 buffer * @param offset 偏移量,用于计算 counter * @returns {Buffer} 加密或解密后的数据 * @private */ _crypt(data_buffer, offset){ let byte_length = data_buffer.length; let enc_data_buffer = Buffer.alloc(byte_length); let start_counter = Math.floor(offset / block_size); let total_counter = (offset+byte_length) % block_size == 0 ? ((offset+byte_length) / block_size)-1 : Math.floor((offset+byte_length) / block_size); // 已处理的字节长度 var handled_length = 0; for(var i = start_counter; i<= total_counter; i++){ // 本次处理的字节长度 let handle_length = (i+1)*block_size-offset-handled_length; if(handled_length + handle_length > byte_length){ handle_length = byte_length - handled_length; } let handle_data_buffer = data_buffer.slice(handled_length, handled_length + handle_length); let handled_data_buffer = null; if(i == 0 && handle_length < block_size && total_counter > 0){ handled_data_buffer = this._cryptBlock(handle_data_buffer, i, handle_length,-1); } else { handled_data_buffer = this._cryptBlock(handle_data_buffer, i, handle_length,0); } enc_data_buffer.fill(handled_data_buffer,handled_length, handled_length+handle_length); handled_length += handle_length; } return enc_data_buffer; } /** * 对16个字节的数据块的加解密 * @param data_buffer 要加密的16字节数据块 * @param counter 分块代号,从0开始 * @param length data_buffer 的长度,<=16, 多数情况下为 16,只有在不满 16 字节时才会 < 16 * @param forward -1: 表示数据块头不足 16 字节,0 表示正好是 16 字节 或 数据块尾不满 16 节节 * @returns {Buffer} * @private */ _cryptBlock(data_buffer, counter, length, forward){ var xor_buffer = Buffer.alloc(block_size); xor_buffer.fill(this._iv.slice(0,8),0,8) xor_buffer.writeUIntBE(counter,10,6); let cipher = crypto.createCipheriv(alg,this._key,null); var xor_key_buffer = cipher.update(xor_buffer); cipher.final(); var enc_buffer = Buffer.alloc(length); if(forward === -1){ let length_diff = block_size - length; for(var i=length_diff;i<block_size;i++){ let number = data_buffer[i-length_diff] ^ xor_key_buffer[i]; enc_buffer[i-length_diff] = number; } } else { for(var i =0; i < length; i++){ let number = data_buffer[i] ^ xor_key_buffer[i]; enc_buffer[i] = number; } } return enc_buffer; } } exports.AES = AES;