什么是密码学?
密码学是保护信息安全的科学,它通过加密技术 将可读的信息(明文)转换为不可读的形式(密文),只有授权方才能解密恢复原始内容。就像给信息上了一把"数字锁 ",只有拥有正确"钥匙"的人才能打开
。
一、古典密码学:密码学的起源
古典密码学主要靠 "替换" 和 "移位",原理简单,适合手动计算,常见于战争、书信保密。
1.1 核心原理
① 替换法 - 用固定规则替换原文中的字符
示例:bee
b → w, e → p
密文:wpp
分类:
-
单表替换
:所有字符使用同一张替换表原始:abcde
替换:swtrp
缺点:容易被破解 ------ 比如英文中 "e" 出现频率最高,只要统计密文中哪个字符出现最多,大概率就是 "e" 的替换。
-
多表替换
:使用多张替换表,按密钥轮换使用表1:abcde → swtrp
表2:abcde → chfhk
表3:abcde → jftou原文:bee
密钥:312(表示用表3、表1、表2)
密文:fpk
优点:比单表安全,破解难度高很多。
② 移位法 - 按字母表位置移动字符
把字母表按固定位数 "平移",最经典的是 "凯撒加密"(凯撒大帝用来给军队发命令)。
凯撒加密示例:
原始:abcde
后移2位:cdefg
"hello" → "jgnnq"
缺点:移位位数有限(最多 25 位),暴力尝试几次就能破解。
1.2 古典密码的破解
频率分析法:利用字母出现的统计规律
- 英文中'e'出现频率最高(约12.7%)
- 't', 'a', 'o'等也有明显特征
- 通过分析密文字母频率推测替换规则
历史名机 :恩尼格玛密码机
(二战时期德国使用,后被图灵破解)
二、现代密码学三大支柱
现代密码学不再靠人工 / 简单机器,而是靠数学算法,能应对海量数据、高并发场景,核心分 3 类:散列函数、对称加密、非对称加密。
2.1 散列函数(哈希函数):"不可逆的指纹"
散列函数能把任意长度的明文,变成固定长度、不可逆的密文 (叫 "哈希值
" 或 "消息摘要
"),就像给信息盖 "唯一指纹"。
核心特点:
- 不可逆:知道哈希值,无法反推明文(比如知道 "123" 的 MD5 是 "202cb962ac59075b964b07152d234b70",没法反推 "123");
- 唯一性:不同明文(除非刻意构造 "碰撞")的哈希值一定不同;
- 固定长度:不管明文是 1 字节还是 1GB,哈希值长度固定(比如 MD5 是 32 位十六进制,SHA-256 是 64 位十六进制)。
常见算法及用途:
算法 | 哈希值长度 | 用途 | 例子(明文 "123") |
---|---|---|---|
MD5 | 32 位十六进制 | 文件校验、密码存储(已逐渐被淘汰,易碰撞) | 202cb962ac59075b964b07152d234b70 |
SHA-1(Secure Hash Algorithm) | 40 位十六进制 | 早期 Git 版本控制(已淘汰) | 40bd001563085fc35165329ea1ff5c5ecbdbbeef |
SHA-256 | 64 位十六进制 | 现在主流(文件校验、区块链、密码存储) | a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3 |
实战场景:
- 网站存密码:不会存明文 "123456",而是存它的 SHA-256 哈希值,登录时比对哈希值(即使数据库泄露,黑客也拿不到明文密码);
- 下载软件校验:官方给出安装包的 SHA-256 值,你下载后算一遍,如果一致,说明没被篡改。
Java 代码示例(计算 MD5):
bash
import java.security.MessageDigest;
public class HashDemo {
public static void main(String[] args) throws Exception {
String input = "123"; // 明文
// 1. 获取MD5算法实例
MessageDigest md = MessageDigest.getInstance("MD5");
// 2. 计算哈希值(字节数组)
byte[] hashBytes = md.digest(input.getBytes());
// 3. 转成16进制字符串(方便查看)
StringBuilder sb = new StringBuilder();
for (byte b : hashBytes) {
sb.append(String.format("%02x", b)); // %02x表示补0成2位十六进制
}
System.out.println("MD5哈希值:" + sb.toString()); // 输出:202cb962ac59075b964b07152d234b70
}
}
2.2 对称加密:同一把钥匙
加密和解密用同一把密钥,就像你家的门,钥匙既能锁门也能开门。特点是 "快",适合加密大量数据。
(1)常见算法对比:
算法 | 密钥长度 | 特点 | 用途 |
---|---|---|---|
DES | 8 字节(64 位,含 1 位校验) | 早期算法,安全性低(已淘汰) | 旧系统兼容 |
3DES | 24 字节(3 个 DES 密钥) | 对 DES 三次加密,安全性提高,但速度慢 | 旧金融系统 |
AES | 16/192/256 字节(推荐 16 字节) | 现在主流,速度快、安全性高 | 手机支付、文件加密、HTTPS 数据传输 |
技术分类:
- 流加密 :逐位加密,如RC4
123456789 → 先加密1,再加密2,再加密3... - 块加密 :分组加密,如AES
12345678 → 分成[1234]和[5678]分别加密
特点:- ✅ 加密速度快
- ✅ 适合大数据量加密
- ❌ 密钥分发和管理困难
- ❌ 无法实现数字签名
(2)关键概念:加密模式 + 填充模式
对称加密按 "块" 处理数据(比如 AES 每次处理 16 字节),需要解决 "数据不够块长" 和 "相同明文出相同密文" 的问题:
① 加密模式:决定怎么处理多块数据
模式 | 特点 | 优点 | 缺点 |
---|---|---|---|
ECB | 每块独立加密,相同明文块出相同密文块 | 并行处理,速度快 | 不安全(容易被破解规律) |
CBC | 每块先和前一块密文 "异或",再加密 | 相同明文出不同密文,安全 | 串行处理,速度稍慢(需要初始向量 IV) |
例子:加密 "abcabcabc"(AES-16 字节,ECB vs CBC)
ECB
:两个 "abc" 块加密后密文相同;CBC
:第一个 "abc" 用 IV 加密,第二个 "abc" 和第一个密文异或后加密,密文不同。
推荐:用 CBC 模式,更安全。
② 填充模式:解决 "数据不够块长"
如果数据长度不是块长的整数倍(比如 AES-16 字节,数据只有 10 字节),需要 "补满":
NoPadding
:不填充,要求数据必须是块长的整数倍(否则报错);PKCS5Padding
:缺 n 字节就补 n 个 "n"(比如缺 6 字节,补 6 个 "\x06"),最常用。
(3)实战注意:结合 Base64
对称加密的密文是 "字节数组"(比如[65, -12, 34]),直接传输会乱码,所以通常用Base64 编码
转成字符串(比如 "QT4i")。
⚠️ 注意:Base64 是编码不是加密,目的是 "方便传输",不是 "保密"!
(4)Java 代码示例(AES-CBC 加密):
bash
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
public class AesDemo {
// AES密钥(16字节)、初始向量IV(16字节,CBC模式必须)
private static final String KEY = "1234567890abcdef";
private static final String IV = "abcdef1234567890";
// 加密:明文→密文(Base64字符串)
public static String encrypt(String plaintext) throws Exception {
// 1. 生成密钥和IV
SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes(), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(IV.getBytes());
// 2. 初始化Cipher(CBC模式+PKCS5Padding)
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
// 3. 加密→Base64编码
byte[] cipherBytes = cipher.doFinal(plaintext.getBytes());
return Base64.getEncoder().encodeToString(cipherBytes);
}
// 解密:密文(Base64字符串)→明文
public static String decrypt(String ciphertext) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes(), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(IV.getBytes());
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
// 4. Base64解码→解密
byte[] plainBytes = cipher.doFinal(Base64.getDecoder().decode(ciphertext));
return new String(plainBytes);
}
public static void main(String[] args) throws Exception {
String plaintext = "我是秘密消息";
// 加密
String ciphertext = encrypt(plaintext);
System.out.println("加密后(Base64):" + ciphertext); // 比如:xY+...(长字符串)
// 解密
String decrypted = decrypt(ciphertext);
System.out.println("解密后:" + decrypted); // 输出:我是秘密消息
}
}
3. 非对称加密:"公钥加密,私钥解密"
用一对密钥(公钥 + 私钥):公钥可以公开(像地址一样分享给别人),私钥自己保存(绝对不能泄露)。特点是 "安全",但速度慢,适合加密少量数据(比如密钥)。
(1)核心规则:
- 公钥加密的密文,只有对应的私钥能解密;
- 私钥加密的密文(叫 "数字签名"),只有对应的公钥能解密;
- 公钥由私钥生成,无法从公钥反推私钥。
(2)常见算法对比:
算法 | 密钥长度 | 特点 | 用途 |
---|---|---|---|
RSA |
1024/2048/4096 位(推荐 2048 位) | 兼容性好,应用广 | 数字签名、密钥交换(HTTPS) |
ECC |
160/256 位 | 相同安全级下,密钥更短、速度更快 | 移动端加密、区块链(比特币用 ECC) |
DSA |
1024 位 | 只用于数字签名,不能加密数据 | 身份验证 |
(3)实战场景:HTTPS 的 "密钥交换"**
HTTPS 为什么安全?
因为它结合了 "对称 + 非对称" 加密:
- 浏览器向服务器要 "公钥";
- 浏览器用公钥加密 "对称密钥"(比如 AES 密钥),发给服务器;
- 服务器用私钥解密,拿到对称密钥;
- 后续数据传输,都用对称密钥加密(快)。
------ 既解决了对称加密 "密钥难传输" 的问题,又解决了非对称加密 "慢" 的问题。
(4)Java 代码示例(RSA 生成密钥对 + 加密):
bash
import javax.crypto.Cipher;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
public class RsaDemo {
public static void main(String[] args) throws Exception {
String algorithm = "RSA";
String plaintext = "要传输的AES密钥:1234567890abcdef";
// 1. 生成RSA密钥对(公钥+私钥)
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(algorithm);
keyPairGen.initialize(2048); // 密钥长度2048位
KeyPair keyPair = keyPairGen.generateKeyPair();
PublicKey publicKey = keyPair.getPublic(); // 公钥(可公开)
PrivateKey privateKey = keyPair.getPrivate(); // 私钥(自己存)
// 2. 打印公钥和私钥(Base64编码,方便查看)
System.out.println("公钥(Base64):" + Base64.getEncoder().encodeToString(publicKey.getEncoded()));
System.out.println("私钥(Base64):" + Base64.getEncoder().encodeToString(privateKey.getEncoded()));
// 3. 公钥加密(加密少量数据,比如对称密钥)
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes());
String encrypted = Base64.getEncoder().encodeToString(encryptedBytes);
System.out.println("公钥加密后:" + encrypted);
// 4. 私钥解密
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encrypted));
String decrypted = new String(decryptedBytes);
System.out.println("私钥解密后:" + decrypted); // 输出:要传输的AES密钥:1234567890abcdef
}
}
三、密码学实战指南
3.1 对称 vs 非对称:如何选择?
特性 | 对称加密 | 非对称加密 |
---|---|---|
速度 | 快(适合大数据) | 慢(适合小数据) |
密钥管理 | 复杂(密钥分发难) | 简单(公钥可公开) |
签名功能 | 不支持 | 支持 |
典型应用 | 文件加密、数据库加密 | 密钥交换、数字签名 |
最佳实践: 结合两者优势
- 用非对称加密安全传输对称加密的密钥
- 用对称加密快速加密实际数据
3.2 密钥长度建议
- RSA:至少2048位(1024位已不安全)
- ECC:256位(相当于RSA 3072位安全性)
- AES:128位(平衡安全与性能)
密钥越长越安全?
不一定!AES-128 位已经足够安全(破解需要的算力远超全球总和),更长的密钥(如 256 位)会增加性能消耗,没必要。
3.3 Base64:不是加密的编码
重要认知 : Base64不是加密算法!它只是 "字节→字符串
" 的编码方式,目的是让二进制数据能够用文本形式安全传输,"避免传输乱码"。
原理: 每3个字节(24位)转换为4个6位的Base64字符
- 1 字节 = 8 位,3 字节 = 24 位;
- 把 24 位分成 4 组,每组 6 位;
- 6 位最大是 63(2^6-1),对应 64 个字符(A-Z、a-z、0-9、+、/);
- 如果不足 3 字节,缺 1 字节补 1 个 "=",缺 2 字节补 2 个 "="。
例子:编码 "ab"(2 字节 = 16 位)
- 16 位→补 8 位 0→24 位,分成 4 组 6 位;
- 对应 Base64 字符:Y、W、I、=;
- 最终编码结果:"YWI="。
与Base58区别:
Base58(Base64 的 "简化版"):Base58 去掉了容易混淆的字符(0、O、1、l)和特殊符号(+、/),适合手写或肉眼识别,比如比特币地址
用 Base58 编码。
3.4 数字签名:网络世界的"手写签名"
数字签名用 "私钥签名,公钥验证
",解决 "信息被篡改" 和 "发件人抵赖" 的问题
作用:
- 身份认证:证明消息发送者的身份
- 完整性验证:确保消息未被篡改
- 不可否认:发送者不能否认发送过的消息
实现过程:
- 对消息计算哈希值
- 用私钥加密哈希值(这就是数字签名)
- 接收方用公钥解密并验证哈希值
比如软件发布:
- 软件开发者用私钥对 "软件哈希值" 签名(生成签名文件);
- 用户下载软件后,算软件的哈希值,并用开发者的公钥验证签名;
- 如果验证通过,说明软件没被篡改,且确实是开发者发布的。
四、实际开发注意事项
4.1 字符串处理陷阱
加密解密后,一定要用**new String()
**,别用toString()!
bash
// ❌ 错误方式 - 会输出哈希值而不是实际内容
byte[] data = "hello".getBytes();
System.out.println(data.toString()); // 输出: [B@1540e19d
// ✅ 正确方式 - 使用明确的字符编码
System.out.println(new String(data, "UTF-8")); // 输出: hello
4.2 现代密码学发展趋势
- 后量子密码学:抵抗量子计算机攻击的新算法
- 同态加密:在加密状态下直接进行计算
- 国密算法:中国自主研发的密码算法体系(SM2, SM3, SM4)
五、总结:现代密码学核心用法表
需求 | 推荐技术 | 关键点 |
---|---|---|
密码存储、文件校验 | SHA-256(散列函数) | 不可逆,固定长度 |
大量数据加密(文件、传输) | AES-CBC(对称加密)+ Base64 | 密钥 16 字节,用 CBC 模式 + PKCS5Padding |
密钥交换、数字签名 | RSA-2048/ECC-256(非对称加密) | 公钥公开,私钥保密 |
避免传输乱码 | Base64 | 不是加密,是编码 |
密码学就像网络世界的"安全卫士",从简单的字母替换发展到今天复杂的数学算法。它的核心不是 "搞懂复杂算法",而是 "选对工具"------ 比如加密文件用 AES,存密码用 SHA-256,传输密钥用 RSA。
记住黄金法则 :没有绝对的安全,只有相对的安全。 持续学习、及时更新加密方案才是真正的安全保障。