标准编码与算法

1 编码

1.1 url 编码

概述:对 url 做加解密主要是为了防止传入的参数有特殊值导致请求不到对应的网站;

URL 的值编码案例

python 复制代码
import urllib.parse
url_value = 'abc& price-'
quete_value = urllib.parse.quote(url_value)
print(f'{url_value}加密后:{quete_value}')      # abc& price-加密后:abc%26%20price-
unquote_value = urllib.parse.unquote(quete_value)
print(f'{quete_value}解密后的值:{unquote_value}')    # abc%26%20price-解密后的值:abc& price-

对字典做整体url编码

python 复制代码
import urllib.parse
data = {
    'name': 'p*&234 9',
    'pwd': '13249eiW'
}
# 对整个字典做加密
encode_data = urllib.parse.urlencode(data)
print(f'{data}整体加密后的结果为{encode_data}')  # {'name': 'p*&234 9', 'pwd': '13249eiW'}整体加密后的结果为name=p%2A%26234+9&pwd=13249eiW
# 对整个字典做解密
decode_data = urllib.parse.parse_qs(encode_data)
print(f'{encode_data}解密后的数据如下:{decode_data}')   # name=p%2A%26234+9&pwd=13249eiW解密后的数据如下:{'name': ['p*&234 9'], 'pwd': ['13249eiW']}

1.2 base64编码

1.2.1 python 实现 Base64

概述:base64由 26个大写字母 A~Z, 26个小写字母 a~z,10个数字 0~9,符号 '+','/' 共64个字符组成。base64 的基本思路是将原始数据的三个字符拆分转化为四个字节(固定对三个字符进行编码,首先将三个字符转换为 Ascll 编码,然后将 Ascll 编码转换为二进制,从前到后每六位取一次得到对应的十进制索引),然后根据 Base64 的对应表得到对应的编码数据。

注意:当原始数据凑不够三个字符时,一般直接补0,编码结果中会额外使用 '=' 来替代

编码 & 解码实例

python 复制代码
import base64
s = 'yimuguoqiu'.encode()   # 先转换成二进制
res = base64.b64encode(s).decode()  # 再通过 base64 对二进制进行编码, decode是将字节转字符
print(res)  # eWltdWd1b3FpdQ==

pre = base64.b64decode(res).decode()
print(f'{res} 通过base64还原后的结果为 {pre}') # eWltdWd1b3FpdQ== 通过base64还原后的结果为 yimuguoqiu

总结:base64 编码时传入字节 (二进制数据), 解码时传入字符数据。

特殊场景实例

尝试解码网站处理过的标准Base64字符 'eW91IGFyZSBnb29kISE'

python 复制代码
import base64
s = 'eW91IGFyZSBnb29kISE'   # 通过位数发现并不是 4 的倍数,严重怀疑是将末尾的 = 省略了
# data = base64.b64decode(s)  # 直接解码肯定是报错的,因为不是4的倍数
s += (4-len(s)%4)*'='    # 尝试还原原本的Base64字符,补充被删除掉的 = 
data = base64.b64decode(s).decode()
print(f'{s} 解码后的结果为:{data}') # eW91IGFyZSBnb29kISE= 解码后的结果为:you are good!!

知识点补充:在概述里已经提到过 base64 的字符组成结构了,但是部分网站的 base64 编码中可能会出现 '-' 和 '' ,这其实是变种 base64 ,是为了防止数据库误操作做的适配,所以当遇到这种情况时只需要将 '-' 替换成 '+', 将 '' 替换成 '/' 即可实现解码。


1.2.2 JS 实现 Base64

概述:首先要明确一点,js既可以在浏览器环境运行,又可以在解释器 (node.js) 中运行,这是两个独立的环境,但他们的共同点是都可以运行 js 的基础语法,对于 base64 在这两个环境中的用法差不多。

JS 实现 Base64 编码&解码演示

javascript 复制代码
// 浏览器环境演示
btoa('hello')   // 编码
'aGVsbG8='

atob('aGVsbG8=')   // 解码
'hello'

// node.js 演示
s = 'hello'
encode_str = btoa(s)
console.log(s,'编码后:',encode_str)   // hello 编码后: aGVsbG8=
decode_str = atob(encode_str)
console.log(encode_str,'解码后:',decode_str)   // aGVsbG8= 解码后: hello

