python各种加密逻辑
-
这玩意不可逆. 所以. 摘要算法就不是一个加密逻辑.
-
相同的内容计算出来的摘要应该是一样的
-
from hashlib import md5 obj = md5() obj.update("alex".encode("utf-8")) # obj.update("wusir".encode('utf-8')) # 可以添加多个被加密的内容 bs = obj.hexdigest() print(bs)
如何避免撞库: md5在进行计算的时候可以加盐. 加盐之后. 就很难撞库了.
from hashlib import md5 salt = "我是盐.把我加进去就没人能破解了" obj = md5(salt.encode("utf-8")) # 加盐 obj.update("alex".encode("utf-8")) bs = obj.hexdigest() print(bs)
from hashlib import sha1, sha256 sha = sha256(b'salt') sha.update(b'alex') print(sha.hexdigest())
在我们访问一个url的时候总能看到这样的一种url
https://www.sogou.com/web?query=%E5%90%83%E9%A5%AD%E7%9D%A1%E8%A7%89%E6%89%93%E8%B1%86%E8%B1%86&_asf=www.sogou.com&_ast=&w=01019900&p=40040100&ie=utf8&from=index-nologin&s_from=index&sut=3119&sst0=1630994614300&lkt=0%2C0%2C0&sugsuv=1606978591882752&sugtime=1630994614300
此时会发现, 在浏览器上明明是能看到中文的. 但是一旦复制出来. 或者在抓包工具里看到的. 都是这种%. 那么这个%是什么鬼? 也是加密么?
非也, 其实我们在访问一个url的时候. 浏览器会自动的进行urlencode操作. 会对我们请求的url进行编码. 这种编码规则被称为百分号编码. 是专门为url(统一资源定位符)准备的一套编码规则.
一个url的完整组成:
scheme://host:port/dir/file?p1=v1&p2=v2#anchor http ://www.baidu.com/tieba/index.html?name=alex&age=18 参数: key=value 服务器可以通过key拿value
此时. 如果参数中出现一些特殊符号. 比如'=' 我想给服务器传递a=b=c这样的参数. 必然会让整个URL产生歧义.
所以, 把url中的参数部分转化成字节. 每字节的再转化成2个16进制的数字. 前面补%.
看着很复杂. 在python里. 直接一步到位
from urllib.parse import urlencode, unquote, quote # 单独编码字符串 wq = "米饭怎么吃" print(quote(wq)) # %E7%B1%B3%E9%A5%AD%E6%80%8E%E4%B9%88%E5%90%83 print(quote(wq, encoding="gbk")) # %C3%D7%B7%B9%D4%F5%C3%B4%B3%D4 # 多个数据统一进行编码 dic = { "wq": "米饭怎么吃", "new_wq": "想怎么吃就怎么吃" } print(urlencode(dic)) # wq=%E7%B1%B3%E9%A5%AD%E6%80%8E%E4%B9%88%E5%90%83&new_wq=%E6%83%B3%E6%80%8E%E4%B9%88%E5%90%83%E5%B0%B1%E6%80%8E%E4%B9%88%E5%90%83 print(urlencode(dic, encoding="utf-8")) # 也可以指定字符集 # 一个完整的url编码过程 base_url = "http://www.baidu.com/s?" params = { "wd": "大王" } url = base_url + urlencode(params) print(url) # http://www.baidu.com/s?wd=%E5%A4%A7%E7%8E%8B 解码 s = "http://www.baidu.com/s?wd=%E5%A4%A7%E7%8E%8B" print(unquote(s)) # http://www.baidu.com/s?wd=大王
base64其实很容易理解. 通常被加密后的内容是字节. 而我们的密文是用来传输的(不传输谁加密啊). 但是, 在http协议里想要传输字节是很麻烦的一个事儿. 相对应的. 如果传递的是字符串就好控制的多. 此时base64就应运而生了. 26个大写字母+26个小写字母+10个数字+2个特殊符号(+和/)组成了一组类似64进制的计算逻辑. 这就是base64了.
import base64
"""
字节 => base64.b64encode().decode() => 字符串
字符串 => base64.b64decode() => 字节
"""
bs = "我要吃饭".encode("utf-8") # 把字节转化成b64 print(base64.b64encode(bs).decode()) # 把b64字符串转化成字节 s = "5oiR6KaB5ZCD6aWt" print(base64.b64decode(s).decode("utf-8")) 注意, b64处理后的字符串长度. 一定是4的倍数. 如果在网页上看到有些密文的b64长度不是4的倍数. 会报错 例如, import base64 s = "ztKwrsTj0b0" bb = base64.b64decode(s) print(bb) 此时运行出现以下问题 Traceback (most recent call last): File "D:/PycharmProjects/rrrr.py", line 33, in <module> bb = base64.b64decode(s) File "D:\Python38\lib\base64.py", line 87, in b64decode return binascii.a2b_base64(s) binascii.Error: Incorrect padding 解决思路. base64长度要求. 字符串长度必须是4的倍数. 填充一下即可 import base64 s = "ztKwrsTj0b0" s += ("=" * (4 - len(s) % 4)) print("填充后", s) bb = base64.b64decode(s).decode("gbk") print(bb)
import binascii # # 把字节转化成十六进制的数字 # # b: 字节 # # 2: to # # a: ascii # # hex十六进制 # r = binascii.b2a_hex(s).decode() # print(r) # 把十六进制的数字转化成字节 # s = "e6a8b5e5a4ab" # bs = binascii.a2b_hex(s) # print(bs.decode("utf-8"))
请注意.
由于标准的Base64编码后可能出现字符+和/,但是这两个字符在URL中就不能当做参数传递,所以就出现了Base64URL,下面是它们的区别:
1. Base64编码后出现的+和/在Base64URL会分别替换为-和_
2. Base64编码中末尾出现的=符号用于补位,这个字符和queryString中的key=value键值对会发生冲突,所以在Base64URL中=符号会被省略,去掉=后怎么解码呢?因为Base64是把3个字节变为4个字节,所以,Base64编码的长度永远是4的倍数,因此,需要加上=把Base64字符串的长度变为4的倍数,就可以正常解码了。
我们的应对方案:
在处理base64的时候.如果遇到了没有+和/的情况. 可以采用下面的方案来替换掉+和/
b64 = base64.b64decode(mi, b"-_")
所谓对称加密就是加密和解密用的是同一个秘钥. 就好比. 我要给你邮寄一个箱子. 上面怼上锁. 提前我把钥匙给了你一把, 我一把. 那么我在邮寄之前就可以把箱子锁上. 然后快递到你那里. 你用相同的钥匙就可以打开这个箱子.
条件: 加密和解密用的是同一个秘钥. 那么两边就必须同时拥有钥匙才可以.
常见的对称加密: AES, DES, 3DES. 我们这里讨论AES和DES.
测试网站:
pip install pycrypto => 很多人装不上....
pip install pycryptodome => 可以装..
安装之后依然不好用.. 发现只有crypto 而Crypto用不了.
去模块的安装目录. 改一下文件名, 把小写的c改成大写的C
如果报错vc++14.0
解决方案:
-
升级pip
-
降低你的python版本..(3.10)
""" 长度 16: *AES-128* 24: *AES-192* 32: *AES-256* MODE 加密模式. 常见的ECB, CBC 以下内容来自互联网~~ ECB:是一种基础的加密方式,密文被分割成分组长度相等的块(不足补齐),然后单独一个个加密,一个个输出组成密文。 CBC:是一种循环模式,前一个分组的密文和当前分组的明文异或或操作后再加密,这样做的目的是增强破解难度。 CFB/OFB:实际上是一种反馈模式,目的也是增强破解的难度。 FCB和CBC的加密结果是不一样的,两者的模式不同,而且CBC会在第一个密码块运算时加入一个初始化向量。 """
数据准备
from Crypto.Cipher import AES, DES, DES3 from Crypto.Util.Padding import pad, unpad # 填充 import base64 mi = "变色的生活~" # 1. 创建加密器 # key, 秘钥, 长度必须是16(最常用), 24, 32位的字节. # mode, 加密模式, # ECB, 可以没有iv # CBC, 需要iv 的 # iv, 必须在CBC模式下, 长度是16位字节 aes = AES.new(key=b'1234561234564561',mode=AES.MODE_CBC, iv=b'uuuuuuuuuuuuuuuu')
加密过程
mi = "变色的生活~" # #创建加密器 aes = AES.new(key=b'aaaaaaaaaaajjjjj', mode=AES.MODE_CBC, iv=b'uuuuuuuuuuuuuuuu') # 加密 #加密之前需要对数据进行填充,使用pad模块 data = pad(mi.encode("utf-8"), 16) result = aes.encrypt(data) new_data = base64.b64encode(result).decode() # 把字节进行base64编码 print(new_data) # 结果:XlbF7Xj1CSQm64ML9y5JEnTCZxTqUBuHfFnFlv2O32s=
解密过程
#解密 s = "XlbF7Xj1CSQm64ML9y5JEnTCZxTqUBuHfFnFlv2O32s=" #创建解密器 aes = AES.new(key=b'aaaaaaaaaaajjjjj', mode=AES.MODE_CBC, iv=b'uuuuuuuuuuuuuuuu') # #把密文处理成字节 data_jie = base64.b64decode(s) # 解密 r = aes.decrypt(data_jie) # 去掉填充 new_result = unpad(r,16) print(new_result.decode('utf-8'))
非对称加密. 加密和解密的秘钥不是同一个秘钥. 这里需要两把钥匙. 一个公钥, 一个私钥. 公钥发送给客户端. 发送端用公钥对数据进行加密. 再发送给接收端, 接收端使用私钥来对数据解密. 由于私钥只存放在接受端这边. 所以即使数据被截获了. 也是无法进行解密的.
from Crypto.Cipher import PKCS1_v1_5, AES, DES # 加密器 from Crypto.PublicKey import RSA import base64 # 1. 生成秘钥 rsa_key = RSA.generate(bits=2048) # # 秘钥的本质是字节. key = rsa_key.export_key(format="PEM") with open("rsa_private_key.pem", mode="wb") as f: f.write(key)
# 用密钥生成公钥 pub_key = rsa_key.publickey().export_key() with open("rsa_public_key.pem", mode="wb") as f: f.write(pub_key)
使用RAS进行加密
from Crypto.Cipher import PKCS1_v1_5, AES, DES # 加密器 from Crypto.PublicKey import RSA import base64 # 使用RAS进行加密 ming = "任性的挑拨~疯狂的冒出了头" # 导入的key可以是pem格式的秘钥, 或者是字节形式的秘钥都可以 f = open("rsa_public_key.pem", mode="r", encoding="utf-8") pub_key = f.read() f.close() # 导入key rsakey = RSA.importKey(pub_key) # 创建加密器 rsa = PKCS1_v1_5.new(key=rsakey) # 加密 mi_bs = rsa.encrypt(ming.encode("utf-8")) mi = base64.b64encode(mi_bs).decode() print(mi) # rsa加密的结果可能会不一样.. 解密的过程如果没问题. 得到的结果是相同的. # 解密的过程 f = open("rsa_private_key.pem", mode="r", encoding="utf-8") pri_key = f.read() f.close() rsakey = RSA.importKey(pri_key) rsa = PKCS1_v1_5.new(key=rsakey) # mi = "Qa7JIaZDZ7iAEzl8XHds+z6rWhd8yvrrG50BUA8amuSZBxVVpZjz8zvMl2w1unJSp8Y2Y4etZ9lKBzhoRKpwJlp0w9Aa99n7c4E7GSeLzW7VTGwyv68FQqjl6Q/Hpdg9i52h0Q77B7SNtNewXKSI0TjOtCuLIBBfjuIKs2r3vpvtDCLTiYKNEAxBr2KNTxdaq1Vt2fpy74KCTWlPRa2LOz100O9SmYC3FMupPlHFx18SkDmIyJ8a/67j0JcJNNrMqgAzNhlamlte8YK98LVELQ6n7rmkUi5Mww6tJKfmnfBCubRXWIXLaAXoHI6fpvq259ADkn6dIaw9x886z9MZ9w==" # 解密, 第一个参数是解密内容, 第二个参数是 如果计算过程中出错了. 返回什么东西 data = rsa.decrypt(base64.b64decode(mi),None) print(data.decode('utf-8'))