AES对称加密实战——前端js加密后端python解密

引言

高级加密标准(AES, Advanced Encryption Standard),是一种最常见的对称加密算法 。其加密流程如下图所示,发送方通过密钥对明文加密后进行网络传输,接收方用同样的密钥将密文解密。在前后端通讯场景中,可利用AES算法对用户密码进行加密后传输,防止被抓包而造成密码泄露。

加解密流程

为了完成加解密操作,双方需要事先约定好: 秘钥长度,密钥,IV值,加密模式,填充方式共计5项内容。下面以128位密钥长度、CBC加密模式、零填充为例进行实战介绍,更多AES加密算法的细节读者可自行查阅。

前端 JS加解密

前端要实现AES加密,需要安装CryptoJS。 CryptoJS是一个JavaScript的加解密的工具包。它支持多种算法:MD5、SHA1、SHA2、SHA3、RIPEMD-160 的哈希散列,以及进行 AES、DES、Rabbit、RC4、Triple DES 加解密。

npm install --save crypto-js

新建secret.js,将相关操作封装成函数,代码如下:

// 引用AES源码js
const CryptoJS = require('crypto-js');  

// 128位的密钥与IV,与后端约定好
let crypt_key = 'OS7kWn9kGLmr7wxD';
let crypt_iv = 'AgBJQGRaHehjSgjT';
 
// 加密
export function encrypt(data) {
    // 将key解析为字节
    let aes_key = CryptoJS.enc.Utf8.parse(crypt_key);
    // 将iv解析为字节
    let new_iv = CryptoJS.enc.Utf8.parse(crypt_iv);
    // AES加密 CBC模式 ZeroPadding
    let encrypted = CryptoJS.AES.encrypt(data, aes_key, { 
        iv: new_iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.ZeroPadding
    });
    // 返回字符串
    return encrypted.toString();
}
 
//解密
export function decrypt(data) {
    let aes_key = CryptoJS.enc.Utf8.parse(crypt_key);
    let aes_iv = CryptoJS.enc.Utf8.parse(crypt_iv);
     // 将数据编码成Base64格式
    let baseResult=CryptoJS.enc.Base64.parse(data);  
    let ciphertext=CryptoJS.enc.Base64.stringify(baseResult);
    // AES解密 CBC模式 ZeroPadding
    let decryptResult = CryptoJS.AES.decrypt(ciphertext, aes_key, {    
        iv: aes_iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.ZeroPadding
    });
    // 返回字符串
    let resData = decryptResult.toString(CryptoJS.enc.Utf8).toString();
    return resData;
}

测试前端加解密:

import {encrypt, decrypt} from '@/utils/secret' 

let ps = '123456'
let encodePs = encrypt(ps)
let decodePs = decrypt(encodePs)
console.log('encodePs', encodePs)
// 输出:AA2uUXaD4p/s6HcPbX3CGw==
console.log('decodePs', decodePs)
// 输出:123456

后端Python加解密

后端Python要实现AES加密,需要安装 pycryptodome 。

pip install pycryptodome

新建secret.py,将相关操作封装成函数,代码如下:

import base64
import random

from Crypto.Cipher import AES

# 随机生成,用于加解密,与前端约定好
aes_key = 'OS7kWn9kGLmr7wxD'
aes_iv = 'AgBJQGRaHehjSgjT'

# 生成指定长度的16进制字符串
def generateRandomNum(n):
    s = ''
    valid_s = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    for i in range(n):
        s += random.choice(valid_s)
    return s

# AES-128-CBC 零填充 加解密
class AESCryptor:
    def __init__(self, key, iv):
        self.key = key.encode('utf-8')
        self.iv = iv.encode('utf-8')

    def encrypt_string(self, data):
        # 编码成字节
        data = data.encode('utf-8')
        # 填充为16的倍数 采用零填充
        while len(data) % 16 != 0:
            data += b'\x00'
        # 加密 采用CBC模式
        my_aes = AES.new(self.key, AES.MODE_CBC, self.iv)
        cipher_data = my_aes.encrypt(data)
        # 返回base64密文
        return base64.b64encode(cipher_data).decode('utf-8')

    def decrypt_string(self, data):
        # 编码为字节
        real_data = base64.b64decode(data)
        # 解密
        my_aes = AES.new(self.key, AES.MODE_CBC, self.iv)
        decrypt_data = my_aes.decrypt(real_data)
        # 解密后的数据去除加密前添加的pad,即0
        pos = len(decrypt_data)-1
        while decrypt_data[pos] == 0:
            pos -= 1
        decrypt_data = decrypt_data[:pos+1].decode('utf-8')
        return decrypt_data


if __name__ == '__main__':
    # 随机生成16*8 = 128位的key 和 iv,手动进行前后端同步
    # print('aes_key', generateRandomNum(16))
    # print('aes_iv', generateRandomNum(16))
    
    # 测试后端加解密
    aes = AESCryptor(aes_key, aes_iv)
    # 前端传回的加密后的数据
    encode_data = 'AA2uUXaD4p/s6HcPbX3CGw=='
    msg = aes.decrypt_string(encode_data)
    print(msg)
    # 输出:123456
    print(aes.encrypt_string(msg))
    # 输出:AA2uUXaD4p/s6HcPbX3CGw== 与前端加密后的数据相同

参考文献

  1. 利用POST进行用户登录的安全问题剖析
  2. 前端AES加密Python后端解密数据
  3. python实现AES加密解密

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
青葱年少的头像青葱年少普通用户
上一篇 2023年12月19日
下一篇 2023年12月19日

相关推荐