各种加密逻辑:MD5、sha256、Base64、AES、DES、RSA

在我们进行js逆向的时候. 总会遇见一些我们人类无法直接能理解的东西出现. 此时你看到的大多数是被加密过的密文.

一. 一切从MD5开始

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

MD5是一种密码摘要算法。在数学上,摘要其实计算逻辑就是hash。hash算法是非常优秀的一种摘要逻辑。无法通过密文得到明文。

 

Hash在线计算、md5计算、sha1计算、sha256计算、sha512计算:

https://1024tools.com/hash

https://www.cmd5.com

 

MD5的特点:

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

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

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

 

MD5一般应用于:

1. 密码(比如用户设置密码的时候,数据库存储密文。用户支付的时候,去数据库比对加密后的密文,前提是两个明文加密的结果是一样的)。
2. 一致性检测(比如MySQL软件官方下载)。

 

python实现md5:

from hashlib import md5

obj = md5()
obj.update("alex".encode("utf-8"))
# obj.update("wusir".encode('utf-8'))  # 可以添加多个被加密的内容

bs = obj.hexdigest()
print(bs)

我们把密文丢到网页里. 发现有些网站可以直接解密. 但其实不然. 这里并不是直接解密MD5. 而是"撞库".

就是它网站里存储了大量的MD5的值. 就像这样:

而需要进行查询的时候. 只需要一条select语句就可以查询到了. 这就是传说中的撞库.

如何避免撞库: 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算法是不可逆的, 所以不存在解密的逻辑.

 

obj = md5()
obj.update("123456".encode("utf-8"))
print(obj.hexdigest())
# 记住这个值,123456 md5加密后是 e10adc3949ba59abbe56e057f20f883e

需要你背下来的东西. 下意识的反应....

当你在网站上看到了一些加密逻辑. 发现. 计算的结果是32位字符串

该字符串的组成是 0-9a-f(十六进制) 可以猜测是md5

如果结果是e10开头. 直接标准md5. 

注意:如果计算的结果不是e10. 这会儿. 你要小心了. 有可能是魔改的md5,只能抠代码....扣起来非常痛苦。

 

二. Base64

base64其实很容易理解. 通常被加密后的内容是字节. 而我们的密文是用来传输的(不传输谁加密啊). 但是, 在http协议里想要传输字节是很麻烦的一个事儿. 相对应的. 如果传递的是字符串就好控制的多. 此时base64就应运而生了. 26个大写字母+26个小写字母+10个数字+2个特殊符号(+和/)组成了一组类似64进制的计算逻辑. 这就是base64了.

import base64

bs = "我要吃饭".encode("utf-8")
# 把字节转化成b64
print(base64.b64encode(bs).decode())  # 5oiR6KaB5ZCD6aWt

# 把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)

 

base64的末尾有可能是 = 作为填充的

import base64
s = "2i1ohdkljfkldasjflkdasjfdalsz"
bs = s.encode("utf-8")
bs_64 = base64.b64encode(bs).decode()
print(bs_64)
print(len(bs_64) % 4)

 

base64 既然可以把字节处理成字符串,有的网站就会把一些小的图片(头像, 图标) -> base64

import base64
s = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAFCAYAAACJmvbYAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAJklEQVQI12NgwAEsuv/8xy9h3vX7P6oEKp/BHCqA0yhzdB0MDAwAFXkTK5la4mAAAAAASUVORK5CYII="
# 如何保存成图片
s = s.split(",")[1]
bs = base64.b64decode(s)
with open("xxx.png", mode="wb") as f:
    f.write(bs)

 

由于标准的Base64编码后可能出现字符+和/,但是这两个字符在URL中就不能当做参数传递,所以就出现了Base64URL,下面是它们的区别:​
1. Base64编码后出现的+和/在Base64URL会分别替换为-和_
2. Base64编码中末尾出现的=符号用于补位,这个字符和queryString中的key=value键值对会发生冲突,所以在Base64URL中=符号会被省略,去掉=后怎么解码呢?因为Base64是把3个字节变为4个字节,所以,Base64编码的长度永远是4的倍数,因此,需要加上=把Base64字符串的长度变为4的倍数,就可以正常解码了。

有的网站会把base64进行特殊的处理. 把+替换成-   把/替换成_,我们的应对方案:
在处理base64的时候.如果遇到了没有+和/的情况. 可以采用下面的方案来替换掉+和/
应对方案一.  replace("-", "+").replace("_", "/")
应对方案二. 用base64 自动处理
如果转化后. 进行解密操作后. 发现数据不对. 或者根本无法解密, 调换一下顺序即可

