node.js 实现 AES CTR 加解密

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;

 

 

posted @ 2019-06-11 07:27  feshfans  阅读(1035)  评论(0编辑  收藏  举报