常见加密方式的Python实现
1. 前言
我们所说的加密方式,都是对二进制编码的格式进行加密的,对应到Python中,则是我们的Bytes
。
所以当我们在Python中进行加密操作的时候,要确保我们操作的是Bytes
,否则就会报错。
将字符串和Bytes
互相转换可以使用encode()
和decode()
方法。如下所示:
# 方法中不传参数则是以默认的utf-8编码进行转换
enc = 'PyJun'.encode()
print(enc)
print(enc.decode())
# b'PyJun'
# PyJun
注:两位十六进制常常用来显示一个二进制字节。
利用binascii
模块可以将十六进制显示的字节转换成我们在加解密中更常用的显示方式:
import binascii
info = binascii.b2a_hex(b'PyJun')
print(info)
print(binascii.a2b_hex(info))
# b'50794a756e'
# b'PyJun'
2. URL编码
正常的URL中是只能包含ASCII字符的,也就是字符、数字和一些符号。而URL编码就是一种浏览器用来避免url中出现特殊字符(如汉字)的编码方式。
其实就是将超出ASCII范围的字符转换成带%
的十六进制格式。
例子:
from urllib import parse
info = parse.quote("PyJun到此一游")
print(info)
print(parse.unquote(info))
# PyJun%E5%88%B0%E6%AD%A4%E4%B8%80%E6%B8%B8
# PyJun到此一游
3. Base64编码
Base64是一种用64个字符来表示任意二进制数据的方法。
Base64编码可以称为密码学的基石。可以将任意的二进制数据进行Base64编码。所有的数据都能被编码为并只用65个字符就能表示的文本文件。( 65字符:A~Z a~z 0~9 + / = )编码后的数据~=编码前数据的4/3,会大1/3左右。
3.1. 原理
- 将所有字符转化为ASCII码。
- 将ASCII码转化为8位二进制 。
- 将二进制3个归成一组(不足3个在后边补0)共24位,再拆分成4组,每组6位。
- 统一在6位二进制前补两个0凑足8位。
- 将补0后的二进制转为十进制。
- 从Base64编码表获取十进制对应的Base64编码。
3.2. 说明
- 转换的时候,将三个byte的数据,先后放入一个24bit的缓冲区中,先来的byte占高位。
- 数据不足3byte的话,于缓冲区中剩下的bit用0补足。然后,每次取出6个bit,按照其值选择查表选择对应的字符作为编码后的输出。
- 不断进行,直到全部输入数据转换完成。
- 如果最后剩下两个输入数据,在编码结果后加1个“=”。
- 如果最后剩下一个输入数据,编码结果后加2个“=”。
- 如果没有剩下任何数据,就什么都不要加,这样才可以保证资料还原的正确性。
3.3. python使用
Python内置的base64
模块可以直接进行base64的编解码
注意:用于base64编码的,要么是ASCII包含的字符,要么是二进制数据
In [1]: import base64
In [2]: base64.b64encode(b'hello world')
Out[2]: b'aGVsbG8gd29ybGQ='
In [3]: base64.b64decode(b'aGVsbG8gd29ybGQ=')
Out[3]: b'hello world'
4. MD5(信息-摘要算法)
message-digest algorithm 5(信息-摘要算法)。经常说的“MD5加密”,就是信息摘要算法。
md5,其实就是一种算法。可以将一个字符串,或文件,或压缩包,执行md5后,就可以生成一个固定长度为128bit的串。这个串,基本上是唯一的。
4.1. 特点
- 压缩性:任意长度的数据,算出的MD5值长度都是固定的。
- 容易计算:从原数据计算出MD5值很容易。
- 抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。
- 强抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。
- 不可逆性:每个人都有不同的指纹,看到这个人,可以得出他的指纹等信息,并且唯一对应,但你只看一个指纹,是不可能看到或读到这个人的长相或身份等信息。
4.2. python使用
由于MD5模块在python3中被移除,在python3中使用hashlib
模块进行md5操作
import hashlib
# 待加密信息
str = '这是一个测试'
# 创建md5对象
hl = hashlib.md5()
# 此处必须声明encode
# 若写法为hl.update(str) 报错为: Unicode-objects must be encoded before hashing
hl.update(str.encode(encoding='utf-8'))
print('MD5加密前为 :' + str)
print('MD5加密后为 :' + hl.hexdigest())
运行结果
MD5加密前为 :这是一个测试
MD5加密后为 :cfca700b9e09cf664f3ae80733274d9f
md5的长度,默认为128bit,也就是128个0和1的二进制串。这样表达是很不友好的。所以将二进制转成了16进制,每4个bit表示一个16进制,所以128/4 = 32 换成16进制表示后,为32位了。
为什么网上还有md5是16位的呢?
其实16位的长度,是从32位md5值来的。是将32位md5去掉前八位,去掉后八位得到的。
5. AES
高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院(NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一。
AES在软件及硬件上都能快速地加解密,相对来说较易于实作,且只需要很少的存储器。作为一个新的加密标准,目前正被部署应用到更广大的范围。
5.1. 特点
- 抵抗所有已知的攻击。
- 在多个平台上速度快,编码紧凑。
- 设计简单。
AES为分组密码,分组密码也就是把明文分成一组一组的,每组长度相等,每次加密一组数据,直到加密完整个明文。在AES标准规范中,分组长度只能是128位,也就是说,每个分组为16个字节(每个字节8位)。密钥的长度可以使用128位、192位或256位。密钥的长度不同,推荐加密轮数也不同。
一般常用的是128位
5.2. Python实现
PyCrypto是 Python 中密码学方面最有名的第三方软件包,提供了许多加密算法的使用。可惜的是,它的开发工作于2012年就已停止。
幸运的是,有一个该项目的分支PyCrytodome 取代了 PyCrypto 。
在Linux上安装,可以使用以下 pip 命令:
pip install pycryptodome
导入:
import Crypto
在Windows 系统上安装则稍有不同:
pip install pycryptodomex
导入:
import Cryptodome
代码示例:
from Cryptodome.Cipher import AES
from Cryptodome import Random
from binascii import b2a_hex
# 要加密的明文
data = "I'm PyJun"
# 密钥key 长度必须为16(AES-128)、24(AES-192)、或32(AES-256)Bytes 长度.
# 目前AES-128足够用
key = b'PyJun'
key = key[:32] + (32-len(key[:32]))*b' '
# 生成长度等于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())
运行结果:
密钥k为: b'PyJun '
iv为: b'c4b5667e86a7cd00'
加密后数据为: b'372ea7889776b6c649d11de4c25de9eefe'
解密后数据为: I'm PyJun
6. RSA
RSA加密算法是一种非对称加密算法
。在公开密钥加密和电子商业中RSA被广泛使用。
该算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥,即公钥,而两个大素数组合成私钥。公钥是可发布的供任何人使用,私钥则为自己所有,供解密之用。
6.1. 非对称加密
典型的如RSA等,常见方法,使用openssl ,keytools等工具生成一对公私钥对,使用被公钥加密的数据可以使用私钥来解密,反之亦然(被私钥加密的数据也可以被公钥解密) 。
在实际使用中私钥一般保存在发布者手中,是私有的不对外公开的,只将公钥对外公布,就能实现只有私钥的持有者才能将数据解密的方法。 这种加密方式安全系数很高,因为它不用将解密的密钥进行传递,从而没有密钥在传递过程中被截获的风险,而破解密文几乎又是不可能的。
但是算法的效率低,所以常用于很重要数据的加密,常和对称配合使用,使用非对称加密的密钥去加密对称加密的密钥。
6.2. Python实现
首先我们需要安装一个rsa
模块:
pip install rsa
代码示例
import rsa
publickey,privatekey = rsa.newkeys(1024) # 加密得到公钥和私钥
pub = publickey.save_pkcs1()#获取公钥
#将公钥保存到文件*************
filepub = open("public.pem",'wb')
filepub.write(pub)
filepub.close()
pri = privatekey.save_pkcs1()#获取私钥
#将私钥保存到文件***********
filepri = open('private.pem','wb')
filepri.write(pri)
filepri.close()
content = b"PyJun" #待加密的字符串
#取出公钥
with open('public.pem','rb') as file_pub:
f_pub = file_pub.read()
pubkey = rsa.PublicKey.load_pkcs1(f_pub)
#取出私钥
with open('private.pem','rb') as file_pri:
f_pri =file_pri.read()
prikey = rsa.PrivateKey.load_pkcs1(f_pri)
#加密字符串string
crypt = rsa.encrypt(content, pubkey)#使用公钥去加密字符串
#解密
de_crypt = rsa.decrypt(crypt,prikey)#用私钥去解密
print(crypt)
print(de_crypt)
#解出来的de_crypt与content应该是相等的,判断一下
assert content == de_crypt
运行结果:
b'u\x1fy\\/b\x87O\x9c\x86\x13\x16\xabti\xa4\xf7\xba\xd2\xe0bD\x02\xb9\x7fi\xd5\xeb\xc6\x9a\xc8f\x8c\xd5NoA\x17P\x97\xf8\xfe\xb0%\xd2*\x8fK\x03\x9c\xd0\xb9\x9a\x93@\xbec\xd7\xd5;M\x92\x1bq\xb7`l\x8b\xf9\xb9\xa9\x88\x01\x96\x98\xc1\x17\xcb\x12\xce\xd2\xe7^M\xa5\xbfAq\x94\x0e)\xaf\xc0\xdb\xc9\x7f\ngT\x16\xd5YnND\xd85\xf0}\xc3\xd3\x12ovYp\xfdQ\\`<\xb4Q\xa0|a\xcd\xb9'
b'PyJun'
来源:CSDN
作者:Py-Jun
链接:https://blog.csdn.net/qq_16166591/article/details/96646265