用python3實現AES/CBC/PKCS5padding算法加解密


一、背景

將java代碼的AES加密demo用python語言實現(通過pycryptodome包)

二、關鍵詞

Python3、pycryptodome、AES/CBC/PKCS5padding、中文

三、java代碼加密demo

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;

private static String sKey = "********************************"; //密鑰是string類型
private static String ivParameter = sKey.substring(0, 16); ; //偏移量是密鑰截取16位,也是string類型

/**
* AES 加密
* @param str 明文
* @param key 秘鑰
* @return 返回加密密文
* @throws Exception
*/
public static String encrypt(String str) throws Exception {
    try {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        byte[] raw = sKey.getBytes();  // 密鑰轉成byte
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes()); //偏移量轉成byte
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
        byte[] encrypted = cipher.doFinal(str.getBytes("utf-8"));
        return Base64.encodeBase64String(encrypted); //base64編碼
    } catch (Exception ex) {
        return null;
    }
}

四、java代碼解密demo

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;

private static String sKey = "********************************";
private static String ivParameter = sKey.substring(0, 16); ; 
/**
* AES 解密
* @param str 密文
* @param key 秘鑰
* @return 返回明文
* @throws Exception
*/
public static String decrypt(String str) throws Exception {
    try {
        byte[] raw = sKey.getBytes();
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes()); //偏移量
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
        byte[] encrypted1 = Base64.decodeBase64(str);
        byte[] original = cipher.doFinal(encrypted1);
        String originalString = new String(original, "utf-8");
        return originalString;
    } catch (Exception ex) {
        return null;
    }
} 

五、java代碼解釋說明

通過以上java代碼可以知曉以下信息:

AES加密相關:

AES加密模式:CBC
填充方式:PKCS5
偏移量:密鑰截取前16位
輸出:base64
字符:utf-8
其他信息:

密鑰是string類型,使用時需轉成bytes
偏移量也是string類型,使用時需轉成bytes

六、python代碼實現

使用 pycryptodome 進行 AES/CBC/PKCS5(算法/模式/補碼方式) 加密

from Crypto.Cipher import AES  
from base64 import b64decode, b64encode

BLOCK_SIZE = AES.block_size
# 不足BLOCK_SIZE的補位(s可能是含中文,而中文字符utf-8編碼占3個位置,gbk是2,所以需要以len(s.encode()),而不是len(s)計算補碼)
pad = lambda s: s + (BLOCK_SIZE - len(s.encode()) % BLOCK_SIZE) * chr(BLOCK_SIZE - len(s.encode()) % BLOCK_SIZE)
# 去除補位
unpad = lambda s: s[:-ord(s[len(s) - 1:])]


class AESCipher:
    def __init__(self, secretkey: str):
        self.key = secretkey  # 密鑰
        self.iv = secretkey[0:16]  # 偏移量

    def encrypt(self, text):
        """
        加密 :先補位,再AES加密,后base64編碼
        :param text: 需加密的明文
        :return:
        """
        # text = pad(text) 包pycrypto的寫法,加密函數可以接受str也可以接受bytess
        text = pad(text).encode()  # 包pycryptodome 的加密函數不接受str
        cipher = AES.new(key=self.key.encode(), mode=AES.MODE_CBC, IV=self.iv.encode())
        encrypted_text = cipher.encrypt(text)
        # 進行64位的編碼,返回得到加密后的bytes,decode成字符串
        return b64encode(encrypted_text).decode('utf-8')

    def decrypt(self, encrypted_text):
        """
        解密 :偏移量為key[0:16];先base64解,再AES解密,后取消補位
        :param encrypted_text : 已經加密的密文
        :return:
        """
        encrypted_text = b64decode(encrypted_text)
        cipher = AES.new(key=self.key.encode(), mode=AES.MODE_CBC, IV=self.iv.encode())
        decrypted_text = cipher.decrypt(encrypted_text)
        return unpad(decrypted_text).decode('utf-8')

七、執行結果

密鑰應由16位,24位,32位字母或數字組成,。本例使用32位的密鑰進行加密