2 加解密

2.1 摘要加密算法 (MD5,sha1,sha256...)

概述:MD5是非常常见的摘要(hash)算法,小巧,速度快,所有摘要算法都难破解(只能通过撞库破解);

摘要算法特点

1 不可逆 (摘要算法说白了就不是一个加密逻辑,因为如果是加密就能解密);

2 相同的内容得出的结果是一样的,不同的内容得到的结果相差非常大 (这也是为什么说撞库能还原加密前的数据);

3 不同的摘要算法结果的长度是固定的,算法越复杂得到的结果就越长;

4 摘要算法都是由数字 "0~9",字母 "a~f" 组成的字符串,其中 MD5 的长度为 32,sha1 的长度为 40,sha256 的长度为 64,sha512 的长度为 128;

Python 实现 md5 加密案例

python 复制代码
from hashlib import md5, sha1
# 1 md5 实例化
md5_ = md5()

# 2 update 更新数据:对数据做摘要
data = "hello Niko".encode()
md5_.update(data)   # 注意:和之前的编码一样,传入的要是字节数据

# 3 获取摘要结果 (注意:有两种方式能获取摘要结果:hexdigest() 和 digest())
md5_res = md5_.hexdigest()
# md5_.digest()
print(md5_res) # 70f82955a3ef9260d4606fa1d762b923

Python md5 加盐操作案例

网站如果直接用原生 md5 对用户的密码进行加密,就存在严重的安全问题 ---- 被撞库的隐患,因此网站一般会为用户原有密码的基础上加盐,达到降低安全风险的目的

python 复制代码
from hashlib import md5, sha1
# 1 md5 实例化
salt = 'Niko没有Major冠军'.encode()    # 具体的内容随意,能达到混淆原有密码的功能即可
md5_ = md5(salt)    # 注意:加的盐也要是字节数据

# 2 update 更新数据:对数据做摘要
data = "hello Niko".encode()
md5_.update(data)   # 注意:和之前的编码一样,传入的要是字节数据

# 3 获取摘要结果
md5_res = md5_.hexdigest()
# md5_.digest()
print(md5_res) # 71ce88f33e45f4af14983becbca99334

Python md5 的一致性案例

md5 的 update 是一个追加操作,不是覆盖操作,多次 update 操作后,最后拿到的摘要结果是整个更新后字符串的摘要结果

python 复制代码
from hashlib import md5, sha1
# 1 md5 实例化
salt = 'Niko没有Major冠军'.encode()
md5_ = md5(salt)    # 注意:加的盐也要是字节数据

# 2 update 更新数据:对数据做摘要
data1 = "hello ".encode()
data2 = "Niko".encode()
md5_.update(data1)   # 注意:和之前的编码一样,传入的要是字节数据
md5_.update(data2)

# 3 获取摘要结果
md5_res = md5_.hexdigest()
# md5_.digest()
print(md5_res) # 71ce88f33e45f4af14983becbca99334

疑点解答:这时候可能就会有疑问,既然 md5 一次 update 与多次 update 得到的结果一致,为什么还要多次 update 呢? 这其实是要用在对应的场景,例如验证上传文件的完整性时,需要提前获取上传文件的内容再根据文件内容生成对应的 md5 值,用于与上传后的文件 md5 值做对比,假如本地有一个 100G 的文件,如果直接通过 python 去读文件数据,很容易崩溃,这时候就可以以分片的方式每 100MB 读一次再 update ,最后得到整个文件的 md5 值;

Python md5 对文件内容做摘要

python 复制代码
from hashlib import md5, sha1
# 1 md5 实例化
md5_ = md5()

# 2 update 更新数据:对数据做摘要
with open('data.log') as f:
    for line in f:
        md5_.update(line.encode())

# 3 获取摘要结果
md5_res = md5_.hexdigest()
# md5_.digest()
print(md5_res)

知识点补充

在获取摘要结果的时候既可以用 hexdigest() 也可以用 digest() ,它们得到的其实是一个东西,区别在于 digest() 得到的是一个字节类型的数据,而 hexdigest() 相当于在 digest() 的基础上做了一个类型转换,将字节转换成了字符串。

Python 其它摘要算法的语法演示

其它摘要算法与 md5 几乎一致,直接替换即可直接使用

python 复制代码
from hashlib import md5, sha1, sha256, sha512
# 1 实例化
sha1_ = sha1()    # 注意:加的盐也要是字节数据

# 2 update 更新数据:对数据做摘要
data = "hello Niko".encode()
sha1_.update(data)   # 注意:和之前的编码一样,传入的要是字节数据

# 3 获取摘要结果
md5_res = sha1_.hexdigest()
print(md5_res, len(md5_res)) # 0e104d2e7c5b8ad7ca599e82e8b3492c7385fb83 40

总结:sha1, sha256, md5算法都是摘要算法,都是在计算 hash 值,它们知识散列的程度不同,注意,它们的核心是散列,而不是加密,并且由于 hash 算法是不可逆的,所以不存在解密逻辑。

上面演示了 Python 的操作方法,下面演示 JS 的操作方法,首先安装环境:

python 复制代码
npm install crypto-js

JS 实现 MD5 摘要的案例

javascript 复制代码
crypto_js = require('crypto-js')

var data = "hello Niko"

// 生成 MD5 摘要
const md5Digest = crypto_js.MD5(data).toString()
console.log(md5Digest)  // 70f82955a3ef9260d4606fa1d762b923

2.2 对称加密算法 (AES, DES)

概述:形象点说就是加密和解密用同一把钥匙(密钥)的加密算法叫对称加密算法

安装

这个库里面有很多算法,里面包含了 AES,DES 等众多加密算法库所以我们要安装它

Haskell 复制代码
pip install pycryptodome

知识点:AES 有很多模式,常用的模式是 CBC 和 ECB ,CBC 的参数包含密钥 (key) 和偏移量 (iv),而 ECB 的参数只有密钥 (key);下面做详细解释:

python 复制代码
"""
key的长度:
    16:对应 AES-128
    24: 对应 AES-192
    32: 对应 AES-256
    
偏移量(iv)的长度固定为16位

模式:
    常用模式是 ECB, CBC;
    ECB: 是一种基础的加密方式,密文被分割成分组长度相等的块(这里分割的长度取决于key指定的位数,不足就补齐,有点像之前的 Base64 编码原理),然后每个分组单独加密,最后将所有块加密的结果拼接起来就是最后加密的结果;
    CBC: 一种循环模式,前一个分组的密文会与当前分组的明文做亦或操作后再加密,依此类推,提升了破解难度
"""

Python AES先加密后编码案例

python 复制代码
from Crypto.Cipher import AES, DES
from Crypto.Util.Padding import pad,unpad   # pad用于填充, unpad用于去除填充
import base64

# 提前预处理要加密的数据
data = '尼尼爱孩孩爱京京'.encode()   # 待加密数据,由于传入的一定是字节数据,所以这里提前处理为字节数据
new_data = pad(data,16) # 由于我们要做 16 位的加密,所以数据的长度一定要是 16 的倍数,这里直接用填充库去做填充
print(f'原始数据:{data}')   # 原始数据:b'\xe5\xb0\xbc\xe5\xb0\xbc\xe7\x88\xb1\xe5\xad\xa9\xe5\xad\xa9\xe7\x88\xb1\xe4\xba\xac\xe4\xba\xac'
print(f'补充完的新数据:{new_data}')    # 补充完的新数据:b'\xe5\xb0\xbc\xe5\xb0\xbc\xe7\x88\xb1\xe5\xad\xa9\xe5\xad\xa9\xe7\x88\xb1\xe4\xba\xac\xe4\xba\xac\x08\x08\x08\x08\x08\x08\x08\x08'

# 1 实例化对象,构建算法对象
# 参数:mode:AES.MODE_CBC/MODE_ECB key iv(CBC模式有这个参数, ECB模式没有)
key = '1234567890abcdef'.encode()
iv = 'ajdihexildtiskdi'.encode()
aes_obj = AES.new(mode=AES.MODE_CBC,key=key,iv=iv)

# 2 数据加密
encrypt_data = aes_obj.encrypt(new_data)
print(f'AES加密后的数据:{encrypt_data}')  # AES加密后的数据:b'\xc2\xde\xb9\xdfa\x8fCt\xb3C2\xb1"\x16[\xa0\x0b\x910\xfbu\x87\x1c-\x83\xd0\xf6}\x1d\x8b\xdc4'

# 3 base64 编码
# 正常网页中看到的 AES 还需要套一层 base64 编码 (即先加密后编码)
encode_encrypt_data = base64.b64encode(encrypt_data).decode()
print(f'网页中看到的 AES 加密后的结果:{encode_encrypt_data}')   # 网页中看到的 AES 加密后的结果:wt6532GPQ3SzQzKxIhZboAuRMPt1hxwtg9D2fR2L3DQ=

Python AES 先解码后解密案例

python 复制代码
import base64
from Crypto.Cipher import AES, DES
from Crypto.Util.Padding import pad,unpad   # pad用于填充, unpad用于去除填充

# 1 解码 base64
encode_encrypt_data = 'wt6532GPQ3SzQzKxIhZboAuRMPt1hxwtg9D2fR2L3DQ='
encrypt_data = base64.b64decode(encode_encrypt_data)

# 2 数据解密:需要知道用的什么算法,什么密钥,这也是后续逆向的重点,这里先假设知道
key = '1234567890abcdef'.encode()
iv = 'ajdihexildtiskdi'.encode()
aes_obj = AES.new(mode=AES.MODE_CBC,key=key,iv=iv)
data = aes_obj.decrypt(encrypt_data)
print(data) # b'\xe5\xb0\xbc\xe5\xb0\xbc\xe7\x88\xb1\xe5\xad\xa9\xe5\xad\xa9\xe7\x88\xb1\xe4\xba\xac\xe4\xba\xac\x08\x08\x08\x08\x08\x08\x08\x08'
a = unpad(data,16).decode() # 想要解码中文一定要用到 unpad 提前将之前自动添加的内容去除,当然英文想要还原内容也需要用到,只是中文不去除就完全还原不了
print(a)    # 尼尼爱孩孩爱京京

补充

1 上面的案例都在讲 AES 的 CBC 模式,如果想要用到 ECB 模式只需要将程序中 iv 相关操作直接删除即可,然后将 CBC 替换为 ECB;

2 如果想要用到 DES 只需要将 AES 直接替换为 DES 即可,操作基本一样

JS 实现 AES 加解密案例

javascript 复制代码
var cryptoJs = require('crypto-js')

var data = 'hello Niko'

// 密钥 16字节 128位
var key = cryptoJs.enc.Utf8.parse('nikoyouaregood66') // 这个操作就相当于 python 中的 encode()
// 偏移量 16字节 128位
var iv = cryptoJs.enc.Utf8.parse('makeNikogreatage') // 相当于 python 对字符串做 encode() 操作

// js 中 AES 加密将初始化对象与加密操作整合在一起了
var encrypt_data = cryptoJs.AES.encrypt(data, key, {
    iv: iv, // 注意:如果是 ECB 模式就不存在 iv
    mode: cryptoJs.mode.CBC,
    padding: cryptoJs.pad.Pkcs7 // 这一行的作用就是做自动填充,填充成 key 的倍数 (16,24,32 ...)
})

// 加密后的数据(从加密结果获取)
var encryptedData = encrypt_data.toString();
console.log(encryptedData) // i9hoYJsmYfaoNtXSxL/Icg==

// 执行解密操作
var decryptResult = cryptoJs.AES.decrypt(encryptedData, key, {
    iv: iv,
    mode: cryptoJs.mode.CBC,
    padding: cryptoJs.pad.Pkcs7
});

// 将解密结果转换为UTF-8字符串
var decryptedData = decryptResult.toString(cryptoJs.enc.Utf8);

console.log(decryptedData); // hello Niko

2.3 公钥与私钥

作用

1 公钥加密,私钥解密:数据加密;

2 私钥加密,公钥解密:数字签名。

场景理解

