Python 加密与解密小结
这篇文章主要介绍了Python 加密与解密,使用base64或pycrypto模块
前言
据记载,公元前400年,古希腊人发明了置换密码。1881年世界上的第一个电话保密专利出现。在第二次世界大战期间,德国军方启用“恩尼格玛”密码机,密码学在战争中起着非常重要的作用。随着信息化和数字化社会的发展,人们对信息安全和保密的重要性认识不断提高,于是在1997年,美国国家标准局公布实施了“美国数据加密标准(DES)”,民间力量开始全面介入密码学的研究和应用中,采用的加密算法有DES、RSA、SHA等。随着对加密强度需求的不断提高,近期又出现了AES、ECC等。
使用密码学可以达到以下目的:保密性:防止用户的标识或数据被读取。数据完整性:防止数据被更改。身份验证:确保数据发自特定的一方。
我们所说的加密方式,都是对二进制编码的格式进行加密的,对应到Python中,则是我们的Bytes
。所以当我们在Python中进行加密操作的时候,要确保我们操作的是Bytes
,否则就会报错。注:两位十六进制常常用来显示一个二进制字节。
将字符串和Bytes
互相转换可以使用encode()
和decode()
方法。
# 方法中不传参数则是以默认的utf-8编码进行转换
In [1]: '南北'.encode()
Out[1]: b'\xe5\x8d\x97\xe5\x8c\x97'In [2]: b'\xe5\x8d\x97\xe5\x8c\x97'.decode()
Out[2]: '南北'
利用binascii
模块可以将十六进制显示的字节转换成我们在加解密中更常用的显示方式:
In [1]: import binasciiIn [2]: '南北'.encode()
Out[2]: b'\xe5\x8d\x97\xe5\x8c\x97'In [3]: binascii.b2a_hex('南北'.encode())
Out[3]: b'e58d97e58c97'In [4]: binascii.a2b_hex(b'e58d97e58c97')
Out[4]: b'\xe5\x8d\x97\xe5\x8c\x97'In [5]: binascii.a2b_hex(b'e58d97e58c97').decode()
Out[5]: '南北'
加密算法分类
对称加密算法:
对称加密采用了对称密码编码技术,它的特点是文件加密和解密使用相同的密钥发送方和接收方需要持有同一把密钥,发送消息和接收消息均使用该密钥。相对于非对称加密,对称加密具有更高的加解密速度,但双方都需要事先知道密钥,密钥在传输过程中可能会被窃取,因此安全性没有非对称加密高。常见的对称加密算法:DES,AES,3DES等等
非对称加密算法:
文件加密需要公开密钥(publickey)和私有密钥(privatekey)。接收方在发送消息前需要事先生成公钥和私钥,然后将公钥发送给发送方。发送放收到公钥后,将待发送数据用公钥加密,发送给接收方。接收到收到数据后,用私钥解密。在这个过程中,公钥负责加密,私钥负责解密,数据在传输过程中即使被截获,攻击者由于没有私钥,因此也无法破解。非对称加密算法的加解密速度低于对称加密算法,但是安全性更高。非对称加密算法:RSA、DSA、ECC等算法
消息摘要算法:
消息摘要算法可以验证信息是否被篡改。在数据发送前,首先使用消息摘要算法生成该数据的签名,然后签名和数据一同发送给接收者。接收者收到数据后,对收到的数据采用消息摘要算法获得签名,最后比较签名是否一致,以此来判断数据在传输过程中是否发生修改。
Python加密库
PyCrypto是 Python 中密码学方面最有名的第三方软件包。可惜的是,它的开发工作于2012年就已停止。其他人还在继续发布最新版本的 PyCrypto,如果你不介意使用第三方的二进制包,仍可以取得Python 3.5 的相应版本。比如,可以在 Github 上找到了对应Python3.5的PyCrypto 二进制包。幸运的是,有一个该项目的分支 PyCrytodome 取代了 PyCrypto 。
# Linux 上安装它,你可以使用以下pip 命令:
pip3 install -i https://pypi.douban.com/simple pycryptodome
# 导入
import Crypto# 在Windows 系统上安装则稍有不同:
pip3 install -i https://pypi.douban.com/simple pycryptodomex
# 导入
import Cryptodome
DES加密
全称为Data EncryptionStandard,即数据加密标准,是一种使用密钥加密的块算法,又被称为美国数据加密标准
DES算法的入口参数有三个:Key、Data、Mode。其中Key为7个字节共56位,是DES算法的工作密钥;Data为8个字节64位,是要被加密或被解密的数据;Mode为DES的工作方式,有两种:加密或解密。
密钥长64位,密钥事实上是56位参与DES运算(第8、16、24、32、40、48、56、64位是校验位,使得每个密钥都有奇数个1),分组后的明文组和56位的密钥按位替代或交换的方法形成密文组。
from Crypto.Cipher import DES
key = b'abcdefgh' # 密钥 8位或16位,必须为bytesdef pad(text):# 加密函数,如果text不是8的倍数【加密文本text必须为8的倍数!】,那就补足为8的倍数while len(text) % 8 != 0:text += ' 'return textdes = DES.new(key, DES.MODE_ECB) # 创建一个DES实例
text = 'Python rocks!'
padded_text = pad(text) # 需要加密的数据
encrypted_text = des.encrypt(padded_text.encode('utf-8')) # 加密
print(encrypted_text)
# rstrip(' ')返回从字符串末尾删除所有字符串的字符串(默认空白字符)的副本
plain_text = des.decrypt(encrypted_text).decode().rstrip(' ') # 解密
print(plain_text)
3DES
3DES(或称为Triple DES)是三重数据加密算法(TDEA,Triple Data Encryption Algorithm)块密码的通称。它相当于是对每个数据块应用三次DES加密算法。
由于计算机运算能力的增强,原版DES密码的密钥长度变得容易被暴力破解。3DES即是设计用来提供一种相对简单的方法,即通过增加DES的密钥长度来避免类似的攻击,而不是设计一种全新的块密码算法。
3DES(即Triple DES)是DES向AES过渡的加密算法(1999年,NIST将3-DES指定为过渡的加密标准),加密算法,其具体实现如下:设Ek()和Dk()代表DES算法的加密和解密过程,K代表DES算法使用的密钥,M代表明文,C代表密文,这样:
3DES加密过程为:C=Ek3(Dk2(Ek1(M)))
3DES解密过程为:M=Dk1(EK2(Dk3(C)))
AES加密
高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院(NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一。
AES在软件及硬件上都能快速地加解密,相对来说较易于实作,且只需要很少的存储器。作为一个新的加密标准,目前正被部署应用到更广大的范围。
AES只是个基本算法,实现AES有若干模式。其中的CBC模式因为其安全性而被TLS(就是https的加密标准)和IPSec(win采用的)作为技术标准。简单地说,CBC使用密码和salt(起扰乱作用)按固定算法(md5)产生key和iv。然后用key和iv(初始向量,加密第一块明文)加密(明文)和解密(密文)。
特点
- 抵抗所有已知的攻击。
- 在多个平台上速度快,编码紧凑。
- 设计简单。
AES为分组密码,分组密码也就是把明文分成一组一组的,每组长度相等,每次加密一组数据,直到加密完整个明文。在AES标准规范中,分组长度只能是128位,也就是说,每个分组为16个字节(每个字节8位)。密钥的长度可以使用128位、192位或256位(16、24和32字节)。密钥的长度不同,推荐加密轮数也不同。
##################################### AES.MODE_ECB ####################################
from Cryptodome.Cipher import AES
from binascii import b2a_hex, a2b_hex
# 密钥key 长度必须为16(AES-128)、24(AES-192)、或32(AES-256)Bytes 长度.
# 目前AES-128足够用
key = 'abcdefghigklmnop'
# 加密内容需要长达16位字符,所以进行空格拼接
def pad(text):while len(text) % 16 != 0:text += ' 'return text# 进行加密算法,模式ECB模式,把叠加完16位的秘钥传进来
aes = AES.new(key.encode(), AES.MODE_ECB)
# 加密内容,此处需要将字符串转为字节
text = 'hello'
# 进行内容拼接16位字符后传入加密类中,结果为字节类型
encrypted_text = aes.encrypt(pad(text).encode())
encrypted_text_hex = b2a_hex(encrypted_text)
print(encrypted_text_hex)# 此处是为了验证是否能将字节转为字符串后,进行解密成功
# 实际上a 就是 encrypted_text ,也就是加密后的内容
# 用aes对象进行解密,将字节类型转为str类型,错误编码忽略不计
de = str(aes.decrypt(a2b_hex(encrypted_text_hex)), encoding='utf-8',errors="ignore")
# 获取str从0开始到文本内容的字符串长度。
print(de[:len(text)])##################################### AES.MODE_CFB ####################################
from Cryptodome.Cipher import AES
from Cryptodome import Random
from binascii import b2a_hex # 要加密的明文
data = '南来北往'
# 密钥key 长度必须为16(AES-128)、24(AES-192)、或32(AES-256)Bytes 长度.
# 目前AES-128足够用
key = b'this is a 16 key'
# 生成长度等于AES块大小的不可重复的密钥向量
iv = Random.new().read(AES.block_size)# 使用key和iv初始化AES对象, 使用MODE_CFB模式
mycipher = AES.new(key, AES.MODE_CFB, iv)
# 加密的明文长度必须为16的倍数,如果长度不为16的倍数,则需要补足为16的倍数
# 将iv(密钥向量)加到加密的密文开头,一起传输
ciphertext = iv + mycipher.encrypt(data.encode())# 解密的话要用key和iv生成新的AES对象
mydecrypt = AES.new(key, AES.MODE_CFB, ciphertext[:16])
# 使用新生成的AES对象,将加密的密文解密
decrypttext = mydecrypt.decrypt(ciphertext[16:])print('密钥k为:', key)
print('iv为:', b2a_hex(ciphertext)[:16])
print('加密后数据为:', b2a_hex(ciphertext)[16:])
print('解密后数据为:', decrypttext.decode())
面向对象方式
#coding: utf8
import sys
from Crypto.Cipher import AES
from binascii import b2a_hex, a2b_hexclass prpcrypt():def __init__(self, key):self.key = keyself.mode = AES.MODE_CBC#加密函数,如果text不是16的倍数【加密文本text必须为16的倍数!】,那就补足为16的倍数def encrypt(self, text):cryptor = AES.new(self.key, self.mode, self.key)#这里密钥key 长度必须为16(AES-128)、24(AES-192)、或32(AES-256)Bytes 长度.目前AES-128足够用length = 16count = len(text)add = length - (count % length)text = text + ('\0' * add)self.ciphertext = cryptor.encrypt(text)#因为AES加密时候得到的字符串不一定是ascii字符集的,输出到终端或者保存时候可能存在问题#所以这里统一把加密后的字符串转化为16进制字符串return b2a_hex(self.ciphertext)#解密后,去掉补足的空格用strip() 去掉def decrypt(self, text):cryptor = AES.new(self.key, self.mode, self.key)plain_text = cryptor.decrypt(a2b_hex(text))return plain_text.rstrip('\0')if __name__ == '__main__':pc = prpcrypt('keyskeyskeyskeys') #初始化密钥e = pc.encrypt("00000")d = pc.decrypt(e) print e, de = pc.encrypt("00000000000000000000000000")d = pc.decrypt(e) print e, d
运行结果
a9755fd70d8d6db65a6fac12d4797dde 00000
2c1969f213c703ebedc36f9e7e5a2b88922ac938c983201c200da51073d00b2c 00000000000000000000000000
RSA加密
RSA加密算法是一种非对称加密算法
。在公开密钥加密和电子商业中RSA被广泛使用。
该算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥,即公钥,而两个大素数组合成私钥。公钥是可发布的供任何人使用,私钥则为自己所有,供解密之用。
3个参数:rsa_n, rsa_e,message。rsa_n, rsa_e 用于生成公钥,message: 需要加密的消息
因为RSA加密算法的特性,RSA的公钥私钥都是10进制的,但公钥的值常常保存为16进制的格式,所以需要将其用int()
方法转换为10进制格式。
安装 pip install rsa
使用
import rsa
from binascii import b2a_hex, a2b_hexclass rsacrypt():def __init__(self, pubkey, prikey):self.pubkey = pubkeyself.prikey = prikeydef encrypt(self, text):self.ciphertext = rsa.encrypt(text.encode(), self.pubkey)# 因为rsa加密时候得到的字符串不一定是ascii字符集的,输出到终端或者保存时候可能存在问题# 所以这里统一把加密后的字符串转化为16进制字符串return b2a_hex(self.ciphertext)def decrypt(self, text):decrypt_text = rsa.decrypt(a2b_hex(text), prikey)return decrypt_textif __name__ == '__main__':pubkey, prikey = rsa.newkeys(256)rs_obj = rsacrypt(pubkey,prikey)text='hello'ency_text = rs_obj.encrypt(text)print(ency_text)print(rs_obj.decrypt(ency_text))"""
b'7cb319c67853067abcd16aad25b3a8658e521f83b1e6a6cf0c4c2e9303ad3e14'
b'hello'
"""
base64
Base64编码,64指A-Z、a-z、0-9、+和/这64个字符,还有“=”号不属于编码字符,而是填充字符。为什么发明这么个编码呢,这个编码的原理很简单,“破解”也很容易,原因是电子邮件刚出来的时候,只传递英文字符,这没有问题,但是后来,中国人,日本人都要发email,这样问题就来了,因为这些字符有可能会被邮件服务器或者网关当成命令处理,故必须得有一种编码来对邮件进行加密,但是加密的目的是为了能够使得一些原始的服务器不出问题(现在服务器早已经能处理这些乱七八糟得情况了,不过因为已经形成了一套规范,所以邮件还是得经过Base64编码才能传递)。
优点:方法简单。缺点:不保险,别人拿到密文可以自己解密出明文
编码原理:将3个字节转换成4个字节((3 X 8)=24=(4X6)),先读入3个字节,每读一个字节,左移8位,再右移四次,每次6位,这样就有4个字节了。解码原理:将4个字节转换成3个字节,先读入4个6位(用或运算),每次左移6位,再右移3次,每次8位,这样就还原了。
import base64
s1 = base64.encodestring('hello world')
s2 = base64.decodestring(s1)
print s1, s2
结果
aGVsbG8gd29ybGQ=
hello world