天天看点

crypto-js实现前端《AES/DES》加密,python进行对应的后端解密

最近学习爬虫在处理用户登录注册时,网站数据在传输时一般都是进行了加密处理后再传输的,因此想到对网站的登录操作进行加密,查阅资料找到了前端使用crypto-js来进行加密,后端使用python对应的解密方式反向解密。后续慢慢一个一个的加密方式补充。

一、对称加密AES、DES

1. AES加密解密

1.AES-128-CBC

Zero:数据长度不足时填充0,如果数据长度刚好合适就不填充

PKCS5:填充8字节长度的ASCII码编号为8的字符

PKCS7:(1) 数据如果长度刚好合适,就填充数据长度的字节,填充数据为ASCII码编号为数据长度的字符

​ (2)数据长度如果没对齐,则差n长度,就补充n长度的ASCII码编号为n的字符

分析

  1. 前端参数
    参数 类型 长度/bytes 描述
    key hex_str 16 加密秘钥
    iv hex_str 16 偏移量,采用key MD5加密获取
    data str 16n 加密的数据,不足的根据padding来进行填充
    mode / / 加密方式
    padding / / 数据填充方式
  2. 前端逻辑分析
    1. key秘钥采用随机数生成16为字符,然后通过Crypto.enc.UTF8.parse解析成需要的key
    2. iv偏移量采用key进行MD5加密后取前16个字符作为偏移量
    3. AES加密后的数据最后再base64加密后显现的
    4. 数据传输时加密把key和加密后的数据传到后端
  3. 后端参数
    参数 类型 长度/bytes 描述
    key str 16 前端出来的加密秘钥
    iv str 16 偏移量,采用key MD5加密后取前16位
    data bytes / 从前端传来的数据,通过base64解密后的
    mode / / 加密方式
    padding / / 数据填充方式
  4. 后端逻辑分析
    1. 拿到随机的key字符串,然后转化为MD5,取前面16个字符作为偏移量iv
    2. 将前端传来的加密数据先base64解密为需要AES解密的数据
    3. AES解密,key,iv都时字符串
    4. 不同的数据填充方式,获取的最后解密的数据是不同的,需要根据不同的数据填充方式转化为最终我们需要的前端原始未加密的字符串

js前端实现

// n位随机数生成
function randomNum(n) {
    let sString = "";
    let strings = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    for (i = 0; i < n; i++) {
        ind = Math.floor(Math.random() * strings.length);
        sString += strings.charAt(ind);
    }
    return sString
}

//AES-128-CBC-ZeroPadding加密
function encrypt(data, key) {
    iv = CryptoJS.MD5(key).toString().substring(0, 16);  //取转化为md5格式的前面16位字符
    key = CryptoJS.enc.Utf8.parse(key);  //解析后的key
    iv = CryptoJS.enc.Utf8.parse(iv); //解析后的iv
    encrypted = CryptoJS.AES.encrypt(data, key, { //j加密
        iv: iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.ZeroPadding
    });
    return encrypted.toString()

}

           

后端解密

import base64
import hashlib
from Crypto.Cipher import AES, DES
class DeAesCrypt:
    """
    AES-128-CBC解密
    """
    
    def __init__(self, data, key, pad):
        """
        :param data: 加密后的字符串
        :param key: 随机的16位字符
        :param pad: 填充方式
        """
        self.key = key
        self.data = data
        self.pad = pad.lower()

        hash_obj = hashlib.md5()  # 构造md5对象
        hash_obj.update(key.encode())  # 进行md5加密,md5只能对byte类型进行加密
        res_md5 = hash_obj.hexdigest()  # 获取加密后的字符串数据
        self.iv = res_md5[:16]

    @property
    def decrypt_aes(self):
        """AES-128-CBC解密"""
        real_data = base64.b64decode(self.data)
        my_aes = AES.new(self.key, AES.MODE_CBC, self.iv)

        decrypt_data = my_aes.decrypt(real_data)
        return self.get_str(decrypt_data)

    def get_str(self, bd):
        """解密后的数据去除加密前添加的数据"""
        if self.pad == "zero":  # 去掉数据在转化前不足16位长度时添加的ASCII码为0编号的二进制字符
            return ''.join([chr(i) for i in bd if i != 0 ])
        
        elif self.pad == "pkcs7":  # 去掉pkcs7模式中添加后面的字符
            return ''.join([chr(i) for i in i > 32])
        
        else:
            return "不存在此种数据填充方式"
           