import base64
s = "Z21kD9ZK1ke6ugku2ccWu-MeDWh3z252xRTQv-wZ6jddVo3tJLe7gIXz4PyxGl73nSfLAADyElSjjvrYdCvEP4pfohVVEX1DxoI0yhm36ytQNvu-WLU94qULZQ72aml6cYBVU_O5b4O-ND0rEWbfEEp-CNsPQEwjRV-iM008hyg4psnuxT9hhBXjpzH8uzBxqR9VBh9v2T35qVb4ipPvX70IaGcbG17rvXyras-RWT24bdytS47IgDmQVMfd1cQrTyDVZo1ByopBhc63S3QOKtcY7Aip1m9eLirsRpmecA4zC-0 Pn2FSpsOJi3Gw6g2Cy8ryt-pnNGsoO2wf_9lWV4Y7LHqAeQfAMcFERawC1PVDQzouFE2Xvyw20H4XCtJMvcdMgnQgmpo42vaU4Uj6SbNLQbWjrCEXEtcrKu9AVGU03oJeo_Stun9oboBASa5gzP7pecQ8NPIB9xciY5O_jk_QuNG-vZVRdTecK1qSEHMpuSgM5dRMN9iTCD93qAwQYE5qOMCuOfeNnWhUewho3XJfMewpsDstvjSesdXfR5rtrrydSw76xvqQotdSH7J0RAUGjj5Wv-WVxelEFbDYQpPvOWHg3wXOa1pORzELoEG6v8_WR5_5C-buAoAC4d9xa1O76dd1nj1e_NzvdicnBPNj0HSt_APXvtqDzmBLEZUwa9b5_ZhgyJldGwdWHzLwxx8xPM34M_YlW28J8msT5RBMgPv9ahAJSsKQReQD4pboP2cwxWC9yPeloFc68TOLIeSrMvXrT8aJw8S5MpAX0C3-2 yN8g0RY80n9QjzsnHIgwXERlrw66hMOHch4DIp-AvbPZl9WTncFPL_A2x-Y4EVkUtvV_JNzDJsVgoITdkekhKHkFmAyMwvKbYvW9-4 pFisyOozkh83rko35YZSqDvvIXEWZyWVpGld2SI_xESEP82gtzgrplZzUuVq_WVDegypnD5DZmZsidnHkau-j5FSYwpivm-96 QgVDIBuYTjvV9Lv9GatSYyZBCjjx0Xi8I-JuH3x12GITyiXmmn5xjbvpLQSBVly_urZpNy1SnH2XCVrZOOauAy_ZgAByrlGlhzxBuGL_fBd2r6Lv4kERgaD6tnGVlBk2inBiPbW8Sex90bE7g0T5ps6w4_jzVmcmQZ-M2cX7u1y7mlV_vF-puzU81na5nvQIaJmWQn0_9-466 JGALXEaW7CHb-BqNZTCdLYJgj6n-DbX9sZs8raeEeJsE3aK1P4YmnUgn6wQHQCYLKmDzTC79KoL-6 eCpHe2VD-P48bDfCKLOrqSeLYiWKnZyTAq42h-f9zZtd-fapb4sHO8O0IUF_6DBQ987P9-dvJhghTj8_UIpMjFqBrGDJbnhbGy5Yjn_G1uUtT1usUGwFoBuEJ96r_VPKlxXYjqrgaW3w3ElKr2pEfkKhTKDSLL_inlWGYhTogPsRX8kJubNsZ5V9FcmU2tlGFh5dlDwA-fTTQunsrLt_G40RrK0iWqSvyEjMZi7DqzdeITNK_sk1OlYMguTK6z12P4tzKgV3Ynq6Xo9ToRQE_g9oSH1OEo8mUw7AQ8dNi7FxXjpAJHbfdbKwIjAqUeOfCUJ3UVBMGGR5Um4w80nDvf-gLaxdCePHELy_ev8E-s3K5kADoaE9iFcM5Kz8N_4bfZ_trx6-LufqiDUcT4rNKa42o3pEOUeS9m61MLyRLp2O1YwxypYPNUbl8yK8Q_gTBrTQK3bfo3hs0vDYTpIL1CBLjMNTfip6nv_mE7mZzhOweBpGyoYl1k3kOPSc_0fviR0jdaeYPsaHx-R1V1erV0okwp8kexYr_5FqyFXEnJxGATUoCY5Tc6OfgOh7grXUnVhf2aTG6Uc1ODQk2_h0DkxlFcwUXJf45N3LadYQDs6nN10tiyh6yTL5aZADP1JGgAFAWpXW4GFzIWf4DmjQ3VQKhulLeUEFHOuJgUmzWQtrV831E4EYBVhWAH5050MrhsPbcd1tvYDnvdEOmSi-pUmu7MWoxK95rzfo71bgUHdEbFN3IDSVbYtUYYwegifnjFS17dcyP2q6HTIygor00eEarvj32vWH7TENiqonqGLOuWYcLeRtjRtblY6MAlDvSSxEDeg4cO5PD-MtsgIMvUnGxYUafUp8uAKR1E4LKwA9VLYM5Bl5RMVDLtYyqsUn5zBOZU"
bs = base64.b64decode(s, b"-_")
print(bs)

 

