跨网络互联技术(加密算法)
网络数据包分层结构
┌───────────────────────────────┐
│ 以太网头 (Ethernet Header) │ ← 链路层:源 MAC、目标 MAC
├───────────────────────────────┤
│ IP 头 (IP Header) │ ← 网络层:源 IP、目标 IP、TTL 等
├───────────────────────────────┤
│ TCP 头 (TCP Header) │ ← 传输层:源端口、目标端口、序列号
├───────────────────────────────┤
│ HTTP 报文 (HTTP Request/Resp) │ ← 应用层:GET/POST、头部、正文
└───────────────────────────────┘
什么是TCP
在发送应用数据之前,TCP 会用三次握手建立一条可靠的双向通道:
SYN: 客户端提出“我要连你”(同步序列号)。
SYN‑ACK: 服务端回应“我收到了,也准备好了”(同步并确认)。
ACK: 客户端再确认一次“好,开始吧”。
这三步让双方都知道彼此的初始序列号与收发能力,随后才进入数据传输。
TCP 是“面向连接”的,必须先建立连接(SYN → SYN-ACK → ACK)。
UDP 是“无连接”的,直接发数据,不需要建立或关闭连接。
RSA/ECC(非对称加密)
方式 1:你说的 “甲方生成对称密钥,用公钥加密”(以 RSA 为例)
乙方生成 RSA 密钥对:私钥(自己留)、公钥(发给甲方)。
甲方要发 “123”:
自己生成一个对称密钥(比如 AES 密钥 “key123”)。
用乙方的公钥加密这个对称密钥(“key123” 变成乱码)。
用 “key123” 加密 “123” 得到密文。
把 “加密后的对称钥” 和 “加密后的 123” 一起发给乙方。
乙方收到后:
用自己的私钥解密 “加密后的的对称密钥”,得到 “key123”。
用 “key123” 解密密文,得到 “123”。
方式 2:ECC 常用的 “密钥交换”
乙方生成 ECC 密钥对:私钥(自己留)、公钥(发给甲方)。
甲方生成自己的 ECC 临时密钥对:私钥(自己留)、公钥(发给乙方)。
双方各自计算:
甲方用自己的私钥 + 乙方的公钥 → 算出对称密钥 “key123”。
乙方用自己的私钥 + 甲方的公钥 → 也算出 “key123”。
甲方用 “key123” 加密 “123” 发给乙方,乙方用 “key123” 解密。
RSA代码分析
解释
公钥加密的密文只能用私钥解密,私钥加密的密文也只能用公钥解密
代码
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization, hashes# ------------------------------
# 1. 生成RSA密钥对(私钥+公钥)
# ------------------------------
# 生成2048位RSA私钥(密钥长度推荐2048位及以上,安全性更高)
# public_exponent=65537:固定公钥指数,是RSA的标准选择
private_key = rsa.generate_private_key(public_exponent=65537,key_size=2048
)# 从私钥中提取公钥(公钥可公开给其他人)
public_key = private_key.public_key()# 序列化密钥为PEM格式(便于存储或传输,如保存到文件)
private_pem = private_key.private_bytes(encoding=serialization.Encoding.PEM,format=serialization.PrivateFormat.PKCS8, # 通用的私钥格式encryption_algorithm=serialization.NoEncryption() # 不加密私钥(实际场景可加密)
)public_pem = public_key.public_bytes(encoding=serialization.Encoding.PEM,format=serialization.PublicFormat.SubjectPublicKeyInfo # 公钥标准格式
)print("RSA私钥(PEM格式,需保密):\n", private_pem.decode()[:50] + "...") # 简化输出
print("\nRSA公钥(PEM格式,可公开):\n", public_pem.decode()[:50] + "...")# ------------------------------
# 2. 用公钥加密数据(只能加密小数据,这里加密"123")
# ------------------------------
# 要加密的数据(RSA加密长度有限制,2048位密钥约可加密245字节以内的数据)
data = "123".encode() # 转为字节流# 用公钥加密(OAEP是推荐的安全填充方式)
encrypted_data = public_key.encrypt(data,padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), # 掩码生成函数algorithm=hashes.SHA256(), # 哈希算法label=None)
)print("\n加密后的数据(十六进制):", encrypted_data.hex())# ------------------------------
# 3. 用私钥解密数据
# ------------------------------
# 用私钥解密(必须和加密时用相同的填充方式)
decrypted_data = private_key.decrypt(encrypted_data,padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()),algorithm=hashes.SHA256(),label=None)
)print("解密后的数据:", decrypted_data.decode()) # 输出"123"PEM 格式的作用:“二进制→文本” 的转换器
PEM 格式其实是一种 “编码规则”,把二进制密钥转成由A-Z、a-z、0-9、+、/组成的 Base64 字符串,再加上固定的头部(-----BEGIN PUBLIC KEY-----)和尾部(-----END PUBLIC KEY-----),形成可读的文本。
私钥加密原始数据,公钥解密还原
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization, hashes# 1. 生成RSA密钥对
private_key = rsa.generate_private_key(public_exponent=65537,key_size=2048
)
public_key = private_key.public_key()# 2. 用私钥直接加密原始数据(注意:RSA私钥加密也有长度限制,2048位约245字节)
data = "123".encode() # 要加密的原始数据# 私钥加密(使用OAEP填充,和公钥加密时保持一致,确保能解密)
encrypted_by_private = private_key.encrypt(data,padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()),algorithm=hashes.SHA256(),label=None)
)
print("私钥加密后的数据(十六进制):", encrypted_by_private.hex())# 3. 用公钥解密,直接还原原始数据(不做验证,仅解密)
decrypted_by_public = public_key.decrypt(encrypted_by_private,padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()),algorithm=hashes.SHA256(),label=None)
)
print("公钥解密后的数据:", decrypted_by_public.decode()) # 输出"123"验证签名是否被篡改
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization, hashes# 1. 生成RSA密钥对(和之前相同)
private_key = rsa.generate_private_key(public_exponent=65537,key_size=2048
)
public_key = private_key.public_key()# 2. 用私钥加密数据(注意:这里的“加密”更偏向“签名”逻辑)
data = "123".encode()# 私钥加密(使用PSS填充,适合签名场景;也可继续用OAEP,但PSS更推荐用于签名)
encrypted_by_private = private_key.sign(data,padding.PSS(mgf=padding.MGF1(hashes.SHA256()),salt_length=padding.PSS.MAX_LENGTH),hashes.SHA256()
)print("私钥加密(签名)后的数据(十六进制):", encrypted_by_private.hex())# 3. 用公钥解密(验证签名)
try:# 公钥解密密(验证签名):如果成功数据是否由对应私钥加密(即签名)public_key.verify(encrypted_by_private, # 私钥加密后的结果(签名)data, # 原始数据padding.PSS(mgf=padding.MGF1(hashes.SHA256()),salt_length=padding.PSS.MAX_LENGTH),hashes.SHA256())print("公钥解密(验证)成功:数据确实由对应应私钥加密,且未被篡改")print("原始数据:", data.decode())
except:print("公钥解密(验证)失败:数据被篡改或不是对应应私钥加密")AES(对称加密)
IV(初始化向量)的作用一句话说透:让相同明文 + 相同密钥,每次加密都得到不同密文,防止被破解,随机生成就是为了最大化这个效果。
IV 的核心作用:给 “洗牌” 定一个 “随机起始位置”
没有 IV 时:
你每次加密 “123”(相同明文),用同一把 AES 密钥(相同规则),“洗牌” 的第一步(替换、移位)起始位置都一样。
结果就是:每次加密后的密文都完全相同 —— 黑客只要看到 “某串乱码 = 123”,下次再碰到这串乱码,直接就知道是 123,一破解一个准。
有 IV 时:
IV 就像洗牌前的 “随机起点”,比如第一次让你从第 3 格开始换值,第二次从第 7 格开始,第三次从第 12 格开始。
哪怕明文和密钥都没变,因为 “起始位置” 随机,最终的洗牌结果(密文)也完全不同,黑客没法通过 “密文重复” 猜明文。
IV 不用保密,只需要 “每次不同”
IV 的作用是 “定起始位置”,不是 “加密密钥”,所以可以和密文一起公开传输(比如之前代码里,IV 放在密文最前面)。
解密时,只要用 “和加密时相同的 IV + 相同的密钥”,就能找到正确的 “起始位置”,反向洗牌还原明文。
代码
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
import os# ------------------------------
# 1. 生成AES密钥(关键!必须保存好,解密需要)
# ------------------------------
# AES密钥长度可选:128位(16字节)、192位(24字节)、256位(32字节)
# 推荐128位或256位,这里用128位示例
aes_key = os.urandom(16) # 随机生成16字节(128位)密钥
print("AES密钥(十六进制):", aes_key.hex()) # 显示密钥(实际中不要泄露)# ------------------------------
# 2. 定义加密函数(用AES加密数据)
# ------------------------------
def aes_encrypt(key, plaintext):# 生成随机IV(初始化向量):AES-CBC模式需要16字节IV,每次加密都要不同iv = os.urandom(16) # 随机生成16字节IV# 创建加密器:使用AES算法,CBC模式(最常用的模式之一)cipher = Cipher(algorithms.AES(key), modes.CBC(iv))encryptor = cipher.encryptor()# 数据补位:CBC模式要求数据长度是16字节的倍数,不足的用空格补全# (实际场景中会用更标准的PKCS7补位,这里简化用空格)padded_text = plaintext.encode().ljust(16 * ((len(plaintext) // 16) + 1))# 执行加密ciphertext = encryptor.update(padded_text) + encryptor.finalize()# 返回:IV + 密文(解密时需要IV,所以一起传输)return iv + ciphertext# ------------------------------
# 3. 定义解密函数(用AES解密数据)
# ------------------------------
def aes_decrypt(key, encrypted_data):# 从加密结果中提取IV(前16字节)和密文(剩余部分)iv = encrypted_data[:16]ciphertext = encrypted_data[16:]# 创建解密器:和加密用相同的算法和模式cipher = Cipher(algorithms.AES(key), modes.CBC(iv))decryptor = cipher.decryptor()# 执行解密decrypted_padded = decryptor.update(ciphertext) + decryptor.finalize()# 去除补位的空格,转回字符串return decrypted_padded.decode().strip()# ------------------------------
# 4. 测试:加密解密"123"
# ------------------------------
# 要加密的数据
data = "123"# 加密
encrypted = aes_encrypt(aes_key, data)
print("加密后的数据(十六进制):", encrypted.hex()) # 十六进制显示密文# 解密
decrypted = aes_decrypt(aes_key, encrypted)
print("解密后的数据:", decrypted)MD5 (数据指纹)
MD5 是一种哈希 算法(哈希算法,也叫散列算法),核心作用是把任意长度的数据(比如一段文字、一个文件)转换成一个固定长度的字符串(通常是 32 位十六进制十六进制的十六数字和字母的组合),这个字符串被称为 “哈希值” 或 “摘要”。
不可逆:通过哈希值无法反推出原始数据(比如知道 202cb962ac59075b964b07152d234b70,无法算出原始数据是 “123”)。
加盐
MD5 加盐(Salt)是一种增强 MD5 安全性的手段,专门用来对付 “字典攻击”(通过彩虹表反查明文),原理很简单:给原始数据 “加一段随机字符串” 再算 MD5,让黑客的字典彻底失效。
加盐的核心逻辑:
生成随机 “盐”:每次加密时,生成一段随机字符串(比如 8 位、16 位,越长越安全),比如 “salt_123$”。
明文 + 盐混合:把原始明文和盐拼接在一起(比如明文 “123”+ 盐 “salt_123”)。
计算加盐后的 MD5:用混合后的字符串算 MD5,得到的哈希值和不加盐时完全不同。
存储 “盐 + 加盐后的 MD5”:盐不需要保密,和最终的 MD5 值一起存到数据库里(比如存 “salt_123$|a7b3f9...”)。
