python各种加密逻辑

一.MD5加密 

MD5是一个非常常见的摘要(hash)算法.. 其特点就是小巧. 速度快. 极难被破解(王小云女士). 所以, md5依然是国内非常多的互联网公司选择的密码摘要算法.

  1. 这玩意不可逆. 所以. 摘要算法就不是一个加密逻辑.

  2. 相同的内容计算出来的摘要应该是一样的

  3. 不同的内容(哪怕是一丢丢丢丢丢不一样) 计算出来的结果差别非常大

 

md5的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)

扩展; sha256

from hashlib import sha1, sha256
sha = sha256(b'salt')
sha.update(b'alex')
print(sha.hexdigest())

不论是sha1, sha256, md5都属于摘要算法. 都是在计算hash值. 只是散列的程度不同而已. 这种算法有一个特性. 他们是散列. 不是加密. 而且, 由于hash算法是不可逆的, 所以不存在解密的逻辑.

 

二. URLEncode和Base64

在我们访问一个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.

测试网站: https://the-x.cn/cryptography/Aes.aspx

3.1 AES

pip install pycrypto  => 很多人装不上....
pip install pycryptodome => 可以装..
安装之后依然不好用.. 发现只有crypto   而Crypto用不了.
去模块的安装目录. 改一下文件名, 把小写的c改成大写的C

如果报错vc++14.0

解决方案:

  1. 升级pip

  2. 降低你的python版本..(3.10)

  3. 去找whl文件 https://www.lfd.uci.edu/~gohlke/pythonlibs/

"""
长度
    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'))

 

四.非对称加密

非对称加密. 加密和解密的秘钥不是同一个秘钥. 这里需要两把钥匙. 一个公钥, 一个私钥. 公钥发送给客户端. 发送端用公钥对数据进行加密. 再发送给接收端, 接收端使用私钥来对数据解密. 由于私钥只存放在接受端这边. 所以即使数据被截获了. 也是无法进行解密的.

常见的非对称加密算法: RSA, DSA等等, 我们就介绍一个. RSA加密, 也是最常见的一种加密方案

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'))

 

 

posted @ 2023-06-24 16:10  小青年て  阅读(11)  评论(0编辑  收藏  举报