前端AES加密Python后端解密数据

前言:账号密码一直对我们来说真的非常非常重要,但大多数人不是很重视,比如日常工作中,员工会经常登录到不同网站去查数据或者完成自己的工作,但是账号密码他们不一定会保存,经常会忘了。或者他们的密码都是名字拼音或者简单的数字,员工忘记密码管理员可以帮忙修改,但是密码在网上泄露那么会造成无可挽回的损失。自己平常也有很多站点的账号密码,以前是放在记事本,也试过放在gitee,感觉太危险了,万一丢了,或者忘了在哪就太麻烦了。为了方便自己,后来写了一个密码管理系统,用到了AES加密相关知识,对自己帮助挺大的,分享一下这块技术。

一、什么是AES
高级加密标准(AES,Advanced Encryption Standard),是一种最常见的对称加密算法,AES在世界各地的软件和硬件中实施加密敏感数据。

AES的加密流程介绍

1.明文P:没有经历加密的数据
2.密钥K:用来加密明文的密码,在对称加密算法中,加密与解密的密钥是相同的。
密钥为接收方与发送方协商产生,但不可以直接在网络上传输,否则会导致密钥泄漏,通常是通过非对称加密算法加密密钥,
然后再通过网络传输给对方,或者直接面对面商量密钥。密钥是绝对不可以泄漏的,否则会被攻击者还原密文,窃取机密数据。
3.AES加密函数
经加密函数处理后的数据
4.AES解密函数
设AES解密函数为D,则 P = D(K, C),其中C为密文,K为密钥,P为明文。也就是说,把密文C和密钥K作为解密函数的参数输入,则解密函数会输出明文P。

AES基本的结构

AES为分组密码,分组密码就是把明文分成一组一组的,每组长度相等,每次加密一组数据,直到加密完整个明文。在AES标准规范中,分组长度只能是128位,也就是说每个分组为16个字节(每个字节8位)。
AES 包括三种分组密码:AES-128、AES-192 和 AES-256。
AES-128使用128位密钥长度来加密和解密消息块
AES-192使用192位密钥长度来加密和解密消息块
AES-256使用256位密钥长度来加密和解密消息块

AES加密的模式
AES分为几种模式,比如ECB,CBC,CFB等等,这些模式除了ECB由于没有使用IV而不太安全,其他模式差别并没有太明显,大部分的区别在IV和KEY来计算密文的方法略有区别。

AES加密中IV的作用
IV称为初始向量,不同的IV加密后的字符串是不同的,加密和解密需要相同的IV,既然IV看起来和key一样,却还要多一个IV的目的,对于每个块来说,key是不变的,但是只有第一个块的IV是用户提供的,其他块IV都是自动生成。
IV的长度为16字节。超过或者不足,可能实现的库都会进行补齐或截断。但是由于块的长度是16字节,所以一般可以认为需要的IV是16字节。

AES加密中PADDING的作用
PADDING是用来填充最后一块使得变成一整块,所以对于加密解密两端需要使用同一的PADDING模式,大部分PADDING模式为PKCS5, PKCS7, NOPADDING。

AES加密和解密端
对于加密端,应该包括:加密秘钥长度,密钥,IV值,加密模式,PADDING方式。
对于解密端,应该包括:解密秘钥长度,密钥,IV值,解密模式,PADDING方式。

二、前端实现AES加密解密功能
前端要实现AES加密,需要下载crypto-js.js,crypto-js是一个纯javascript写的加密算法类库,可以非常方便地在javascript进行 MD5、SHA1、SHA2、SHA3、RIPEMD-160 哈希散列,进行 AES、DES、Rabbit、RC4、Triple DES 加解密

下载链接如下:

https://github.com/brix/crypto-js/releases

前端代码如下:

crypt_key = 'l36DoqKUYQP0N7e1';
crypt_iv = '131b0c8a7a6e072e';

//加密
function encrypt(data) {
    let aes_key = CryptoJS.enc.Utf8.parse(crypt_key);  //解析后的key
    let new_iv = CryptoJS.enc.Utf8.parse(crypt_iv); //解析后的iv
    encrypted = CryptoJS.AES.encrypt(data, aes_key, { //AES加密
        iv: new_iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.ZeroPadding
    });
    return encrypted.toString()
}

//解密
function decrypt(data) {
    let aes_key = CryptoJS.enc.Utf8.parse(crypt_key);  // 解析后的key
    let aes_iv = CryptoJS.enc.Utf8.parse(crypt_iv);   // 解析后的iv
    let baseResult=CryptoJS.enc.Base64.parse(data);   // Base64解密
    let ciphertext=CryptoJS.enc.Base64.stringify(baseResult);     // Base64解密
    let decryptResult = CryptoJS.AES.decrypt(ciphertext,aes_key, {    // AES解密
        iv: aes_iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.ZeroPadding
    });

    resData = decryptResult.toString(CryptoJS.enc.Utf8).toString();
    return resData;
}

测试下前端代码

console.log(encrypt('456')); 加密
console.log(decrypt('aCbhraRUHLvqxdoG5amBNQ==')); 解密

查看结果:

 

 

三、后端实现AES加密解密功能 

import base64
import hashlib
from Crypto.Cipher import AES, DES
from binascii import b2a_hex, a2b_hex


class DeAesCrypt:
    """
    AES-128-CBC解密
    """
    def __init__(self, data, key, pad='zero'):
        """
        :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 add_to_16(self,text):
        pad = 16 - len(text.encode('utf-8')) % 16
        text = text + pad * chr(pad)
        return text.encode('utf-8')


    def encrypt(self):
        """AES-128-CBC加密"""
        # 预处理,填充明文为16的倍数
        text = self.add_to_16(self.data)
        cryptor = AES.new(self.key,AES.MODE_CBC, self.iv)

        cipher_text = cryptor.encrypt(text)

        base = 16

        if base == 16:
            # 返回16进制密文
            return b2a_hex(cipher_text).decode('utf-8')
        elif base == 64:
            # 返回base64密文
            return base64.b64encode(cipher_text).decode('utf-8')


    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 bd if i > 32])

        else:
            return "不存在此种数据填充方式"

四、效果展示  

前端添加用户名test,密码为123456

 

 

 

查看传送的数据是否加密,可以看到账号密码都已经加密过了

 

 后端检查解密数据是否正确

username=DeAesCrypt(data=info_data.get('username'),key=info_data.get('aes_key')).decrypt_aes
password=DeAesCrypt(data=info_data.get('password'),key=info_data.get('aes_key')).decrypt_aes
{'username': 'test', 'password': '123456'}

  

详细AES加密知识可以查看下面链接

https://blog.csdn.net/qq_28205153/article/details/55798628

补充:

由于 PyCrypto 已经超过三年无人维护,因此 Github 上的开发者 Varbin 在该项目的 Github issue 里呼吁开发们不要再使用 PyCrypto ,而应该将 PyCrypto 替换为 PyCryptodome。
对于使用 PyCrypto 的已有项目而言,PyCryptodome 保持了与 PyCrypto 相当高的兼容性并且处于良好的维护状态,因此便于更换。对于要使用 Python 加密库的新项目,建议开发者使用 PyCryptodome 或者 cryptography。

 

posted @ 2021-08-18 16:38  lucky_tomato  阅读(1797)  评论(0编辑  收藏  举报