三. 对称加密

所谓对称加密就是加密和解密用的是同一个秘钥. 就好比. 我要给你邮寄一个箱子. 上面怼上锁. 提前我把钥匙给了你一把, 我一把. 那么我在邮寄之前就可以把箱子锁上. 然后快递到你那里. 你用相同的钥匙就可以打开这个箱子.

条件: 加密和解密用的是同一个秘钥. 那么两边就必须同时拥有钥匙才可以.

常见的对称加密: AES, DES, 3DES. 我们这里讨论AES和DES

3.1 AES

安装
pip install pycrypto    =>  80%装不上,用pycryptodome 做平替... 它里面包含了PyCrypto
pip install pycryptodome   => 可以装..

安装之后依然不好用.. 发现只有crypto   而Crypto用不了. 去模块的安装目录. 改一下文件名, 把小写的c改成大写的C

找到 site-packages 文件夹, pip install requests  装两次看提示会告诉你 site-packages 在哪里

到 site-packages 文件夹里找. crypto文件夹. 把它的名字改成Crypto,然后就可以用了.

 

如果报错vc++14.0   

解决方案: 

1. 升级pip
2. 降低你的python版本..(3.10)
3. 去找whl文件  https://www.lfd.uci.edu/~gohlke/pythonlibs/

 

# 导入AES, 请注意, 大小写问题.
import binascii

from Crypto.Cipher import AES, DES, DES3
from Crypto.Util.Padding import pad, unpad  # 填充
import base64

# 如何进行ase加密
"""
AES.new 三个参数:
<1>.key:  可以有三种长度,分别是: *AES-128*: 16 ;  *AES-192*: 24 ; *AES-256*: 32
百分之90以上网站AES-128,所以通常是16位的字节。
那怎么确定是用16位、24位还是32位呢,一般就直接给16位,如果不行,再换其他的。
如果在逆向过程中看到的是只给我们20个字节,要考虑把20个字节怎么变成16个字节,所以要砍掉一些,只留前面16个。
如果在网页上看到十六进制字符串,如果是32位的,那么基本上用的都是16位的字节。转换关系:每两个字符串对于一个字节,使用: binascii.b2a_hex() 和 binascii.a2b_hex() 。
<2>.mode: CBC(需要iv), ECB(不需要iv)
<3>.iv: CBC模式下是16位的字节
"""
# 创建一个aes加密器
aes = AES.new(key=b'1234567890123456', IV=b'1234567890123456', mode=AES.MODE_CBC)

ming = "威海龙王和东海龙王谁更猛!!!!!"  #这是明文,需要对明文加密
ming = ming.encode("utf-8")   # 第一件事必须处理成字节。注意:加密或者解密, 处理的都是字节。

# 如果不给填充16个字节,就会报错:Data must be padded to 16 byte boundary in CBC mode 
ming = pad(ming, 16)
mi = aes.encrypt(ming)   
print(mi)
# 输出:b'\x97\x99\xf5\x8a\xc4\xe6\xce\xe1*\r\xfc\x8e\xbb\xe4\xc9C\xc5\x9bN\x9fYZF\xcf\xf3\xd5*\x18\xd7\x04\xeb\xa38\xe7\xec\xfb\xe2\xb0\xe0Cf\xd4P\xe3\xef\x87o\x80'
# 能不能用utf8或者binascii处理这些加密的内容? print(mi.decode("utf-8"))  加密之后的内容是杂乱无章的字节,没有任何规律的字节,没那么容易处理这些加密内容。
# 所以utf8是不能处理这些内容的,还是得用 base64 来处理。  
s = base64.b64encode(mi).decode()
print(s)
# 输出结果: l5n1isTmzuEqDfyOu+TJQ8WbTp9ZWkbP89UqGNcE66M45+z74rDgQ2bUUOPvh2+A

