文章目录
-
- 1. 核心概念地图
- 2. 对称加密:AES 的内部解剖与实战
-
- 2.1 AES 单轮变换流程图
- 2.2 分组模式详解:ECB vs CBC
- 2.3 实战:AES-GCM 加密与解密
-
- Java (JDK 11+)
- JavaScript (Node.js)
- 3. 非对称加密:RSA 的数理逻辑
-
- 3.1 RSA 密钥生成流程图
- 3.2 填充的重要性:OAEP
- 3.3 实战:RSA-OAEP 加密与解密
-
- Java (JDK 11+)
- JavaScript (Node.js)
- 4. 数字签名:身份验证
-
- 4.1 签名生成与验证流程
- 4.2 实战:RSASSA-PSS 签名
-
- Java (JDK 11+)
- JavaScript (Node.js)
- 5. 混合加密实战:HTTPS (TLS 1.2) 握手全流程
-
- 5.1 实战:使用 OpenSSL 分析 HTTPS 握手
- 6. 密码存储与哈希:PBKDF2 实战
-
-
- Java (JDK 8+)
- JavaScript (Node.js)
-
- 7. 技术深度补充:常见攻击与防御
- 8. 总结:现代安全架构设计图
导读:本文结合 Java 后端与 JavaScript (Node.js) 前后端代码,剖析如何正确实现 AES-GCM、RSA-OAEP 和 PBKDF2,避开常见安全陷阱。
1. 核心概念地图
| Base64 | 编码(非加密) | ✅ 可逆 | ❌ 无保密性 | 邮件、JWT、图片内嵌 |
| MD5 / SHA-1 | 哈希摘要 | ❌ 不可逆 | ⚠️ 已不安全 | 文件校验(仅限非敏感场景) |
| SHA-2 / SHA-3 | 哈希摘要 | ❌ 不可逆 | ✅ 安全 | 密码哈希、数字签名 |
| 对称加密(AES) | 加密 | ✅ 可逆 | ✅ 高(需正确使用) | 数据传输、数据库加密 |
| 非对称加密(RSA) | 加密/签名 | ✅ 可逆 | ✅ 高(需足够密钥长度) | 密钥交换、身份认证 |
关键区分:
- 编码 ≠ 加密(Base64 是编码)
- 哈希 ≠ 加密(MD5/SHA 是单向函数)
- 加密必须可逆(否则无法还原数据)
2. 对称加密:AES 的内部解剖与实战
AES (Advanced Encryption Standard) 是基于SP 网络(代换-置换网络)的。它是目前最流行的对称加密算法。
2.1 AES 单轮变换流程图
AES 加密不是一步完成的,而是需要多轮迭代(AES-128 为 10 轮)。下图展示了核心的数据处理流向:
#mermaid-svg-kZK7x6lAQZvFzLPM{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-kZK7x6lAQZvFzLPM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kZK7x6lAQZvFzLPM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kZK7x6lAQZvFzLPM .error-icon{fill:#552222;}#mermaid-svg-kZK7x6lAQZvFzLPM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kZK7x6lAQZvFzLPM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kZK7x6lAQZvFzLPM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kZK7x6lAQZvFzLPM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kZK7x6lAQZvFzLPM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kZK7x6lAQZvFzLPM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kZK7x6lAQZvFzLPM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kZK7x6lAQZvFzLPM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kZK7x6lAQZvFzLPM .marker.cross{stroke:#333333;}#mermaid-svg-kZK7x6lAQZvFzLPM svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kZK7x6lAQZvFzLPM p{margin:0;}#mermaid-svg-kZK7x6lAQZvFzLPM .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-kZK7x6lAQZvFzLPM .cluster-label text{fill:#333;}#mermaid-svg-kZK7x6lAQZvFzLPM .cluster-label span{color:#333;}#mermaid-svg-kZK7x6lAQZvFzLPM .cluster-label span p{background-color:transparent;}#mermaid-svg-kZK7x6lAQZvFzLPM .label text,#mermaid-svg-kZK7x6lAQZvFzLPM span{fill:#333;color:#333;}#mermaid-svg-kZK7x6lAQZvFzLPM .node rect,#mermaid-svg-kZK7x6lAQZvFzLPM .node circle,#mermaid-svg-kZK7x6lAQZvFzLPM .node ellipse,#mermaid-svg-kZK7x6lAQZvFzLPM .node polygon,#mermaid-svg-kZK7x6lAQZvFzLPM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-kZK7x6lAQZvFzLPM .rough-node .label text,#mermaid-svg-kZK7x6lAQZvFzLPM .node .label text,#mermaid-svg-kZK7x6lAQZvFzLPM .image-shape .label,#mermaid-svg-kZK7x6lAQZvFzLPM .icon-shape .label{text-anchor:middle;}#mermaid-svg-kZK7x6lAQZvFzLPM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-kZK7x6lAQZvFzLPM .rough-node .label,#mermaid-svg-kZK7x6lAQZvFzLPM .node .label,#mermaid-svg-kZK7x6lAQZvFzLPM .image-shape .label,#mermaid-svg-kZK7x6lAQZvFzLPM .icon-shape .label{text-align:center;}#mermaid-svg-kZK7x6lAQZvFzLPM .node.clickable{cursor:pointer;}#mermaid-svg-kZK7x6lAQZvFzLPM .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-kZK7x6lAQZvFzLPM .arrowheadPath{fill:#333333;}#mermaid-svg-kZK7x6lAQZvFzLPM .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-kZK7x6lAQZvFzLPM .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-kZK7x6lAQZvFzLPM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kZK7x6lAQZvFzLPM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-kZK7x6lAQZvFzLPM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kZK7x6lAQZvFzLPM .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-kZK7x6lAQZvFzLPM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-kZK7x6lAQZvFzLPM .cluster text{fill:#333;}#mermaid-svg-kZK7x6lAQZvFzLPM .cluster span{color:#333;}#mermaid-svg-kZK7x6lAQZvFzLPM div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-kZK7x6lAQZvFzLPM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-kZK7x6lAQZvFzLPM rect.text{fill:none;stroke-width:0;}#mermaid-svg-kZK7x6lAQZvFzLPM .icon-shape,#mermaid-svg-kZK7x6lAQZvFzLPM .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kZK7x6lAQZvFzLPM .icon-shape p,#mermaid-svg-kZK7x6lAQZvFzLPM .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-kZK7x6lAQZvFzLPM .icon-shape rect,#mermaid-svg-kZK7x6lAQZvFzLPM .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kZK7x6lAQZvFzLPM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-kZK7x6lAQZvFzLPM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-kZK7x6lAQZvFzLPM :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
是 Rounds 1-9
否 Final Round
输入: 128位明文块
AddRoundKey (与轮密钥异或)
SubBytes (S盒替换)
ShiftRows (行移位)
MixColumns (列混合)
AddRoundKey (与轮密钥异或)
SubBytes (S盒替换)
ShiftRows (行移位)
AddRoundKey (最终轮密钥异或)
输出: 128位密文块
轮次计数 < 9?
2.2 分组模式详解:ECB vs CBC
AES 只能处理 16 字节(128位)的块。对于长数据,需要模式来切分。下图对比了不安全的 ECB 模式与安全的 CBC 模式:
#mermaid-svg-ZWgLped5enguTCaV{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZWgLped5enguTCaV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZWgLped5enguTCaV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZWgLped5enguTCaV .error-icon{fill:#552222;}#mermaid-svg-ZWgLped5enguTCaV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZWgLped5enguTCaV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZWgLped5enguTCaV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZWgLped5enguTCaV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZWgLped5enguTCaV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZWgLped5enguTCaV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZWgLped5enguTCaV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZWgLped5enguTCaV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZWgLped5enguTCaV .marker.cross{stroke:#333333;}#mermaid-svg-ZWgLped5enguTCaV svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZWgLped5enguTCaV p{margin:0;}#mermaid-svg-ZWgLped5enguTCaV .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZWgLped5enguTCaV .cluster-label text{fill:#333;}#mermaid-svg-ZWgLped5enguTCaV .cluster-label span{color:#333;}#mermaid-svg-ZWgLped5enguTCaV .cluster-label span p{background-color:transparent;}#mermaid-svg-ZWgLped5enguTCaV .label text,#mermaid-svg-ZWgLped5enguTCaV span{fill:#333;color:#333;}#mermaid-svg-ZWgLped5enguTCaV .node rect,#mermaid-svg-ZWgLped5enguTCaV .node circle,#mermaid-svg-ZWgLped5enguTCaV .node ellipse,#mermaid-svg-ZWgLped5enguTCaV .node polygon,#mermaid-svg-ZWgLped5enguTCaV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZWgLped5enguTCaV .rough-node .label text,#mermaid-svg-ZWgLped5enguTCaV .node .label text,#mermaid-svg-ZWgLped5enguTCaV .image-shape .label,#mermaid-svg-ZWgLped5enguTCaV .icon-shape .label{text-anchor:middle;}#mermaid-svg-ZWgLped5enguTCaV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ZWgLped5enguTCaV .rough-node .label,#mermaid-svg-ZWgLped5enguTCaV .node .label,#mermaid-svg-ZWgLped5enguTCaV .image-shape .label,#mermaid-svg-ZWgLped5enguTCaV .icon-shape .label{text-align:center;}#mermaid-svg-ZWgLped5enguTCaV .node.clickable{cursor:pointer;}#mermaid-svg-ZWgLped5enguTCaV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ZWgLped5enguTCaV .arrowheadPath{fill:#333333;}#mermaid-svg-ZWgLped5enguTCaV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZWgLped5enguTCaV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZWgLped5enguTCaV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZWgLped5enguTCaV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ZWgLped5enguTCaV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZWgLped5enguTCaV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ZWgLped5enguTCaV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZWgLped5enguTCaV .cluster text{fill:#333;}#mermaid-svg-ZWgLped5enguTCaV .cluster span{color:#333;}#mermaid-svg-ZWgLped5enguTCaV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ZWgLped5enguTCaV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ZWgLped5enguTCaV rect.text{fill:none;stroke-width:0;}#mermaid-svg-ZWgLped5enguTCaV .icon-shape,#mermaid-svg-ZWgLped5enguTCaV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZWgLped5enguTCaV .icon-shape p,#mermaid-svg-ZWgLped5enguTCaV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ZWgLped5enguTCaV .icon-shape rect,#mermaid-svg-ZWgLped5enguTCaV .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZWgLped5enguTCaV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ZWgLped5enguTCaV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ZWgLped5enguTCaV :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
CBC 模式 – 安全
异或 XOR
AES加密
异或 XOR
AES加密
初始化向量 IV
明文块1
密文1
明文块2
密文2
ECB 模式 – 不安全
AES加密
AES加密
AES加密
明文块1
密文1
明文块1
密文块
明文块2
密文2
图解说明:
- ECB:相同的明文块(如 P1_1 和 P1_2)生成了完全相同的密文块,这会暴露数据的统计规律(例如加密一张黑白图片,轮廓依然可见)。
- CBC:引入了随机初始化向量(IV),并将上一个密文块参与到下一个块的加密中。即使明文相同,只要 IV 不同,密文就完全不同。
2.3 实战:AES-GCM 加密与解密
推荐使用 AES-GCM 模式,因为它不仅加密数据,还生成认证标签,防止篡改。
Java (JDK 11+)
Java 标准库 javax.crypto 提供了完整的 AES-GCM 支持。
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.util.Base64;
public class AesGcmExample {
private static final String AES_GCM_NO_PADDING = "AES/GCM/NoPadding";
private static final int GCM_TAG_LENGTH_BITS = 128; // 必须是 128, 120 或 112
private static final int GCM_IV_LENGTH_BYTES = 12; // 推荐长度 12 bytes
public static String encrypt(byte[] plaintext, byte[] key) throws Exception {
// 1. 生成随机 IV (Nonce)
byte[] iv = new byte[GCM_IV_LENGTH_BYTES];
new SecureRandom().nextBytes(iv);
// 2. 初始化 Cipher
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH_BITS, iv);
Cipher cipher = Cipher.getInstance(AES_GCM_NO_PADDING);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec);
// 3. 加密
byte[] ciphertext = cipher.doFinal(plaintext);
// 4. 拼接 IV 和 Ciphertext (IV 不需要保密,但必须和密文一起传输)
byte[] encryptedBytes = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, encryptedBytes, 0, iv.length);
System.arraycopy(ciphertext, 0, encryptedBytes, iv.length, ciphertext.length);
return Base64.getEncoder().encodeToString(encryptedBytes);
}
public static String decrypt(String encryptedStr, byte[] key) throws Exception {
// 1. 解码 Base64
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedStr);
// 2. 分离 IV 和 Ciphertext
byte[] iv = new byte[GCM_IV_LENGTH_BYTES];
System.arraycopy(encryptedBytes, 0, iv, 0, iv.length);
byte[] ciphertext = new byte[encryptedBytes.length – GCM_IV_LENGTH_BYTES];
System.arraycopy(encryptedBytes, iv.length, ciphertext, 0, ciphertext.length);
// 3. 初始化 Cipher 解密
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH_BITS, iv);
Cipher cipher = Cipher.getInstance(AES_GCM_NO_PADDING);
cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmSpec);
// 4. 解密 (如果被篡改,此处会抛出 AEADBadTagException)
byte[] plaintext = cipher.doFinal(ciphertext);
return new String(plaintext);
}
public static void main(String[] args) throws Exception {
byte[] key = new byte[32]; // AES-256 Key (在实际应用中请从 KMS 获取)
new SecureRandom().nextBytes(key);
String msg = "Top Secret Message";
String encrypted = encrypt(msg.getBytes(), key);
System.out.println("Encrypted: " + encrypted);
String decrypted = decrypt(encrypted, key);
System.out.println("Decrypted: " + decrypted);
}
}
JavaScript (Node.js)
Node.js 内置 crypto 模块处理 GCM 模式非常方便。
const crypto = require('crypto');
const ALGO_NAME = 'aes-256-gcm';
const IV_LENGTH = 12; // 12 bytes
const KEY_LENGTH = 32; // 32 bytes for AES-256
function encrypt(text, key) {
// 1. 生成随机 IV
const iv = crypto.randomBytes(IV_LENGTH);
// 2. 创建加密器
const cipher = crypto.createCipheriv(ALGO_NAME, key, iv);
// 3. 加密 (Buffer.concat 拼接)
let encrypted = cipher.update(text, 'utf8');
encrypted = Buffer.concat([encrypted, cipher.final()]);
// 4. 获取 Auth Tag (必须随密文发送)
const authTag = cipher.getAuthTag();
// 5. 返回: IV + AuthTag + Ciphertext (通常拼接后转 Base64)
return Buffer.concat([iv, authTag, encrypted]).toString('base64');
}
function decrypt(encryptedData, key) {
// 1. 解码 Base64
const buffer = Buffer.from(encryptedData, 'base64');
// 2. 提取 IV, AuthTag, Ciphertext
const iv = buffer.subarray(0, IV_LENGTH);
const authTag = buffer.subarray(IV_LENGTH, IV_LENGTH + 16); // GCM Tag 默认 16 bytes
const ciphertext = buffer.subarray(IV_LENGTH + 16);
// 3. 创建解密器并设置 AuthTag (验证数据完整性的关键)
const decipher = crypto.createDecipheriv(ALGO_NAME, key, iv);
decipher.setAuthTag(authTag);
// 4. 解密
let decrypted = decipher.update(ciphertext);
decrypted = Buffer.concat([decrypted, decipher.final()]);
return decrypted.toString('utf8');
}
// 使用示例
const key = crypto.randomBytes(KEY_LENGTH); // 生成随机密钥
const msg = "Top Secret Message";
const encrypted = encrypt(msg, key);
console.log("Encrypted:", encrypted);
const decrypted = decrypt(encrypted, key);
console.log("Decrypted:", decrypted);
3. 非对称加密:RSA 的数理逻辑
RSA 的安全性依赖于大整数分解难题。
3.1 RSA 密钥生成流程图
下图展示了从选择质数到生成公私钥对的完整数学逻辑流程:
#mermaid-svg-Qj4iHTSCz8HKwhBq{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Qj4iHTSCz8HKwhBq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Qj4iHTSCz8HKwhBq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Qj4iHTSCz8HKwhBq .error-icon{fill:#552222;}#mermaid-svg-Qj4iHTSCz8HKwhBq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Qj4iHTSCz8HKwhBq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Qj4iHTSCz8HKwhBq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Qj4iHTSCz8HKwhBq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Qj4iHTSCz8HKwhBq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Qj4iHTSCz8HKwhBq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Qj4iHTSCz8HKwhBq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Qj4iHTSCz8HKwhBq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Qj4iHTSCz8HKwhBq .marker.cross{stroke:#333333;}#mermaid-svg-Qj4iHTSCz8HKwhBq svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Qj4iHTSCz8HKwhBq p{margin:0;}#mermaid-svg-Qj4iHTSCz8HKwhBq .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-Qj4iHTSCz8HKwhBq .cluster-label text{fill:#333;}#mermaid-svg-Qj4iHTSCz8HKwhBq .cluster-label span{color:#333;}#mermaid-svg-Qj4iHTSCz8HKwhBq .cluster-label span p{background-color:transparent;}#mermaid-svg-Qj4iHTSCz8HKwhBq .label text,#mermaid-svg-Qj4iHTSCz8HKwhBq span{fill:#333;color:#333;}#mermaid-svg-Qj4iHTSCz8HKwhBq .node rect,#mermaid-svg-Qj4iHTSCz8HKwhBq .node circle,#mermaid-svg-Qj4iHTSCz8HKwhBq .node ellipse,#mermaid-svg-Qj4iHTSCz8HKwhBq .node polygon,#mermaid-svg-Qj4iHTSCz8HKwhBq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Qj4iHTSCz8HKwhBq .rough-node .label text,#mermaid-svg-Qj4iHTSCz8HKwhBq .node .label text,#mermaid-svg-Qj4iHTSCz8HKwhBq .image-shape .label,#mermaid-svg-Qj4iHTSCz8HKwhBq .icon-shape .label{text-anchor:middle;}#mermaid-svg-Qj4iHTSCz8HKwhBq .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Qj4iHTSCz8HKwhBq .rough-node .label,#mermaid-svg-Qj4iHTSCz8HKwhBq .node .label,#mermaid-svg-Qj4iHTSCz8HKwhBq .image-shape .label,#mermaid-svg-Qj4iHTSCz8HKwhBq .icon-shape .label{text-align:center;}#mermaid-svg-Qj4iHTSCz8HKwhBq .node.clickable{cursor:pointer;}#mermaid-svg-Qj4iHTSCz8HKwhBq .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Qj4iHTSCz8HKwhBq .arrowheadPath{fill:#333333;}#mermaid-svg-Qj4iHTSCz8HKwhBq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Qj4iHTSCz8HKwhBq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Qj4iHTSCz8HKwhBq .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Qj4iHTSCz8HKwhBq .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Qj4iHTSCz8HKwhBq .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Qj4iHTSCz8HKwhBq .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Qj4iHTSCz8HKwhBq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Qj4iHTSCz8HKwhBq .cluster text{fill:#333;}#mermaid-svg-Qj4iHTSCz8HKwhBq .cluster span{color:#333;}#mermaid-svg-Qj4iHTSCz8HKwhBq div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Qj4iHTSCz8HKwhBq .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Qj4iHTSCz8HKwhBq rect.text{fill:none;stroke-width:0;}#mermaid-svg-Qj4iHTSCz8HKwhBq .icon-shape,#mermaid-svg-Qj4iHTSCz8HKwhBq .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Qj4iHTSCz8HKwhBq .icon-shape p,#mermaid-svg-Qj4iHTSCz8HKwhBq .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Qj4iHTSCz8HKwhBq .icon-shape rect,#mermaid-svg-Qj4iHTSCz8HKwhBq .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Qj4iHTSCz8HKwhBq .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Qj4iHTSCz8HKwhBq .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Qj4iHTSCz8HKwhBq :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
开始: 生成密钥对
随机选择两个大质数 p, q
计算模数 n = p * q
计算欧拉函数 phi = p-1 * q-1
选择公钥指数 e条件: 1 < e < phi 且互质
计算私钥指数 d条件: e * d ≡ 1 mod phi
生成结果
公钥n, e
私钥n, d
3.2 填充的重要性:OAEP
直接用 RSA 加密数据(教科书式 RSA)是极其危险的。必须使用填充。OAEP (Optimal Asymmetric Encryption Padding) 是目前的标准。
为何需要 OAEP? 没有填充的 RSA 是确定性的(相同的明文永远得到相同的密文),且容易满足特定数学结构导致被破解。OAEP 引入了随机性,使加密变成概率性的。
3.3 实战:RSA-OAEP 加密与解密
注意:RSA 速度慢,通常只用来加密对称密钥,不直接加密大文件。
Java (JDK 11+)
使用 RSA/ECB/OAEPWithSHA-256AndMGF1Padding。
import javax.crypto.Cipher;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.util.Base64;
public class RsaOaepExample {
public static void main(String[] args) throws Exception {
// 1. 生成 RSA 密钥对 (2048位)
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048, new SecureRandom());
KeyPair keyPair = keyGen.generateKeyPair();
String message = "Here is the AES Key: 1234567890123456";
// 2. 加密 (使用公钥)
// OAEPWithSHA-256AndMGF1Padding 是最安全的配置之一
Cipher encryptCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
encryptCipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
byte[] encryptedBytes = encryptCipher.doFinal(message.getBytes());
System.out.println("Encrypted: " + Base64.getEncoder().encodeToString(encryptedBytes));
// 3. 解密 (使用私钥)
Cipher decryptCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
decryptCipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
byte[] decryptedBytes = decryptCipher.doFinal(encryptedBytes);
System.out.println("Decrypted: " + new String(decryptedBytes));
}
}
JavaScript (Node.js)
使用 crypto.publicEncrypt 和 oaepHash 选项。
const crypto = require('crypto');
// 1. 生成密钥对
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
});
const message = "Here is the AES Key: 1234567890123456";
// 2. 加密 (使用公钥)
const encrypted = crypto.publicEncrypt(
{
key: publicKey,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: "sha256" // 指定 OAEP 的 Hash 算法
},
Buffer.from(message)
);
console.log("Encrypted (len):", encrypted.length);
// 3. 解密 (使用私钥)
const decrypted = crypto.privateDecrypt(
{
key: privateKey,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: "sha256"
},
encrypted
);
console.log("Decrypted:", decrypted.toString());
4. 数字签名:身份验证
数字签名是“私钥加密哈希”的现代严谨说法(实际操作略有不同,常用 PSS 填充)。
4.1 签名生成与验证流程
下图展示了发送方如何使用私钥签名,接收方如何使用公钥验证身份和完整性:
接收方
发送方
接收方
发送方
#mermaid-svg-vOOPKjB8LPvwtdBP{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vOOPKjB8LPvwtdBP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vOOPKjB8LPvwtdBP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vOOPKjB8LPvwtdBP .error-icon{fill:#552222;}#mermaid-svg-vOOPKjB8LPvwtdBP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vOOPKjB8LPvwtdBP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vOOPKjB8LPvwtdBP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vOOPKjB8LPvwtdBP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vOOPKjB8LPvwtdBP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vOOPKjB8LPvwtdBP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vOOPKjB8LPvwtdBP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vOOPKjB8LPvwtdBP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vOOPKjB8LPvwtdBP .marker.cross{stroke:#333333;}#mermaid-svg-vOOPKjB8LPvwtdBP svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vOOPKjB8LPvwtdBP p{margin:0;}#mermaid-svg-vOOPKjB8LPvwtdBP .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-vOOPKjB8LPvwtdBP text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-vOOPKjB8LPvwtdBP .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-vOOPKjB8LPvwtdBP .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-vOOPKjB8LPvwtdBP .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-vOOPKjB8LPvwtdBP .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-vOOPKjB8LPvwtdBP #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-vOOPKjB8LPvwtdBP .sequenceNumber{fill:white;}#mermaid-svg-vOOPKjB8LPvwtdBP #sequencenumber{fill:#333;}#mermaid-svg-vOOPKjB8LPvwtdBP #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-vOOPKjB8LPvwtdBP .messageText{fill:#333;stroke:none;}#mermaid-svg-vOOPKjB8LPvwtdBP .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-vOOPKjB8LPvwtdBP .labelText,#mermaid-svg-vOOPKjB8LPvwtdBP .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-vOOPKjB8LPvwtdBP .loopText,#mermaid-svg-vOOPKjB8LPvwtdBP .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-vOOPKjB8LPvwtdBP .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-vOOPKjB8LPvwtdBP .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-vOOPKjB8LPvwtdBP .noteText,#mermaid-svg-vOOPKjB8LPvwtdBP .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-vOOPKjB8LPvwtdBP .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-vOOPKjB8LPvwtdBP .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-vOOPKjB8LPvwtdBP .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-vOOPKjB8LPvwtdBP .actorPopupMenu{position:absolute;}#mermaid-svg-vOOPKjB8LPvwtdBP .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-vOOPKjB8LPvwtdBP .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-vOOPKjB8LPvwtdBP .actor-man circle,#mermaid-svg-vOOPKjB8LPvwtdBP line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-vOOPKjB8LPvwtdBP :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
1. 准备数据
2. 验证数据
alt
[Hash_A == Hash_B]
[Hash_A != Hash_B]
计算原始消息的哈希值 Hash_A
使用私钥加密 Hash_A 生成签名 Signature
发送 [原始消息] + [数字签名]
计算收到消息的哈希值 Hash_B
使用公钥解密签名 得到 Hash_A
✅ 验证通过
(数据完整且来源真实)
❌ 验证失败
(数据已篡改或来源伪造)
4.2 实战:RSASSA-PSS 签名
Java 11+ 和 Node.js 均原生支持 PSS 填充,这是比 PKCS#1 v1.5 更安全的签名方案。
Java (JDK 11+)
import java.security.*;
import java.util.Base64;
public class RsaPssExample {
public static void main(String[] args) throws Exception {
// 1. 生成密钥对
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair keyPair = keyGen.generateKeyPair();
String message = "Important contract content";
// 2. 签名 (使用私钥)
// RSASSA-PSS 配置
Signature signature = Signature.getInstance("RSASSA-PSS");
signature.setParameter(new PSSParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 32, 1));
signature.initSign(keyPair.getPrivate());
signature.update(message.getBytes());
byte[] signatureBytes = signature.sign();
System.out.println("Signature: " + Base64.getEncoder().encodeToString(signatureBytes));
// 3. 验证 (使用公钥)
Signature verifier = Signature.getInstance("RSASSA-PSS");
verifier.setParameter(new PSSParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 32, 1));
verifier.initVerify(keyPair.getPublic());
verifier.update(message.getBytes());
boolean isValid = verifier.verify(signatureBytes);
System.out.println("Signature valid? " + isValid);
}
}
JavaScript (Node.js)
const crypto = require('crypto');
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
});
const message = "Important contract content";
// 1. 签名
const sign = crypto.createSign('sha256');
sign.update(message);
sign.end();
const signature = sign.sign({
key: privateKey,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST // 盐长度等于摘要长度
});
console.log("Signature (len):", signature.length);
// 2. 验证
const verify = crypto.createVerify('sha256');
verify.update(message);
verify.end();
const isValid = verify.verify({
key: publicKey,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST
}, signature);
console.log("Signature valid?", isValid);
5. 混合加密实战:HTTPS (TLS 1.2) 握手全流程
这是现代互联网安全的基础,结合了非对称加密(交换密钥)和对称加密(传输数据)。
Server
Client
Server
Client
#mermaid-svg-qUjZMiFjFivAdlFe{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-qUjZMiFjFivAdlFe .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qUjZMiFjFivAdlFe .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qUjZMiFjFivAdlFe .error-icon{fill:#552222;}#mermaid-svg-qUjZMiFjFivAdlFe .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qUjZMiFjFivAdlFe .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qUjZMiFjFivAdlFe .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qUjZMiFjFivAdlFe .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qUjZMiFjFivAdlFe .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qUjZMiFjFivAdlFe .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qUjZMiFjFivAdlFe .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qUjZMiFjFivAdlFe .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qUjZMiFjFivAdlFe .marker.cross{stroke:#333333;}#mermaid-svg-qUjZMiFjFivAdlFe svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qUjZMiFjFivAdlFe p{margin:0;}#mermaid-svg-qUjZMiFjFivAdlFe .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-qUjZMiFjFivAdlFe text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-qUjZMiFjFivAdlFe .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-qUjZMiFjFivAdlFe .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-qUjZMiFjFivAdlFe .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-qUjZMiFjFivAdlFe .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-qUjZMiFjFivAdlFe #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-qUjZMiFjFivAdlFe .sequenceNumber{fill:white;}#mermaid-svg-qUjZMiFjFivAdlFe #sequencenumber{fill:#333;}#mermaid-svg-qUjZMiFjFivAdlFe #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-qUjZMiFjFivAdlFe .messageText{fill:#333;stroke:none;}#mermaid-svg-qUjZMiFjFivAdlFe .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-qUjZMiFjFivAdlFe .labelText,#mermaid-svg-qUjZMiFjFivAdlFe .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-qUjZMiFjFivAdlFe .loopText,#mermaid-svg-qUjZMiFjFivAdlFe .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-qUjZMiFjFivAdlFe .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-qUjZMiFjFivAdlFe .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-qUjZMiFjFivAdlFe .noteText,#mermaid-svg-qUjZMiFjFivAdlFe .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-qUjZMiFjFivAdlFe .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-qUjZMiFjFivAdlFe .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-qUjZMiFjFivAdlFe .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-qUjZMiFjFivAdlFe .actorPopupMenu{position:absolute;}#mermaid-svg-qUjZMiFjFivAdlFe .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-qUjZMiFjFivAdlFe .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-qUjZMiFjFivAdlFe .actor-man circle,#mermaid-svg-qUjZMiFjFivAdlFe line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-qUjZMiFjFivAdlFe :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
5. 验证证书 & 生成预主密钥
7. [使用私钥解密] 得到预主密钥
8. 双方共同计算
会话密钥 = PRF(随机数C + 随机数S + 预主密钥)
======== 建立安全通道 ========
1. ClientHello
(支持的加密套件, 随机数C)
2. ServerHello
(选定加密套件, 随机数S)
3. Certificate
(包含服务器公钥)
4. ServerHelloDone
6. [使用公钥加密] 预主密钥
9. ChangeCipherSpec
通知后续加密
10. Finished
(使用会话密钥加密的校验消息)
11. ChangeCipherSpec
12. Finished
[使用 AES/会话密钥] 加密的应用数据
[使用 AES/会话密钥] 加密的应用数据
注:TLS 1.3 对此进行了优化,移除了 RSA 密钥交换,强制使用 ECDHE,仅需 1-RTT(往返延迟)。
5.1 实战:使用 OpenSSL 分析 HTTPS 握手
作为开发者,你可以使用命令行工具验证上述流程(无论你用 Java 还是 JS,底层的 TLS 逻辑都是一样的):
# 查看网站证书信息
openssl s_client -connect google.com:443 -showcerts
# 查看协商出的加密套件和会话密钥参数(需开启 SSLKEYLOGFILE)
# 环境变量: export SSLKEYLOGFILE=~/keys.log
# 然后使用 Wireshark 打开 keys.log 即可解密 HTTPS 流量查看明文
6. 密码存储与哈希:PBKDF2 实战
千万不要用 MD5 存密码!请使用 KDF (Key Derivation Function)。
Java (JDK 8+)
使用 SecretKeyFactory 和 PBKDF2WithHmacSHA256。
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.SecureRandom;
import java.util.Base64;
public class PasswordHashing {
private static final int ITERATIONS = 100000;
private static final int KEY_LENGTH = 256; // bits
private static final int SALT_LENGTH = 16; // bytes
private static final String ALGO = "PBKDF2WithHmacSHA256";
public static String hashPassword(String password) throws Exception {
// 1. 生成随机盐
SecureRandom random = new SecureRandom();
byte[] salt = new byte[SALT_LENGTH];
random.nextBytes(salt);
// 2. 计算哈希
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, KEY_LENGTH);
SecretKeyFactory factory = SecretKeyFactory.getInstance(ALGO);
byte[] hash = factory.generateSecret(spec).getEncoded();
// 3. 存储: salt + hash (转 Hex)
return bytesToHex(salt) + ":" + bytesToHex(hash);
}
public static boolean verifyPassword(String storedHash, String inputPassword) throws Exception {
String[] parts = storedHash.split(":");
byte[] salt = hexToBytes(parts[0]);
byte[] storedHashBytes = hexToBytes(parts[1]);
// 4. 使用同样的盐计算输入密码的哈希
PBEKeySpec spec = new PBEKeySpec(inputPassword.toCharArray(), salt, ITERATIONS, KEY_LENGTH);
SecretKeyFactory factory = SecretKeyFactory.getInstance(ALGO);
byte[] inputHashBytes = factory.generateSecret(spec).getEncoded();
// 5. 对比
return slowEquals(storedHashBytes, inputHashBytes);
}
// 辅助:防时序攻击比较
private static boolean slowEquals(byte[] a, byte[] b) {
int diff = a.length ^ b.length;
for(int i = 0; i < a.length && i < b.length; i++) {
diff |= a[i] ^ b[i];
}
return diff == 0;
}
private static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) sb.append(String.format("%02x", b));
return sb.toString();
}
private static byte[] hexToBytes(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
}
JavaScript (Node.js)
使用 crypto.pbkdf2 (Async/Promise 版本)。
const crypto = require('crypto');
const ITERATIONS = 100000;
const KEY_LENGTH = 32; // 32 bytes = 256 bits
const DIGEST = 'sha256';
const SALT_LENGTH = 16;
// 使用 Promise 包装异步操作
function hashPassword(password) {
return new Promise((resolve, reject) => {
const salt = crypto.randomBytes(SALT_LENGTH);
crypto.pbkdf2(password, salt, ITERATIONS, KEY_LENGTH, DIGEST, (err, derivedKey) => {
if (err) return reject(err);
// 存储: salt + hash (转 hex)
const stored = salt.toString('hex') + ':' + derivedKey.toString('hex');
resolve(stored);
});
});
}
function verifyPassword(storedHash, password) {
return new Promise((resolve, reject) => {
const [saltHex, hashHex] = storedHash.split(':');
const salt = Buffer.from(saltHex, 'hex');
crypto.pbkdf2(password, salt, ITERATIONS, KEY_LENGTH, DIGEST, (err, derivedKey) => {
if (err) return reject(err);
const derivedKeyHex = derivedKey.toString('hex');
// 使用 crypto.timingSafeEqual 防止时序攻击
const keyBuffer = Buffer.from(hashHex, 'hex');
const derivedBuffer = Buffer.from(derivedKeyHex, 'hex');
if (keyBuffer.length !== derivedBuffer.length) {
return resolve(false);
}
try {
const match = crypto.timingSafeEqual(keyBuffer, derivedBuffer);
resolve(match);
} catch (e) {
resolve(false);
}
});
});
}
// 使用示例
(async () => {
const pass = "my_secure_pass";
const hash = await hashPassword(pass);
console.log("Stored in DB:", hash);
const isValid = await verifyPassword(hash, pass);
console.log("Check result:", isValid);
})();
7. 技术深度补充:常见攻击与防御
为了达到“精通”,必须了解敌人如何攻击。
| 重放攻击 | 通信协议 | 攻击者截获有效的数据包(如转账请求),再次发送。 | 使用 Nonce (随机数) 或 Timestamp (时间戳)。GCM 模式的 Nonce 必须唯一。 |
| 中间人攻击 (MITM) | RSA/TLS | 攻击者冒充服务器,发送自己的公钥给客户端。 | 验证 数字证书链,必须由受信任的 CA 签发。 |
| 填充预言攻击 | RSA-CBC | 攻击者根据解密时的报错信息(Padding Error)逐步推断明文。 | 解密时不立即报错,使用恒定时间算法;改用 RSA-OAEP。 |
| 侧信道攻击 | 硬件/AES | 通过分析 CPU 执行时间、功耗或电磁辐射来推断密钥。 | 物理隔离;使用恒定时间实现的代码库。 |
8. 总结:现代安全架构设计图
如果你正在设计一个安全系统,请遵循以下垂直架构层级,每一层都依赖下一层的安全支撑:
#mermaid-svg-BA5q5INYmRGi76xl{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BA5q5INYmRGi76xl .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BA5q5INYmRGi76xl .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BA5q5INYmRGi76xl .error-icon{fill:#552222;}#mermaid-svg-BA5q5INYmRGi76xl .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BA5q5INYmRGi76xl .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BA5q5INYmRGi76xl .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BA5q5INYmRGi76xl .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BA5q5INYmRGi76xl .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BA5q5INYmRGi76xl .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BA5q5INYmRGi76xl .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BA5q5INYmRGi76xl .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BA5q5INYmRGi76xl .marker.cross{stroke:#333333;}#mermaid-svg-BA5q5INYmRGi76xl svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BA5q5INYmRGi76xl p{margin:0;}#mermaid-svg-BA5q5INYmRGi76xl .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-BA5q5INYmRGi76xl .cluster-label text{fill:#333;}#mermaid-svg-BA5q5INYmRGi76xl .cluster-label span{color:#333;}#mermaid-svg-BA5q5INYmRGi76xl .cluster-label span p{background-color:transparent;}#mermaid-svg-BA5q5INYmRGi76xl .label text,#mermaid-svg-BA5q5INYmRGi76xl span{fill:#333;color:#333;}#mermaid-svg-BA5q5INYmRGi76xl .node rect,#mermaid-svg-BA5q5INYmRGi76xl .node circle,#mermaid-svg-BA5q5INYmRGi76xl .node ellipse,#mermaid-svg-BA5q5INYmRGi76xl .node polygon,#mermaid-svg-BA5q5INYmRGi76xl .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BA5q5INYmRGi76xl .rough-node .label text,#mermaid-svg-BA5q5INYmRGi76xl .node .label text,#mermaid-svg-BA5q5INYmRGi76xl .image-shape .label,#mermaid-svg-BA5q5INYmRGi76xl .icon-shape .label{text-anchor:middle;}#mermaid-svg-BA5q5INYmRGi76xl .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-BA5q5INYmRGi76xl .rough-node .label,#mermaid-svg-BA5q5INYmRGi76xl .node .label,#mermaid-svg-BA5q5INYmRGi76xl .image-shape .label,#mermaid-svg-BA5q5INYmRGi76xl .icon-shape .label{text-align:center;}#mermaid-svg-BA5q5INYmRGi76xl .node.clickable{cursor:pointer;}#mermaid-svg-BA5q5INYmRGi76xl .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-BA5q5INYmRGi76xl .arrowheadPath{fill:#333333;}#mermaid-svg-BA5q5INYmRGi76xl .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BA5q5INYmRGi76xl .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BA5q5INYmRGi76xl .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BA5q5INYmRGi76xl .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-BA5q5INYmRGi76xl .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BA5q5INYmRGi76xl .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-BA5q5INYmRGi76xl .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BA5q5INYmRGi76xl .cluster text{fill:#333;}#mermaid-svg-BA5q5INYmRGi76xl .cluster span{color:#333;}#mermaid-svg-BA5q5INYmRGi76xl div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-BA5q5INYmRGi76xl .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-BA5q5INYmRGi76xl rect.text{fill:none;stroke-width:0;}#mermaid-svg-BA5q5INYmRGi76xl .icon-shape,#mermaid-svg-BA5q5INYmRGi76xl .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BA5q5INYmRGi76xl .icon-shape p,#mermaid-svg-BA5q5INYmRGi76xl .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-BA5q5INYmRGi76xl .icon-shape rect,#mermaid-svg-BA5q5INYmRGi76xl .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BA5q5INYmRGi76xl .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-BA5q5INYmRGi76xl .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-BA5q5INYmRGi76xl :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
密钥管理 Key Management
存储安全 Storage Security
传输安全 Transport Layer
接口安全 API Security
应用层 Application Layer
业务逻辑
敏感数据处理
输入验证
参数清洗
TLS 1.3 / HTTPS
数据库加密 AES-256
密码存储 Argon2/PBKDF2
非硬编码密钥
KMS / HSM / Vault
精通之路:理解上述每一个箭头和模块背后的数学原理与工程妥协,并有能力在性能、安全性和易用性之间找到最佳的平衡点。
网硕互联帮助中心




评论前必须登录!
注册