###secretkey = '6agrioBE1D9yoGOX4yyDMyMFs72jYvJ8'  # 密鑰
text = '使用 pycryptodome 進行 AES/CBC/PKCS5(算法/模式/補碼方式) 加密'  # 待加密的明文
encrypted_text = AESCipher(secretkey).encrypt(text)  # 加密
>>>'yzPmbAOq5Wl8bMYcG/UWgY46r5xjq5VYFbJVqXWnpZQofmk0OXpkato7dT0diuV9qRsG+dQ209wfVt1NaSopt9syqAXm6zH0I91TaLD4Ua4='
decrypted_text = AESCipher(secretkey).decrypt(encrypted_text)  # 解密
>>>'使用 pycryptodome 進行 AES/CBC/PKCS5(算法/模式/補碼方式) 加密'

八、案例

from Crypto.Util.Padding import pad
import base64
import random
from base64 import b64decode, b64encode
from Crypto.Cipher import AES

# 去除補位
unpad = lambda s: s[:-ord(s[len(s) - 1:])]

def aes_encrypt(key, aes_str):
    # 使用key,選擇加密方式
    vi = "16-Bytes--String"
    aes = AES.new(key.encode('utf-8'), AES.MODE_CBC,vi.encode('utf8'))
    pad_pkcs7 = pad(aes_str.encode('utf-8'), AES.block_size, style='pkcs7')  # 選擇pkcs7補全
    encrypt_aes = aes.encrypt(pad_pkcs7)
    # 加密結果
    encrypted_text = str(base64.encodebytes(encrypt_aes), encoding='utf-8')  # 解碼
    encrypted_text_str = encrypted_text.replace("\n", "")
    # 此處我的輸出結果老有換行符,所以用了臨時方法將它剔除
    return encrypted_text_str


def aes_decrypt(key,text):
    """
    解密 :偏移量為key[0:16];先base64解,再AES解密,后取消補位
    :param encrypted_text : 已經加密的密文
    :return:
    """
    iv = "16-Bytes--String"
    encrypted_text = b64decode(text)
    cipher = AES.new(key=key.encode(), mode=AES.MODE_CBC, IV=iv.encode())
    decrypted_text = cipher.decrypt(encrypted_text)
    return unpad(decrypted_text).decode('utf-8')


if __name__ == '__main__':
    # key的長度需要補長(16倍數),補全方式根據情況而定,此處我就手動以‘0’的方式補全的32位key
    # key字符長度決定加密結果,長度16:加密結果AES(128),長度32:結果就是AES(256)
    # key = "".join(random.sample("0123efghjkl456mnopqstuvwsz789abcdef", 16))
    key = "4F5U384JGMK35XUV"
    # 加密字符串長同樣需要16倍數:需注意,不過代碼中pad()方法里,幫助實現了補全(補全方式就是pkcs7)
    aes_str = '{"appKey":"F34AA6648478A344","channel":"17","data":{"phone":"19965412406"},"imeiIdfa":"355455060276059","oaid":"","os":"Android","uuid":"ffffffff-fd9f-ae04-fb56-4c4c00000000","version":"42"}'

    encryption_result = aes_encrypt(key, aes_str)
    print(encryption_result)
    # text = "aNUkeNljo4XhsxBX5G92JL6M/DMm+KG9fUq1X80s9d2zDSO2IBvYrm8kce8SlFAXdhqsMLVjTSpiyZatS2wP+haxLha/aF6PabApn9fFYKbRjA++d6ZkWDjPfU62ik1eWBnQaUYt4mwZUEJjnknXCrVGVjtg0mrDQzwoQzAFzKK9Wy4c5riA84WColA1+dRj+tbbxIuuXP7IXxp/246Bg61a4plA3nBuSNrZ/SorjSavBAcK25Z2KMZ2q9ni1q//"
    text = "XElQzQ8wkCFRt9QXe178vUDUTRLC2QpSdOPxTnutN4c="
    decrypt_result = aes_decrypt(key, text)
    print(decrypt_result)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM