前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >带你手撕 AES算法在Python中的使用

带你手撕 AES算法在Python中的使用

作者头像
Python攻城狮
发布2020-12-28 14:13:36
1.9K0
发布2020-12-28 14:13:36
举报
文章被收录于专栏:Python攻城狮Python攻城狮

记录一下AES加解密在python中的使用 研究AES之前先了解下常用的md5加密,既。然谈到md5,就必须要知道python3中digest()和hexdigest()区别。

  • hash.digest() 返回摘要,作为二进制数据字符串值
  • hash.hexdigest() 返回摘要,作为十六进制数据字符串值
代码语言:javascript
复制
# hashlib是涉及安全散列和消息摘要,提供多个不同的加密算法接口,如SHA1、SHA224、SHA256、SHA384、SHA512、MD5等。
import hashlib

md5 = hashlib.md5()
md5.update("test".encode('utf-8'))
print(u"digest返回的摘要:%s"% md5.digest())
print(u"hexdigest返回的摘要:%s"% md5.hexdigest())

AES:密码学中的高级加密标准(Advanced Encryption Standard,AES),又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。

AES分为几种模式,比如ECB,CBC,CFB、PGP、OFB、CTR等等这个我们可以点击源码即可看到。

代码语言:javascript
复制
#: Electronic Code Book (ECB). See `blockalgo.MODE_ECB`.
MODE_ECB = 1
#: Cipher-Block Chaining (CBC). See `blockalgo.MODE_CBC`.
MODE_CBC = 2
#: Cipher FeedBack (CFB). See `blockalgo.MODE_CFB`.
MODE_CFB = 3
#: This mode should not be used.
MODE_PGP = 4
#: Output FeedBack (OFB). See `blockalgo.MODE_OFB`.
MODE_OFB = 5
#: CounTer Mode (CTR). See `blockalgo.MODE_CTR`.
MODE_CTR = 6
#: OpenPGP Mode. See `blockalgo.MODE_OPENPGP`.
MODE_OPENPGP = 7

对于流加密,需要将分组密码转化为流模式工作。对于块加密(或称分组加密),如果要加密超过块大小的数据,就需要涉及填充和链加密模式。

  • ECB(Electronic Code Book电子密码本)模式

ECB模式是最早采用和最简单的模式,它将加密的数据分成若干组,每组的大小跟加密密钥长度相同,然后每组都用相同的密钥进行加密。

优点:

1.简单;  2.有利于并行计算;  3.误差不会被传送;

缺点: 1.不能隐藏明文的模式;  2.可能对明文进行主动攻击; 因此,此模式适于加密小消息。

  • CBC(Cipher Block Chaining,加密块链)模式

优点:

1.不容易主动攻击,安全性好于ECB,适合传输长度长的报文,是SSL、IPSec的标准。

缺点: 1.不利于并行计算;  2.误差传递; 3.需要初始化向量IV

  • CFB(Cipher FeedBack Mode,加密反馈)模式

优点:

1.隐藏了明文模式;  2.分组密码转化为流模式;  3.可以及时加密传送小于分组的数据;

缺点: 1.不利于并行计算;  2.误差传送:一个明文单元损坏影响多个单元;  3.唯一的IV;

OFB(Output FeedBack,输出反馈)模式

优点:

1.隐藏了明文模式;  2.分组密码转化为流模式;  3.可以及时加密传送小于分组的数据;

缺点: 1.不利于并行计算;  2.对明文的主动攻击是可能的;  3.误差传送:一个明文单元损坏影响多个单元 [4] 。

先说一下我踩得坑,我的版本是python3.7.9,之所以在引入的时候加了个备注# pycryptodome,是因为使用过程中我发现有的python环境需要装pycryptodome这个包,但引用AES的时候又用的是Crypto.Cipher,这里记录下这个坑。

代码语言:javascript
复制
from Crypto.Cipher import AES  # pycryptodome

PADDING

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

pkcs5padding和pkcs7padding的区别

pkcs5paddingpkcs7padding都是用来填充数据的一种模式。在ECB中,数据是分块加密的。如果需要加密的数据的字节码的长度不是块大小的整数倍就需要填充。 使用PKCS5,填充时: 要填充7个字节,那么填入的值就是0×7; 如果只填充1个字节,那么填入的值就是0×1; 恰好8个字节时还要补8个字节的0×08 正是这种即使恰好是8个字节也需要再补充字节的规定,可以让解密的数据很确定无误的移除多余的字节。

PKCS7和PKCS5的区别是数据块的大小;

  • PKCS5填充块的大小为8bytes(64位)
  • PKCS7填充块的大小可以在1-255bytes之间。

因为AES并没有64位的块, 如果采用PKCS5, 那么实质上就是采用PKCS7

python实现 安装所需要的包

pip install pycryptodome

python代码

代码语言:javascript
复制
# -*- coding:utf-8 -*-
import base64
from Crypto.Cipher import AES


class EncryptDate:
    def __init__(self, key):
        self.key = key  # 初始化密钥
        self.length = AES.block_size  # 初始化数据块大小
        self.aes = AES.new(self.key, AES.MODE_ECB)  # 初始化AES,ECB模式的实例
        # 截断函数,去除填充的字符
        self.unpad = lambda date: date[0:-ord(date[-1])]      

    def pad(self, text):
        """
        #填充函数,使被加密数据的字节码长度是block_size的整数倍
        """
        count = len(text.encode('utf-8'))
        add = self.length - (count % self.length)
        entext = text + (chr(add) * add)
        return entext

    def encrypt(self, encrData):  # 加密函数
        res = self.aes.encrypt(self.pad(encrData).encode("utf8"))
        msg = str(base64.b64encode(res), encoding="utf8")
        return msg

    def decrypt(self, decrData):  # 解密函数
        res = base64.decodebytes(decrData.encode("utf8"))
        msg = self.aes.decrypt(res).decode("utf8")
        return self.unpad(msg)


def sm3(text):
    if text is None or len(text) < 5:
        return ''
    return Encode.SM3_add_len(text).upper()


def md5(text):
    if text is None or len(text) < 5:
        return ''
    a = hashlib.md5()
    # 计算md5值
    a.update(text.encode(encoding='utf-8'))
    # 返回摘要,作为十六进制数据字符串值
    return a.hexdigest().upper()

密文示例:

密钥为 1234567890ABCEDF1234567890ABCEDF

  • 明文为 123456,加密后密文为:swqp5cQ56HME/pL99VuHkA==
  • 密文为:swqp5cQ56HME/pL99VuHkA==,解密后明文为 123456

可以看到代码中用到了SM3_add_len() 这里提供下SM3_add_len()的源码,其实就是用字符本身的长度拼接字符本身

代码语言:javascript
复制
def SM3_add_len(src):
    msg=str(src)
    msg=str(len(msg))+msg
    return Encode.SM3(msg)

测试

代码语言:javascript
复制
test_key = "1234567890ABCEDF1234567890ABCEDF"
test_txt = "123456"
eg2 = EncryptDate(test_key)  # 这里密钥的长度必须是16的倍数
en2 = eg2.encrypt(test_txt)  # 对123456进行加密,判断是否加密后等于swqp5cQ56HME/pL99VuHkA==

print("加密:", en2)
print("解密:", eg2.decrypt(en2))
assert en2 == 'swqp5cQ56HME/pL99VuHkA=='        # 判断是否加密后等于swqp5cQ56HME/pL99VuHkA==
assert eg2.decrypt(en2) == test_txt             # 对加密后的密文解密,判断是否等于123456

test_bank_card = '6222222222222222222'
print(sm3(test_bank_card))
# 判断6222222222222222222 经过ECB/PKCS5Padding加密后是否等于5A7E1B360D38CBFDFF078A197283C772C40A4903C1C2D9A082977F209508AFC1
assert sm3(test_bank_card) == '5A7E1B360D38CBFDFF078A197283C772C40A4903C1C2D9A082977F209508AFC1'

# 判断110000201912010000 经过md5加密后是否等于E8C8E29576C886A87A80C39B405F0A19
id_no = '110000201912010000'
print(md5(id_no))
assert md5(id_no) == 'E8C8E29576C886A87A80C39B405F0A19'
ph_no = '13800000000'
print(md5(ph_no))
assert md5(ph_no) == '5DAAD257487F1B493114181A22E37EB5'

可以看到我这里测试用了断言(assert),这个我上一篇文章刚讲过,主要是判断如果它的条件返回错误,则终止程序执行。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • PADDING
  • pkcs5padding和pkcs7padding的区别
  • 密文示例:
  • 测试
相关产品与服务
GPU 云服务器
GPU 云服务器(Cloud GPU Service,GPU)是提供 GPU 算力的弹性计算服务,具有超强的并行计算能力,作为 IaaS 层的尖兵利器,服务于深度学习训练、科学计算、图形图像处理、视频编解码等场景。腾讯云随时提供触手可得的算力,有效缓解您的计算压力,提升业务效率与竞争力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档