# 解密
mi = 'l5n1isTmzuEqDfyOu+TJQ8WbTp9ZWkbP89UqGNcE66M45+z74rDgQ2bUUOPvh2+A'
# 解密逻辑 (对称加密里)加密和解密逻辑几乎一样..
aes = AES.new(key=b'1234567890123456', IV=b'1234567890123456', mode=AES.MODE_CBC)
mi = base64.b64decode(mi)
ming = aes.decrypt(mi)
ming = unpad(ming, 16)  # 干掉填充即可
print(ming.decode("utf-8"))

 

3.2 DES

from Crypto.Cipher import DES
from Crypto.Util.Padding import pad, unpad  # 填充
import base64

# 加密
# 需要加密的数据必须是16的倍数
# 填充规则: 缺少数据量的个数 * chr(缺少数据量个数)
s = "我爱吃鱼"
ming = s.encode("utf-8")
# key: 8个字节
des = DES.new(key=b'12345678', iv=b'12345678', mode=DES.MODE_CBC)
ming = pad(ming, 8)  # 填充到8位
mi = des.encrypt(ming)
mi_s = base64.b64encode(mi).decode()
print(mi_s)

# 解密
mi_s = "bXZ8WcOzh/SW0yuFPTTZog=="
des = DES.new(key=b'12345678', iv=b'12345678', mode=DES.MODE_CBC)
ming = des.decrypt(base64.b64decode(mi_s))
ming = unpad(ming, 8)
print(ming.decode("utf-8"))

 

四.非对称加密

非对称加密: 加密和解密的时候用的是不同的秘钥.

一组秘钥:
1.公钥, 公开的秘钥,  对数据进行加密
2.私钥, 私密的秘钥, 对数据进行解密

非对称加密的逻辑:
1.先在服务器端. 生成一组秘钥, 公钥, 私钥
2.把公钥放出去.
3.客户端在拿到公钥之后. 可以使用公钥对数据进行加密.
4.把数据传输给服务器
5.服务器需要对数据进行解密. 私钥能解密

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

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

4.1 RSA加密解密

公钥和私钥是一对. 不能乱拆....这俩玩意是一起的...

在python中主要有两个库支持rsa加密. 只需要掌握一个就可以.
注意了..在前端js那边. rsa加密逻辑有两个.完全不同的两个库
1. No Padding    ->  不推荐大家直接用python去搞. 推荐直接用js库干...
2. PKCS padding  ->  python中只能搞这种..

rsa的特征:
1. 65537
2. 010001 十六进制. 转化成十进制 65537  70% 是 No Padding  建议选择用js来干...
3. JSEncrypt  js的一个第三方加密库. 专门用来做rsa加密... -> PKCS padding
4. PublicKey  一定是rsa加密. 如果传进去的是一个base64字样的东西 -> 99% 是PKCS padding

4.1.1 创建公钥和私钥

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
from Crypto import Random
import base64

# 随机
gen_random = Random.new


# 生成秘钥
rsakey = RSA.generate(1024)
with open("rsa.public.pem", mode="wb") as f:
    f.write(rsakey.publickey().exportKey())

with open("rsa.private.pem", mode="wb") as f:
    f.write(rsakey.exportKey())

 

4.1.2 加密


# 加密
data = "我要吃饭了"
with open("rsa.public.pem", mode="r") as f:
    pk = f.read()
    rsa_pk = RSA.importKey(pk)
    rsa = PKCS1_v1_5.new(rsa_pk)

    result = rsa.encrypt(data.encode("utf-8"))
    # 处理成b64方便传输
    b64_result = base64.b64encode(result).decode("utf-8")
    print(b64_result)

 

4.1.3 解密


data = "e/spTGg3roda+iqLK4e2bckNMSgXSNosOVLtWN+ArgaIDgYONPIU9i0rIeTj0ywwXnTIPU734EIoKRFQsLmPpJK4Htte+QlcgRFbuj/hCW1uWiB3mCbyU3ZHKo/Y9UjYMuMfk+H6m8OWHtr+tWjiinMNURQpxbsTiT/1cfifWo4="
# 解密
with open("rsa.private.pem", mode="r") as f:
    prikey = f.read()
    rsa_pk = RSA.importKey(prikey)
    rsa = PKCS1_v1_5.new(rsa_pk)
    result = rsa.decrypt(base64.b64decode(data), gen_random)
    print(result.decode("utf-8"))

 

 

posted @ 2022-10-09 19:50  屠魔的少年  阅读(6)  评论(0)    收藏  举报