1.图片加解密的公共数据:key、iv等
// 字符串转字节数组的方法
const stringToBytes = (str: string) => {let ch = 0let st = []let re: any[] = []for (let i = 0; i < str.length; i++) {ch = str.charCodeAt(i) // get charst = [] // set up "stack"do {st.push(ch & 0xFF) // push byte to stackch = ch >> 8 // shift value down by 1 byte}while (ch)// add stack contents to result// done because chars have "wrong" endiannessre = re.concat(st.reverse())}// return an array of bytesreturn re
}/*** 生成加解密:公共加密key、iv、*/
export const CryptoGlobal = (Codekey: string) => {// 将上面下载的图片转为base64编码,并处理key和iv的格式const key = Codekey || 'xxxxxxxxxx'const iv = 'xxxxxxxxxxxxxxx'// 密钥转字节数组(16位) let keyBy = stringToBytes(key)let ivBy = stringToBytes(iv)// 字节数组转Uint8Arraylet keyBv = new Uint8Array(keyBy)let ivBv = new Uint8Array(ivBy)// Uint8Array转WordArraylet keyWA = CryptoU8array.u8array.parse(keyBv) let ivWA = CryptoU8array.u8array.parse(ivBv)return {key: keyWA,subtile: {iv: ivWA,// 解密方式和后端商定为为CBC模式mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7}}
}
2.图片插件所需的方法u8array方法
注:此代码我用了单独的代码管理,文件名:enc-u8array.ts
const CryptoJS = require(‘crypto-js’);
CryptoJS.enc.u8array = {/*** Converts a word array to a Uint8Array.** @param {WordArray} wordArray The word array.** @return {Uint8Array} The Uint8Array.** @static** @example** var u8arr = CryptoJS.enc.u8array.stringify(wordArray);*/stringify: function (wordArray: { words: any; sigBytes: any }) {// Shortcutsvar words = wordArray.wordsvar sigBytes = wordArray.sigBytes// Convertvar u8 = new Uint8Array(sigBytes)for (var i = 0; i < sigBytes; i++) {var byte = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xffu8[i] = byte}return u8},/*** Converts a Uint8Array to a word array.** @param {string} u8Str The Uint8Array.** @return {WordArray} The word array.** @static** @example** var wordArray = CryptoJS.enc.u8array.parse(u8arr);*/parse: function (u8arr: string | any[]) {// Shortcutvar len = u8arr.length// Convertvar words: number[] = []for (var i = 0; i < len; i++) {words[i >>> 2] |= (u8arr[i] & 0xff) << (24 - (i % 4) * 8)}return CryptoJS.lib.WordArray.create(words, len)}
}
export default {u8array: CryptoJS.enc.u8array
}
3.图片加密
const CryptoJS = require('crypto-js');
import CryptoU8array from './enc-u8array'
import { CryptoGlobal } from './cryptoGlobal'
/*** 图片加密*/
export function avatarEncrypt(type: string, reorganizationUrl: string, file: any, Codekey?: string, ) {const recodekey = Codekey?.slice(0, 16)const { key, subtile } = CryptoGlobal(recodekey)const messageWordArray = CryptoU8array.u8array.parse(file);let encrypted = CryptoJS.AES.encrypt(messageWordArray, key, subtile);const encryptedBytes = encrypted.ciphertext;const toWordArray = CryptoU8array.u8array.stringify(encryptedBytes)let blob = new Blob([toWordArray], { type }); // 将加密后串转bloblet blobToFile = new File([blob], reorganizationUrl);return blobToFile
}
4.图片解密
import axios from 'axios'
const CryptoJS = require('crypto-js');
import CryptoU8array from './enc-u8array'
import { CryptoGlobal } from './cryptoGlobal'/*** base64转blob* @param {*} base64 * @returns */
function base64ToBlob(base64: string) {const binary = atob(base64.split(',')[1]);const array = [];for (let i = 0; i < binary.length; i++) {array.push(binary.charCodeAt(i));}return new Blob([new Uint8Array(array)], { type: 'image/jpeg' }); // 这里的type可以根据实际情况进行修改
}/*** 头像解密* @param {*} buffer * @returns */
function avatarDecrypt(buffer: ArrayBuffer, Codekey?: string) {let view = new Uint8Array(buffer)// 将Uint8Array 转成 WordArraylet contentWA = CryptoU8array.u8array.parse(view)// base64字符串let dcBase64String = contentWA.toString(CryptoJS.enc.Base64)// 解密const { key, subtile } = CryptoGlobal(Codekey)let decryptedData = CryptoJS.AES.decrypt(dcBase64String, key, subtile)console.log(decryptedData)// 把解密后的对象再转为base64编码,这步是关键,跟解密文字不同let d64 = decryptedData.toString(CryptoJS.enc.Base64)let encodedData = 'data:image/png;base64,' + d64// 将blob解析URLconst blob = base64ToBlob(encodedData);var url = URL.createObjectURL(blob);return url
}/*** 获取图片路径并调用头像解密方法* @param list 路径* @param key sessionId 对话图片才会需要用* @returns */
export async function decodeAvatarUrl(list: string, key?: string) {const resetList = list[0]=== '{' && list[list.length - 1] === '}' ? JSON.parse(list) : listlet isListType = null;if(resetList != null) {isListType = typeof resetList === 'string' ? resetList : resetList?.urlisListType = isListType.replace(/^http:/gi, '')// 判断是否需要解密头像, 并判断图片数据是否有损坏,损坏显示默认头像try {if(isListType.includes('@#¥%&')) {const res: {data: ArrayBuffer} = await axios.request({responseType: 'arraybuffer',url: isListType,method: 'get'})if(res.data) {const reKey = key?.slice(0, 16)const avatarBase64 = avatarDecrypt(res.data, reKey)isListType = avatarBase64} else {isListType = null}}} catch (error) {isListType = null}}return isListType
}
5.图片加密使用
/** 上传图片方法传参校验 */
type UploadFileType = {/** 图片数据:Uint8Array */stream: any;/** 图片名字 */key: string,/** 上传图片的类型: 1:头像 2:消息图片 */updateType: number;/** sessionId */sessionId: string
}
/*** 上传图片 -> 七牛云** @param stream 图片流:Uint8Array* @param stream 图片名称* @param updateType 1:头像 2:消息图片* @param sessionId 1:头像 2:消息图片* */
export const uploadFile = ({ stream, key, updateType, sessionId }: UploadFileType) => {return new Promise(async (resolve: (res: { rescode: number, url: string, error: any }) => void, reject) => {const folderOs = 'Desktop'const folder = updateType === 1 ? 'avatars' : 'conversations'const folderEnd = '@#¥%&'const reorganizationUrl = `${folderOs}/${folder}/${reorganizationName()}${folderEnd}`const imageType = 'image/' + key.split('.').pop()const putExtra = { // 上传需要的参数fname: '',params: {},mimeType: [imageType]};const config = { // 上传需要的参数useCdnDomain: true,};// token是接口获取const { data } = await updateImageToken()const { qiniuLinkUrl, token } = dataconst decode: any = await avatarEncrypt(imageType, reorganizationUrl, stream, sessionId)const observable = qiniu.upload(decode, reorganizationUrl, token, putExtra, config);observable.subscribe({next(res: any){// console.log(res)},error(err: any){ // 失败// console.log(err)reject({error: err,url: '',rescode: 100})}, complete(res: any){ // 成功// console.log(res)resolve({error: null,url: qiniuLinkUrl+reorganizationUrl,rescode: 200})}})})
}
6.图片解密使用
import { decodeAvatarUrl } from '@/utils/decodeAvatar'
// src: 图片路径,sessionId: 唯一key值,每条对话单独的id
decodeAvatarUrl(src, sessionId)
参考:
CSDN(注:仅能参考解密,不够全面):https://blog.csdn.net/ningtt/article/details/118301703
简书(参考的这篇加密,加解密都有,较为全面):https://www.jianshu.com/p/1f75cd571786