python 复制代码
"""
鲍勃和苏珊的故事:
    苏珊想给鲍勃发一封信,为了只让鲍勃看见信的内容,苏珊就拿着鲍勃的公钥对信的内容做了加密并发送给鲍勃,
只要鲍勃的私钥不泄露,这封信的内容就是安全的,鲍勃拿到信后,用自己的私钥解密就能看到实际内容了;(数据加密场景)

    鲍勃看了信的内容后为苏珊回信,首先用 md5 等摘要算法对信件内容做摘要(digest)(摘要算法的特性,生成固定长度的值),
然后鲍勃用自己的私钥对生成的摘要做加密,加密后的内容成为数字签名(Signature),鲍勃将这个数字签名附在信件后面一起发送给苏珊
(注意:信件内容是没有做加密的,只是基于信件内容和摘要算法生成了数字签名),
苏珊收到信后,首先用鲍勃的公钥对签名做解密,得到了信件内容的摘要,得到摘要就说明这封信确实是鲍勃发的(因为公钥加密只有私钥能解密,反之也一样),
解密后的摘要先记为 md_a , 然后苏珊用对应的摘要算法对信件内容做摘要, 这里的摘要记为 md_b, 然后对比 md_a 和 md_b ,
如果 md_a 和 md_b 内容完全一样就说明信件内容没有被修改过;(数字签名场景)
"""

2.4 非对称加密算法 (RSA)

Python RSA 生成公钥与密钥案例

python 复制代码
from Crypto.PublicKey import RSA

# 创建初始化对象
rsa_obj = RSA.generate(1024)

# 生成密钥
with open('rsa.publick.pem','w') as f:
    # 导出公钥并保存到文件
    f.write(rsa_obj.exportKey().decode())
with open('rsa.private.pom','w') as f:
    # 导出私钥并保存到文件
    f.write(rsa_obj.exportKey().decode())

Python RSA 利用公钥做加密案例

python 复制代码
import base64

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

# 准备需要加密的数据
data = 'niko is good man'

with open('rsa.publick.pem','r') as f:
    # 1 获取公钥字符串
    publick_key_str = f.read()
    # 2 基于公钥字符串构建公钥对象
    publick_key_obj = RSA.importKey(publick_key_str)
    # 3 基于公钥对象生成 RSA 算法对象
    rsa_obj = PKCS1_v1_5.new(publick_key_obj)
    # 4 对数据做加密
    encrypt_data = rsa_obj.encrypt(data.encode())
    # 网站中常对加密数据做编码
    b64_encrypt_data = base64.b64encode(encrypt_data)
    print(b64_encrypt_data.decode())    # f5NuURO0lMx0IOB6g0biLOI0WM2RXMiMdsiflJmm/Jjy9bCodv6TrypDP5mlkbXPyotzl10gLFv3mECeDGBhKUpmXWAMsQwO6FcTQPzBHwdy2FYc2bF4B32A3qtP6Heg49QV98u6x5ksHnHrunvNCBi/2mjcSSTfUgjJ6r/Eozs=

Python RSA 利用私钥做解密案例

python 复制代码
import base64

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

b64_encrypt_data = 'f5NuURO0lMx0IOB6g0biLOI0WM2RXMiMdsiflJmm/Jjy9bCodv6TrypDP5mlkbXPyotzl10gLFv3mECeDGBhKUpmXWAMsQwO6FcTQPzBHwdy2FYc2bF4B32A3qtP6Heg49QV98u6x5ksHnHrunvNCBi/2mjcSSTfUgjJ6r/Eozs='
# 解码操作
encrypt_data = base64.b64decode(b64_encrypt_data.encode())

# 私钥解密
with open('rsa.private.pom','r') as f:
    # 1 获取私钥字符串
    private_key_str = f.read()
    # 2 基于私钥字符串生成私钥对象
    private_key_obj = RSA.importKey(private_key_str)
    # 3 基于私钥对象生成私钥 RSA 算法对象
    private_rsa_obj = PKCS1_v1_5.new(private_key_obj)
    # 4 基于私钥 rsa 对象做解密 (注意:这里要多传一个值 None, 否则会报错)
    source_data = private_rsa_obj.decrypt(encrypt_data, None)
    print(f'通过私钥解密后的数据为:{source_data.decode()}') # 通过私钥解密后的数据为:niko is good man

提示:下面两个案例主要是为了区分浏览器环境与 node.js 环境。