crypto-js实现前端《AES/DES》加密,python进行对应的后端解密

2. DES加密解密

1. DES-128-CBC模式

分析

  1. 前端参数
    参数 类型 长度/bytes 描述
    key hex_str 8 加密秘钥
    iv hex_str 8 偏移量,采用key base64加密获取
    data str 16n 加密的数据,不足的根据padding来进行填充
    mode / / 加密方式
    padding / / 数据填充方式
  2. 前端逻辑分析
    1. key秘钥采用随机数生成8为字符,然后通过Crypto.enc.UTF8.parse解析成需要的key
    2. iv偏移量采用key进行base64加密后取前8个字符作为偏移量
    3. DES加密后的数据最后再base64加密后显现的
    4. 数据传输时加密把key和加密后的数据传到后端
  3. 后端参数
    参数 类型 长度/bytes 描述
    key str 8 前端出来的加密秘钥
    iv str 8 偏移量,采用key base64加密获取前面8位
    data bytes / 从前端传来的数据,通过base64解密后的
    mode / / 加密方式
    padding / / 数据填充方式
  4. 后端逻辑分析
    1. 拿到随机的key字符串,然后转化为base64,取前面8个字符作为偏移量iv
    2. 将前端传来的加密数据先base64解密为需要DES解密的数据
    3. DES解密,key,iv都时字符串
    4. 不同的数据填充方式,获取的最后解密的数据是不同的,需要根据不同的数据填充方式转化为最终我们需要的前端原始未加密的字符串

js前端实现

// n位随机数生成
function randomNum(n) {
    let sString = "";
    let strings = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    for (i = 0; i < n; i++) {
        ind = Math.floor(Math.random() * strings.length);
        sString += strings.charAt(ind);
    }
    return sString
}


// DES-128-CBC-Zero-crypt
function desZeroEncrypt(data, key) {
    let iv = CryptoJS.enc.Utf8.parse(key);
    iv = CryptoJS.enc.Base64.stringify(iv).toString().substring(0,8);//base64加密取前8位
    iv = CryptoJS.enc.Utf8.parse(iv);
    key = CryptoJS.enc.Utf8.parse(key);
   
    
    return CryptoJS.DES.encrypt(data, key, {
        iv: iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.ZeroPadding
    }).toString();

}
           

后端解密

import base64
import hashlib
from Crypto.Cipher import AES, DES
class DeDesCrypt:
    """
    DES-128-CBC解密
    """

    def __init__(self, data, key, pad):
        """
        :param data: 加密后的字符串,在解密是需要先进行base64解密后才行
        :param key: 随机的8位字符
        :param pad: 填充方式
        """
        self.key = key
        self.data = base64.b64decode(data.encode())
        self.pad = pad.lower()
        self.iv = base64.b64encode(key.encode())[0:8]

    @property
    def decrypt_des(self):
        """DES-128-CBC解密"""
        my_des = DES.new(self.key, AES.MODE_CBC, self.iv)

        decrypt_data = my_des.decrypt(self.data)
        return self.get_str(decrypt_data)

    def get_str(self, bd):
        """解密后的数据去除加密前添加的数据"""
        if self.pad == "zero":  # 去掉数据在转化前不足8位长度时添加的ASCII码为0编号的二进制字符
            return ''.join([chr(i) for i in bd if i != 0])
        elif self.pad == "pkcs7":  # 去掉pkcs7模式中添加后面的字符
            return ''.join([chr(i) for i in i > 32])
        else:
            return "不存在此种数据填充方式"