• 用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("
    ", "")
        # 此处我的输出结果老有换行符,所以用了临时方法将它剔除
        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)
    
  • 相关阅读:
    lua table
    lua basic
    lua5.4 coroutine的通俗理解
    HarmonyOS实战—实现双击事件
    HarmonyOS实战—单击事件的四种写法
    HarmonyOS实战—实现单击事件流程
    苹果CMS对接公众号教程
    Spring快速入门
    YYCMS搭建影视网站教程
    分享几个开源Java写的博客系统
  • 原文地址:https://www.cnblogs.com/gqv2009/p/14365911.html
Copyright © 2020-2023  润新知