JS 浏览器环境实现 RSA 加解密案例

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>RSA加解密演示</title>
    <!-- 引入jsencrypt库 -->
    <script src="https://cdn.jsdelivr.net/npm/jsencrypt@3.3.1/bin/jsencrypt.min.js"></script>
</head>
<body>
    <script>
        // 创建JSEncrypt实例
        const rsa = new JSEncrypt();

        // 1. 生成RSA密钥对
        rsa.getKey(); // 生成密钥对
        const publicKey = rsa.getPublicKey();
        const privateKey = rsa.getPrivateKey();

        console.log('=== 生成的密钥对 ===');
        console.log('公钥:\n', publicKey);
        console.log('私钥:\n', privateKey);

        // 2. 原始数据(RSA适合短文本加密,过长会失败)
        const originalData = 'Hello, RSA in Browser!';
        console.log('\n=== 原始数据 ===');
        console.log(originalData);

        // 3. 使用公钥加密
        rsa.setPublicKey(publicKey); // 显式设置公钥
        const encryptedData = rsa.encrypt(originalData);
        console.log('\n=== 加密后的数据(Base64) ===');
        console.log(encryptedData);

        // 4. 使用私钥解密
        rsa.setPrivateKey(privateKey); // 显式设置私钥
        const decryptedData = rsa.decrypt(encryptedData);
        console.log('\n=== 解密后的数据 ===');
        console.log(decryptedData);

        // 5. 验证解密结果
        console.log('\n=== 验证结果 ===');
        console.log('解密是否成功:', decryptedData === originalData ? '成功' : '失败');
    </script>
</body>
</html>

运行结果如下:

JS node.js 环境下 RSA 的加解密案例

javascript 复制代码
// npm install node-rsa
const NodeRSA = require('node-rsa');

// 1. 创建 RSA 实例并生成密钥对(512)
const key = new NodeRSA({ b: 512 }); // b 表示密钥长度(2048/4096等)

// 2. 导出公钥和私钥(PEM格式,常用格式)
const publicKey = key.exportKey('public'); // 公钥(用于加密)
const privateKey = key.exportKey('private'); // 私钥(用于解密)

// 3. 原始数据(RSA适合短文本加密,过长会失败)
const originalData = 'Hello, RSA in Node.js!';
console.log('\n=== 原始数据 ===', originalData);

// 4. 使用公钥加密
// 加密:默认返回 Base64 编码的字符串(可指定编码格式)
const encryptedData = key.encrypt(originalData, 'base64');
console.log('\n=== 加密后的数据(Base64) ===', encryptedData);

// 5. 使用私钥解密
// 解密:将 Base64 密文解密为原始字符串
const decryptedData = key.decrypt(encryptedData, 'utf8');
console.log('\n=== 解密后的数据 ===', decryptedData);

// 6. 验证解密结果
console.log('\n=== 验证结果 ===');
console.log('解密是否成功:', decryptedData === originalData ? '成功' : '失败');

运行结果如下:

相关推荐
又是忙碌的一天4 小时前
前端学习 JavaScript(2)
前端·javascript·学习
2501_915106324 小时前
JavaScript编程工具有哪些?老前端的实用工具清单与经验分享
开发语言·前端·javascript·ios·小程序·uni-app·iphone
GISer_Jing4 小时前
计算机基础——浏览器、算法、计算机原理和编译原理等
前端·javascript·面试
我的xiaodoujiao4 小时前
从 0 到 1 搭建 Python 语言 Web UI自动化测试学习系列 8--基础知识 4--常用函数 2
前端·python·测试工具·ui
蓝瑟4 小时前
React 项目实现拖拽排序功能,如何在众多库中选对 “它”
前端·javascript·react.js
Rhys..4 小时前
Cucumber自学导航
javascript·python·bdd·cucumber
kobe_OKOK_4 小时前
Django ORM 无法通过 `ForeignKey` 自动关联,而是需要 **根据父模型中的某个字段(比如 ID)去查询子模型**。
后端·python·django
蜀中廖化4 小时前
python VSCode中报错 E501:line too long (81 > 79 characters)
开发语言·vscode·python
MoRanzhi12034 小时前
15. Pandas 综合实战案例(零售数据分析)
数据结构·python·数据挖掘·数据分析·pandas·matplotlib·零售