import fs from "fs"; import crypto from "crypto"; const toArrayBuffer = require('to-arraybuffer'); /*** * @breif 加密类对象 */ class Encrypt{ /*** * @breif 构造函数 * @param plaintext 密文信息 * @param key 密钥信息 mac+key * @param iv 向量iv */ constructor(plaintext: ArrayBuffer,key?: Uint8Array,iv?: Uint8Array) { /*** * @breif 初始化列表 */ this.errorInfo = ""; this.cryptError = false; this.digest = new Uint16Array(); this.ciphertext = new Uint8Array(); this.key = new Uint8Array(64); /*** * @breif 64位的随机数 */ if(key != undefined){ this.key = key; }else{ this.key = crypto.randomBytes(64); } /*** * @breif 切割随机数 */ this.aeskey = this.key.slice(0, 32); this.mackey = this.key.slice(32, 64); /*** * @breif 16位的随机数 */ if (iv != undefined){ this.iv = iv; }else{ this.iv = crypto.randomBytes(16); } /*** * @breif 执行加密过程 */ this.encryptProcess(plaintext); } /*** * @breif model */ public errorInfo: string; //错误信息 public cryptError: boolean; //是否加密成功 public iv: Uint8Array; //iv值 public key: Uint8Array; //传处的key; public mackey: Uint8Array; //Mac密钥 public aeskey: Uint8Array; //aes密钥 public digest: ArrayBuffer; //摘要 public ciphertext: ArrayBuffer;//密文 /*** * @breif 传入流返回密文 * @param plaintext 明文参数 */ public encryptProcess(plaintext: ArrayBuffer) { //AES加密 let ciphertext = this.calculateAESEncrypt(this.aeskey, plaintext, this.iv); //组合IV const ivAndCiphertext = new Uint8Array(16 + ciphertext.byteLength); // ivAndCiphertext = iv + ciphertext ivAndCiphertext.set(new Uint8Array(this.iv)); ivAndCiphertext.set(new Uint8Array(ciphertext), 16); //mac加密 let mac = this.calculateMAC(this.mackey, ivAndCiphertext.buffer); //组合Mac const encryptedBin = new Uint8Array(16 + ciphertext.byteLength + 32); //encryptedBin = ivAndCiphertext + mac encryptedBin.set(ivAndCiphertext); encryptedBin.set(new Uint8Array(mac), 16 + ciphertext.byteLength); //摘要算法SHA-256 let digest = this.calculateDigest(encryptedBin.buffer) this.digest = digest; this.ciphertext = encryptedBin.buffer; this.cryptError = true; } /*** * @breif AES加密 * @param aesKey AES密钥对象 * @param plaintext 明文 * @param iv AES加密向量 */ private calculateAESEncrypt(aesKey: ArrayBuffer,plaintext: ArrayBuffer,iv: ArrayBuffer):ArrayBuffer { const cipher = crypto.createCipheriv( 'aes-256-cbc', Buffer.from(new Uint8Array(aesKey).slice(0,32)), Buffer.from(new Uint8Array(iv).slice(0,16)) ); //console.log("加密阶段:plaintext", plaintext, "aeskey:", aesKey, "iv:", iv); let ciphertext = Buffer.concat([ cipher.update(Buffer.from(plaintext)), cipher.final() ]); //console.log("加密阶段:ciphertext", ciphertext); //console.log("开始进行AES解密:","aeskey",new Uint8Array(aesKey).slice(0,32),"iv",new Uint8Array(iv).slice(0,16)); //let decipher = crypto.createDecipheriv( // 'aes-256-cbc', // Buffer.from(new Uint8Array(aesKey).slice(0,32)), // Buffer.from(new Uint8Array(iv).slice(0,16)) //); //console.log("解密ciphertext:",ciphertext); //const decrypted = Buffer.concat([ // decipher.update(Buffer.from(ciphertext)), // decipher.final(), //]); //console.log("明文:",decrypted); this.ciphertext = ciphertext; return toArrayBuffer(this.ciphertext); } /*** * @breif mac摘要 * @param mackey mac密钥 * @param ivAndCiphertext IV+AES加密后的密文 * @return macDiegst 返回Mac摘要信息 */ /** node API*/ private calculateMAC(mackey: Uint8Array, ivAndCiphertext: ArrayBufferLike): ArrayBuffer{ return toArrayBuffer( crypto .createHmac('sha256', Buffer.from(mackey)) .update(Buffer.from(ivAndCiphertext)) .digest() ); } /*** * @breif SHA-256摘要算法 * @param encryptBuffr iv+decryptResult+mac * @return Diegst 返回摘要信息 */ private calculateDigest(ciphertext: ArrayBuffer): ArrayBuffer { return toArrayBuffer( crypto.createHash('sha256').update(Buffer.from(ciphertext)).digest() ); } /*** * @breif 字符串转换成uint8 * @param string 字符串数组 */ private stringToUint8Array(str: String, options = {stream: false}) { if (options.stream) { throw new Error(`Failed to encode: the 'stream' option is unsupported.`); } let pos = 0; const len = str.length; const out = []; let at = 0; // output position let tlen = Math.max(32, len + (len >> 1) + 7); // 1.5x size let target = new Uint8Array((tlen >> 3) << 3); // ... but at 8 byte offset while (pos < len) { let value = str.charCodeAt(pos++); if (value >= 0xd800 && value <= 0xdbff) { // high surrogate if (pos < len) { const extra = str.charCodeAt(pos); if ((extra & 0xfc00) === 0xdc00) { ++pos; value = ((value & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000; } } if (value >= 0xd800 && value <= 0xdbff) { continue; // drop lone surrogate } } // expand the buffer if we couldn't write 4 bytes if (at + 4 > target.length) { tlen += 8; // minimum extra tlen *= (1.0 + (pos / str.length) * 2); // take 2x the remaining tlen = (tlen >> 3) << 3; // 8 byte offset const update = new Uint8Array(tlen); update.set(target); target = update; } if ((value & 0xffffff80) === 0) { // 1-byte target[at++] = value; // ASCII continue; } else if ((value & 0xfffff800) === 0) { // 2-byte target[at++] = ((value >> 6) & 0x1f) | 0xc0; } else if ((value & 0xffff0000) === 0) { // 3-byte target[at++] = ((value >> 12) & 0x0f) | 0xe0; target[at++] = ((value >> 6) & 0x3f) | 0x80; } else if ((value & 0xffe00000) === 0) { // 4-byte target[at++] = ((value >> 18) & 0x07) | 0xf0; target[at++] = ((value >> 12) & 0x3f) | 0x80; target[at++] = ((value >> 6) & 0x3f) | 0x80; } else { // FIXME: do we care continue; } target[at++] = (value & 0x3f) | 0x80; } return target.slice(0, at); } /*** * @breif 生成随机数 * @param leng 生成随机数的位数 */ private getRandomBytes(leng: number): Uint8Array{ var str: string = ""; let size: number = leng/14 + 1; if (size > 0){ for (var i: number = 0;i < size;i++ ){ str += Math.random().toString(16).slice(2,16); } } return this.stringToUint8Array(str.slice(0,leng)); } } /*** * @breif 加密返回对象 */ export interface EnctyptObject{ ciphertext: ArrayBuffer; //密文对象 key: ArrayBuffer; //密钥(64) digest: ArrayBuffer; //摘要 iv?: Uint8Array; //向量 error?: Error; //错误信息 } /*** * @breif 通过路径获取摘要信息 * @param path 路径地址 * @return diegst SHA256摘要信息 */ export function getPathDigest(path: string): ArrayBuffer { return toArrayBuffer( crypto.createHash('sha256').update(Buffer.from(fs.readFileSync(path))).digest() ); } /*** * @breif 通过明文获取摘要信息 * @param plaintext 明文信息 * @return diegst 摘要信息 */ export function getPlaintextDigest(plaintext: ArrayBuffer): ArrayBuffer { return toArrayBuffer( crypto.createHash('sha256').update(Buffer.from(plaintext)).digest() ); } /*** * @breif 通过密文获取摘要信息 * @param ciphertext 密文数据 * @return diegst 摘要信息 */ export function getCiphertextDigest(ciphertext: ArrayBuffer): ArrayBuffer { return toArrayBuffer( crypto.createHash('sha256').update(Buffer.from(ciphertext)).digest() ); } /*** * @breif 对数据流进行进行加密接口函数 * @param arrayBuffer 明文信息 * @param key aes密钥信息 * @param iv iv盐值 * @return EnctyptObject 密文对象 */ export function enctyptSdk(arrayBuffer: ArrayBuffer,key?: Uint8Array,iv?: Uint8Array): EnctyptObject{ if (arrayBuffer == undefined){ throw new Error('stream is undefined'); } if(arrayBuffer.byteLength == 0 ){ throw new Error('stream error,stream length is 0'); } /*** * @breif 返回加密对象 */ let encryotObject: Encrypt = new Encrypt(arrayBuffer,key,iv); return { iv:encryotObject.iv, ciphertext: encryotObject.ciphertext, key: encryotObject.key, digest: encryotObject.digest }; }