- 总览:加密、摘要与编码
- [Base64: 编码](#Base64: 编码)
- 是什么
- 能干嘛
- 怎么玩
- 小总结
- [MD5: 消息摘要](#MD5: 消息摘要)
- 是什么
- 能干嘛
- 常见的坑
- 怎么玩
- 小总结
- [RSA: 非对称加密](#RSA: 非对称加密)
- 是什么
- 核心思想
- 能干嘛
- 怎么玩
- 小总结
- [国密算法: SM2 & SM4](#国密算法: SM2 & SM4)
- 是什么
- 去哪下
- 怎么玩(SM4)
- 怎么玩(SM2)
- 小总结
- 整体学习建议
- 学习路径
- 持续学习资源
☰
密码学算法新手入门教程
导出为图片
总览:加密、摘要与编码
在进入具体算法之前,我们必须先分清三个核心概念:
| 概念 | 核心目的 | 类比 | 是否可逆 | 本文涉及的算法 |
|---|---|---|---|---|
| 编码 (Encoding) | 将数据从一种形式转换为另一种形式,以便于传输或处理。不为保密。 | 翻译官。把中文翻译成英文,内容不变,只是换了种表达方式。 | 可逆 (知道规则就能转回来) | Base64 |
| 摘要/哈希 (Hashing) | 为数据生成一个固定长度的、唯一的"指纹"。用于校验数据完整性。 | 榨汁机。把苹果放进去变成苹果汁,你无法从果汁变回完整的苹果。 | 不可逆 | MD5 (以及更安全的 SHA 系列) |
| 加密 (Encryption) | 通过"密钥"将明文数据变成看不懂的密文,目的是保护数据机密性。 | 带锁的日记本。只有拿对钥匙的人才能打开看内容。 | 可逆 (有密钥就能解密) | RSA, SM2, SM4 |
Base64: 编码而非加密
是什么 (What is it?)
Base64 是一种将任意二进制数据转换为由 64 个可打印字符(A-Z, a-z, 0-9, +, /)组成的文本字符串的编码方法。它就像一位"翻译官",负责把计算机内部的 0101 二进制数据"翻译"成纯文本,以便在那些只支持文本的环境中(如 URL、JSON、邮件正文)安全地传输。
核心要点: Base64 是公开的编码规则,任何人都可以解码。它不提供任何保密性,不是加密!
能干嘛 (What can it do?)
- 在 URL 中传递二进制数据(如小图片)。
- 在 JSON 或 XML 文件中嵌入二进制内容。
- 作为数据 URI (Data URI scheme) 在网页中直接嵌入图片,减少 HTTP 请求。
怎么玩 (How to play)
Java 8 及以上版本内置了强大的 Base64 工具类,无需任何第三方库。
Java Demo: 使用 java.util.Base64 复制
java
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class Base64Example {
public static void main(String[] args) {
String originalInput = "Hello, Wireshark, RSA, MD5, and SM!";
// 编码 (Encode)
String encodedString = Base64.getEncoder().encodeToString(originalInput.getBytes(StandardCharsets.UTF_8));
System.out.println("原始字符串: " + originalInput);
System.out.println("Base64 编码后: " + encodedString);
// 解码 (Decode)
byte[] decodedBytes = Base64.getDecoder().decode(encodedString);
String decodedString = new String(decodedBytes, StandardCharsets.UTF_8);
System.out.println("Base64 解码后: " + decodedString);
}
}
原始字符串: Hello, Wireshark, RSA, MD5, and SM!
Base64 编码后: SGVsbG8sIFdpcmVzaGFyaywgUlNBLCBNRDUgYW5kIFNNIQ==
Base64 解码后: Hello, Wireshark, RSA, MD5, and SM!
小总结
Base64 是一个简单实用的编码工具,用于在文本环境中安全地传输二进制数据。它简单、快速、通用,但切记它与"安全"或"保密"无关。
MD5: 消息摘要 (已不安全)
是什么 (What is it?)
MD5 (Message-Digest Algorithm 5) 是一种消息摘要算法,也叫哈希函数。它能将任意长度的数据,通过一个不可逆的"搅拌"过程,生成一个固定为 128 位(通常表示为 32 个十六进制字符)的唯一"指纹"或"摘要"。
核心特性:
- 定长输出: 无论输入多长,输出总是 32 个十六进制字符。
- 不可逆性: 无法从 MD5 值反推出原始数据。
- 雪崩效应: 输入数据的任何微小变动,都会导致输出的 MD5 值发生巨大变化。
能干嘛 (What can it do?)
MD5 主要用于校验数据完整性 (Integrity Check)。
- 文件校验: 网站提供软件下载时,通常会附上一个 MD5 值。你下载文件后,在本地计算其 MD5 值,如果与网站提供的一致,说明文件在下载过程中没有损坏或被篡改。
- 数字签名: 在对数据签名时,通常不是对整个庞大的原文签名,而是对原文的哈希值进行签名,效率更高。
常见的坑和陷阱
严重警告: 由于 MD5 算法已被发现存在"哈希碰撞"(即可以找到两个不同的输入,得到相同的 MD5 值)的严重漏洞,绝对不能再用于任何需要安全性的场景,例如:
- 密码存储:直接存储用户密码的 MD5 值是极其危险的,容易被彩虹表攻击破解。
- 代码签名或证书颁发:攻击者可以构造一个恶意文件,使其 MD5 与一个合法文件相同。
对于需要安全哈希的场景,请至少使用 SHA-256 或更强的算法。
怎么玩 (How to play)
Java 的 `java.security.MessageDigest` 类提供了 MD5 功能。
Java Demo: 计算字符串的MD5 复制
java
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5Example {
public static String getMd5(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] messageDigest = md.digest(input.getBytes(StandardCharsets.UTF_8));
BigInteger no = new BigInteger(1, messageDigest);
String hashtext = no.toString(16);
while (hashtext.length() < 32) {
hashtext = "0" + hashtext;
}
return hashtext;
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
String text1 = "Hello, MD5!";
String text2 = "Hello, MD5."; // 仅一个点不同
System.out.println("MD5 of '" + text1 + "': " + getMd5(text1));
System.out.println("MD5 of '" + text2 + "': " + getMd5(text2));
}
}
MD5 of 'Hello, MD5!': 85079b70343a41e212341103b415a776
MD5 of 'Hello, MD5.': 2d4292193433554c63742468351516e8
小总结
MD5 是一个快速的哈希算法,仅适用于非安全领域 的文件完整性校验 。任何与安全、密码、认证相关的场景都应禁用 MD5,并使用如 SHA-256 等更现代、更安全的算法。
RSA: 非对称加密
是什么 (What is it?)
RSA 是一种非对称加密 算法,是目前应用最广泛的公钥加密算法。它的核心特点是拥有一对数学上相关的密钥:公钥 (Public Key) 和 私钥 (Private Key)。
- 公钥:可以公开给任何人,用于加密数据和验证签名。
- 私钥:必须由自己妥善保管,绝不泄露,用于解密数据和生成签名。
工作原则: 用公钥加密的数据,只有 对应的私钥才能解密。反之,用私钥签名的数据,只有对应的公钥才能验证。
核心思想原理
RSA 的安全性基于一个数学难题:大整数质因数分解。简单来说,将两个大质数相乘很容易,但要把它们的乘积分解回原来的两个质数,在计算上是极其困难的。这个难题保证了从公钥很难推导出私钥。
graph TD subgraph A[发送方 Alice] A1[明文] --> A2{用 Bob 的公钥加密}; A2 --> A3[密文]; end subgraph B[接收方 Bob] B1[密文] --> B2{用 Bob 自己的私钥解密}; B2 --> B3[明文]; end subgraph C[不可信的信道] A3 -- 通过网络传输 --> B1; end subgraph D[窃听者 Eve] C -- 截获 --> D1[密文]; D1 --> D2{只有 Bob 的公钥, 无法解密}; end
RSA 加密解密流程图
能干嘛 (What can it do?)
- 数据加密: 发送方向接收方发送敏感数据时,用接收方的公钥加密,确保只有接收方能用自己的私钥解开。这是 HTTPS 建立安全连接的关键步骤之一。
- 数字签名: 发送方为了证明某条消息确实是自己发的,可以用自己的私钥对消息的哈希值进行"签名"。接收方用发送方的公钥来"验证签名",如果验证通过,就能确认消息来源可靠且未被篡改。
怎么玩 (How to play)
Java 的 JCE (Java Cryptography Extension) 框架提供了完整的 RSA 支持。
Java Demo: RSA 加密、解密与签名 复制
java
import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;
public class RsaExample {
public static KeyPair generateKeyPair() throws Exception {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(2048, new SecureRandom());
return generator.generateKeyPair();
}
public static String encrypt(String plainText, PublicKey publicKey) throws Exception {
Cipher encryptCipher = Cipher.getInstance("RSA");
encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] cipherText = encryptCipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(cipherText);
}
public static String decrypt(String cipherText, PrivateKey privateKey) throws Exception {
byte[] bytes = Base64.getDecoder().decode(cipherText);
Cipher decriptCipher = Cipher.getInstance("RSA");
decriptCipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(decriptCipher.doFinal(bytes), StandardCharsets.UTF_8);
}
public static String sign(String plainText, PrivateKey privateKey) throws Exception {
Signature privateSignature = Signature.getInstance("SHA256withRSA");
privateSignature.initSign(privateKey);
privateSignature.update(plainText.getBytes(StandardCharsets.UTF_8));
byte[] signature = privateSignature.sign();
return Base64.getEncoder().encodeToString(signature);
}
public static boolean verify(String plainText, String signature, PublicKey publicKey) throws Exception {
Signature publicSignature = Signature.getInstance("SHA256withRSA");
publicSignature.initVerify(publicKey);
publicSignature.update(plainText.getBytes(StandardCharsets.UTF_8));
byte[] signatureBytes = Base64.getDecoder().decode(signature);
return publicSignature.verify(signatureBytes);
}
public static void main(String[] args) throws Exception {
// 1. 生成密钥对
KeyPair keyPair = generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
String originalMessage = "This is a secret message for RSA!";
System.out.println("原始消息: " + originalMessage);
// 2. 加密与解密
String encryptedMessage = encrypt(originalMessage, publicKey);
System.out.println("加密后 (Base64): " + encryptedMessage);
String decryptedMessage = decrypt(encryptedMessage, privateKey);
System.out.println("解密后: " + decryptedMessage);
System.out.println("\n--- 数字签名演示 ---");
// 3. 签名与验签
String signature = sign(originalMessage, privateKey);
System.out.println("签名 (Base64): " + signature);
boolean isVerified = verify(originalMessage, signature, publicKey);
System.out.println("签名验证结果: " + isVerified);
// 尝试用错误数据验签
boolean isTamperedVerified = verify("This is a wrong message!", signature, publicKey);
System.out.println("篡改后数据验签结果: " + isTamperedVerified);
}
}
原始消息: This is a secret message for RSA!
加密后 (Base64): [一段很长的,每次运行都不同的Base64编码字符串]
解密后: This is a secret message for RSA!
--- 数字签名演示 ---
签名 (Base64): [另一段很长的,每次运行都不同的Base64编码字符串]
签名验证结果: true
篡改后数据验签结果: false
小总结
RSA 是现代密码学的基石。它通过公钥和私钥这对独特的组合,完美地解决了数据机密性 (加密)和身份真实性/数据完整性(签名)两大安全难题。但由于其计算速度较慢,通常不用于加密大文件,而是用于加密对称密钥或进行签名。
国密算法: SM2 & SM4
是什么 (What are they?)
国密算法(SM, 商用密码)是中国国家密码管理局发布的一系列密码标准,旨在实现密码技术的自主可控。SM2 和 SM4 是其中最核心、应用最广的两个。
- SM4 (对称加密): 对标国际上的 AES 算法,用于高速数据加密。
- SM2 (非对称加密): 对标国际上的 RSA/ECC 算法,基于椭圆曲线,用于加密和数字签名。
去哪下 (Where to get it?)
Java 原生 JDK 不支持国密算法,必须引入第三方密码学库。目前最主流、最权威的是 Bouncy Castle。
Maven Dependency: Bouncy Castle 复制
xml
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.78.1</version> <!-- 建议使用最新稳定版 -->
</dependency>
怎么玩 (SM4 对称加密)
SM4 用于高效地加解密大量数据,加密和解密使用同一个密钥。
Java Demo: SM4/ECB/PKCS5Padding 加解密 复制
java
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.Security;
import java.util.Base64;
public class SM4Example {
static {
// 必须在使用前静态注册BouncyCastle
Security.addProvider(new BouncyCastleProvider());
}
public static void main(String[] args) throws Exception {
String plainText = "你好,国密SM4对称加密!";
// 1. 生成 SM4 密钥
KeyGenerator keyGen = KeyGenerator.getInstance("SM4", "BC");
keyGen.init(128); // SM4 密钥长度固定为 128 位
SecretKey secretKey = keyGen.generateKey();
byte[] keyBytes = secretKey.getEncoded();
// 2. 加密
Cipher cipher = Cipher.getInstance("SM4/ECB/PKCS5Padding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, "SM4"));
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
String encodedEncrypted = Base64.getEncoder().encodeToString(encryptedBytes);
System.out.println("SM4 Encrypted: " + encodedEncrypted);
// 3. 解密
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBytes, "SM4"));
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encodedEncrypted));
System.out.println("SM4 Decrypted: " + new String(decryptedBytes, "UTF-8"));
}
}
SM4 Encrypted: [一段每次运行都不同的Base64编码字符串]
SM4 Decrypted: 你好,国密SM4对称加密!
怎么玩 (SM2 非对称加密)
SM2 性能优于 RSA,在同等安全强度下密钥更短。同样用于加密和签名。
Java Demo: SM2 加解密 复制
java
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import java.security.*;
import java.util.Base64;
public class SM2Example {
static {
Security.addProvider(new BouncyCastleProvider());
}
public static void main(String[] args) throws Exception {
String plainText = "你好,国密SM2非对称加密!";
// 1. 生成SM2密钥对
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("EC", "BC");
// SM2推荐使用256位椭圆曲线
keyPairGen.initialize(256);
KeyPair keyPair = keyPairGen.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
// 2. 加密
Cipher cipher = Cipher.getInstance("SM2", "BC");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
String encodedEncrypted = Base64.getEncoder().encodeToString(encryptedBytes);
System.out.println("SM2 Encrypted: " + encodedEncrypted);
// 3. 解密
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encodedEncrypted));
System.out.println("SM2 Decrypted: " + new String(decryptedBytes, "UTF-8"));
}
}
SM2 Encrypted: [一段每次运行都不同的Base64编码字符串]
SM2 Decrypted: 你好,国密SM2非对称加密!
小总结
国密 SM2/SM4 是符合中国国家标准的高强度现代密码算法。在需要满足国内合规性要求(如金融、政务等)的项目中,应优先使用国密算法。SM2 相比 RSA 在性能和密钥长度上有优势,而 SM4 则提供了与 AES 同等级别的对称加密能力。
整体学习建议
学习路径建议
- 第一站:编码 (Base64) - 理解数据如何在不同系统中"变形"传输,彻底分清编码和加密的区别。
- 第二站:摘要 (MD5, SHA-256) - 理解哈希函数的单向性和抗碰撞性,学会用它来校验数据完整性,并明白为何 MD5 已被淘汰。
- 第三站:对称加密 (SM4, AES) - 学习使用单一密钥进行高效数据加解密,理解其在加密海量数据时的核心作用。
- 第四站:非对称加密 (RSA, SM2) - 掌握公私钥体系,理解它如何解决密钥分发难题,并学会用它进行加密和数字签名。
- 第五站:融会贯通 - 学习 HTTPS、数字证书等实际应用是如何将以上技术组合起来,构建出强大的安全体系的。
持续学习资源
- Bouncy Castle 官网 : https://www.bouncycastle.org/ - 国密算法和其他密码学算法最权威的 Java 实现之一。
- 《图解密码技术》: 一本非常适合新手的图文并茂的密码学入门书籍。
- RFC 文档: 阅读相关算法的官方标准文档 (RFCs) 是最硬核但最准确的学习方式。
- OWASP (开放式Web应用程序安全项目): 学习密码学在 Web 安全中的应用和最佳实践。
- 总览:加密、摘要与编码
- [Base64: 编码](#Base64: 编码)
- 是什么
- 能干嘛
- 怎么玩
- 小总结
- [MD5: 消息摘要](#MD5: 消息摘要)
- 是什么
- 能干嘛
- 常见的坑
- 怎么玩
- 小总结
- [RSA: 非对称加密](#RSA: 非对称加密)
- 是什么
- 核心思想
- 能干嘛
- 怎么玩
- 小总结
- [国密算法: SM2 & SM4](#国密算法: SM2 & SM4)
- 是什么
- 去哪下
- 怎么玩(SM4)
- 怎么玩(SM2)
- 小总结
- 整体学习建议
- 学习路径
- 持续学习资源
☰
密码学算法新手入门教程
导出为图片
总览:加密、摘要与编码
在进入具体算法之前,我们必须先分清三个核心概念:
| 概念 | 核心目的 | 类比 | 是否可逆 | 本文涉及的算法 |
|---|---|---|---|---|
| 编码 (Encoding) | 将数据从一种形式转换为另一种形式,以便于传输或处理。不为保密。 | 翻译官。把中文翻译成英文,内容不变,只是换了种表达方式。 | 可逆 (知道规则就能转回来) | Base64 |
| 摘要/哈希 (Hashing) | 为数据生成一个固定长度的、唯一的"指纹"。用于校验数据完整性。 | 榨汁机。把苹果放进去变成苹果汁,你无法从果汁变回完整的苹果。 | 不可逆 | MD5 (以及更安全的 SHA 系列) |
| 加密 (Encryption) | 通过"密钥"将明文数据变成看不懂的密文,目的是保护数据机密性。 | 带锁的日记本。只有拿对钥匙的人才能打开看内容。 | 可逆 (有密钥就能解密) | RSA, SM2, SM4 |
Base64: 编码而非加密
是什么 (What is it?)
Base64 是一种将任意二进制数据转换为由 64 个可打印字符(A-Z, a-z, 0-9, +, /)组成的文本字符串的编码方法。它就像一位"翻译官",负责把计算机内部的 0101 二进制数据"翻译"成纯文本,以便在那些只支持文本的环境中(如 URL、JSON、邮件正文)安全地传输。
核心要点: Base64 是公开的编码规则,任何人都可以解码。它不提供任何保密性,不是加密!
能干嘛 (What can it do?)
- 在 URL 中传递二进制数据(如小图片)。
- 在 JSON 或 XML 文件中嵌入二进制内容。
- 作为数据 URI (Data URI scheme) 在网页中直接嵌入图片,减少 HTTP 请求。
怎么玩 (How to play)
Java 8 及以上版本内置了强大的 Base64 工具类,无需任何第三方库。
Java Demo: 使用 java.util.Base64 复制
java
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class Base64Example {
public static void main(String[] args) {
String originalInput = "Hello, Wireshark, RSA, MD5, and SM!";
// 编码 (Encode)
String encodedString = Base64.getEncoder().encodeToString(originalInput.getBytes(StandardCharsets.UTF_8));
System.out.println("原始字符串: " + originalInput);
System.out.println("Base64 编码后: " + encodedString);
// 解码 (Decode)
byte[] decodedBytes = Base64.getDecoder().decode(encodedString);
String decodedString = new String(decodedBytes, StandardCharsets.UTF_8);
System.out.println("Base64 解码后: " + decodedString);
}
}
原始字符串: Hello, Wireshark, RSA, MD5, and SM!
Base64 编码后: SGVsbG8sIFdpcmVzaGFyaywgUlNBLCBNRDUgYW5kIFNNIQ==
Base64 解码后: Hello, Wireshark, RSA, MD5, and SM!
小总结
Base64 是一个简单实用的编码工具,用于在文本环境中安全地传输二进制数据。它简单、快速、通用,但切记它与"安全"或"保密"无关。
MD5: 消息摘要 (已不安全)
是什么 (What is it?)
MD5 (Message-Digest Algorithm 5) 是一种消息摘要算法,也叫哈希函数。它能将任意长度的数据,通过一个不可逆的"搅拌"过程,生成一个固定为 128 位(通常表示为 32 个十六进制字符)的唯一"指纹"或"摘要"。
核心特性:
- 定长输出: 无论输入多长,输出总是 32 个十六进制字符。
- 不可逆性: 无法从 MD5 值反推出原始数据。
- 雪崩效应: 输入数据的任何微小变动,都会导致输出的 MD5 值发生巨大变化。
能干嘛 (What can it do?)
MD5 主要用于校验数据完整性 (Integrity Check)。
- 文件校验: 网站提供软件下载时,通常会附上一个 MD5 值。你下载文件后,在本地计算其 MD5 值,如果与网站提供的一致,说明文件在下载过程中没有损坏或被篡改。
- 数字签名: 在对数据签名时,通常不是对整个庞大的原文签名,而是对原文的哈希值进行签名,效率更高。
常见的坑和陷阱
严重警告: 由于 MD5 算法已被发现存在"哈希碰撞"(即可以找到两个不同的输入,得到相同的 MD5 值)的严重漏洞,绝对不能再用于任何需要安全性的场景,例如:
- 密码存储:直接存储用户密码的 MD5 值是极其危险的,容易被彩虹表攻击破解。
- 代码签名或证书颁发:攻击者可以构造一个恶意文件,使其 MD5 与一个合法文件相同。
对于需要安全哈希的场景,请至少使用 SHA-256 或更强的算法。
怎么玩 (How to play)
Java 的 `java.security.MessageDigest` 类提供了 MD5 功能。
Java Demo: 计算字符串的MD5 复制
java
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5Example {
public static String getMd5(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] messageDigest = md.digest(input.getBytes(StandardCharsets.UTF_8));
BigInteger no = new BigInteger(1, messageDigest);
String hashtext = no.toString(16);
while (hashtext.length() < 32) {
hashtext = "0" + hashtext;
}
return hashtext;
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
String text1 = "Hello, MD5!";
String text2 = "Hello, MD5."; // 仅一个点不同
System.out.println("MD5 of '" + text1 + "': " + getMd5(text1));
System.out.println("MD5 of '" + text2 + "': " + getMd5(text2));
}
}
MD5 of 'Hello, MD5!': 85079b70343a41e212341103b415a776
MD5 of 'Hello, MD5.': 2d4292193433554c63742468351516e8
小总结
MD5 是一个快速的哈希算法,仅适用于非安全领域 的文件完整性校验 。任何与安全、密码、认证相关的场景都应禁用 MD5,并使用如 SHA-256 等更现代、更安全的算法。
RSA: 非对称加密
是什么 (What is it?)
RSA 是一种非对称加密 算法,是目前应用最广泛的公钥加密算法。它的核心特点是拥有一对数学上相关的密钥:公钥 (Public Key) 和 私钥 (Private Key)。
- 公钥:可以公开给任何人,用于加密数据和验证签名。
- 私钥:必须由自己妥善保管,绝不泄露,用于解密数据和生成签名。
工作原则: 用公钥加密的数据,只有 对应的私钥才能解密。反之,用私钥签名的数据,只有对应的公钥才能验证。
核心思想原理
RSA 的安全性基于一个数学难题:大整数质因数分解。简单来说,将两个大质数相乘很容易,但要把它们的乘积分解回原来的两个质数,在计算上是极其困难的。这个难题保证了从公钥很难推导出私钥。
graph TD subgraph A[发送方 Alice] A1[明文] --> A2{用 Bob 的公钥加密}; A2 --> A3[密文]; end subgraph B[接收方 Bob] B1[密文] --> B2{用 Bob 自己的私钥解密}; B2 --> B3[明文]; end subgraph C[不可信的信道] A3 -- 通过网络传输 --> B1; end subgraph D[窃听者 Eve] C -- 截获 --> D1[密文]; D1 --> D2{只有 Bob 的公钥, 无法解密}; end
RSA 加密解密流程图
能干嘛 (What can it do?)
- 数据加密: 发送方向接收方发送敏感数据时,用接收方的公钥加密,确保只有接收方能用自己的私钥解开。这是 HTTPS 建立安全连接的关键步骤之一。
- 数字签名: 发送方为了证明某条消息确实是自己发的,可以用自己的私钥对消息的哈希值进行"签名"。接收方用发送方的公钥来"验证签名",如果验证通过,就能确认消息来源可靠且未被篡改。
怎么玩 (How to play)
Java 的 JCE (Java Cryptography Extension) 框架提供了完整的 RSA 支持。
Java Demo: RSA 加密、解密与签名 复制
java
import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;
public class RsaExample {
public static KeyPair generateKeyPair() throws Exception {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(2048, new SecureRandom());
return generator.generateKeyPair();
}
public static String encrypt(String plainText, PublicKey publicKey) throws Exception {
Cipher encryptCipher = Cipher.getInstance("RSA");
encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] cipherText = encryptCipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(cipherText);
}
public static String decrypt(String cipherText, PrivateKey privateKey) throws Exception {
byte[] bytes = Base64.getDecoder().decode(cipherText);
Cipher decriptCipher = Cipher.getInstance("RSA");
decriptCipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(decriptCipher.doFinal(bytes), StandardCharsets.UTF_8);
}
public static String sign(String plainText, PrivateKey privateKey) throws Exception {
Signature privateSignature = Signature.getInstance("SHA256withRSA");
privateSignature.initSign(privateKey);
privateSignature.update(plainText.getBytes(StandardCharsets.UTF_8));
byte[] signature = privateSignature.sign();
return Base64.getEncoder().encodeToString(signature);
}
public static boolean verify(String plainText, String signature, PublicKey publicKey) throws Exception {
Signature publicSignature = Signature.getInstance("SHA256withRSA");
publicSignature.initVerify(publicKey);
publicSignature.update(plainText.getBytes(StandardCharsets.UTF_8));
byte[] signatureBytes = Base64.getDecoder().decode(signature);
return publicSignature.verify(signatureBytes);
}
public static void main(String[] args) throws Exception {
// 1. 生成密钥对
KeyPair keyPair = generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
String originalMessage = "This is a secret message for RSA!";
System.out.println("原始消息: " + originalMessage);
// 2. 加密与解密
String encryptedMessage = encrypt(originalMessage, publicKey);
System.out.println("加密后 (Base64): " + encryptedMessage);
String decryptedMessage = decrypt(encryptedMessage, privateKey);
System.out.println("解密后: " + decryptedMessage);
System.out.println("\n--- 数字签名演示 ---");
// 3. 签名与验签
String signature = sign(originalMessage, privateKey);
System.out.println("签名 (Base64): " + signature);
boolean isVerified = verify(originalMessage, signature, publicKey);
System.out.println("签名验证结果: " + isVerified);
// 尝试用错误数据验签
boolean isTamperedVerified = verify("This is a wrong message!", signature, publicKey);
System.out.println("篡改后数据验签结果: " + isTamperedVerified);
}
}
原始消息: This is a secret message for RSA!
加密后 (Base64): [一段很长的,每次运行都不同的Base64编码字符串]
解密后: This is a secret message for RSA!
--- 数字签名演示 ---
签名 (Base64): [另一段很长的,每次运行都不同的Base64编码字符串]
签名验证结果: true
篡改后数据验签结果: false
小总结
RSA 是现代密码学的基石。它通过公钥和私钥这对独特的组合,完美地解决了数据机密性 (加密)和身份真实性/数据完整性(签名)两大安全难题。但由于其计算速度较慢,通常不用于加密大文件,而是用于加密对称密钥或进行签名。
国密算法: SM2 & SM4
是什么 (What are they?)
国密算法(SM, 商用密码)是中国国家密码管理局发布的一系列密码标准,旨在实现密码技术的自主可控。SM2 和 SM4 是其中最核心、应用最广的两个。
- SM4 (对称加密): 对标国际上的 AES 算法,用于高速数据加密。
- SM2 (非对称加密): 对标国际上的 RSA/ECC 算法,基于椭圆曲线,用于加密和数字签名。
去哪下 (Where to get it?)
Java 原生 JDK 不支持国密算法,必须引入第三方密码学库。目前最主流、最权威的是 Bouncy Castle。
Maven Dependency: Bouncy Castle 复制
xml
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.78.1</version> <!-- 建议使用最新稳定版 -->
</dependency>
怎么玩 (SM4 对称加密)
SM4 用于高效地加解密大量数据,加密和解密使用同一个密钥。
Java Demo: SM4/ECB/PKCS5Padding 加解密 复制
java
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.Security;
import java.util.Base64;
public class SM4Example {
static {
// 必须在使用前静态注册BouncyCastle
Security.addProvider(new BouncyCastleProvider());
}
public static void main(String[] args) throws Exception {
String plainText = "你好,国密SM4对称加密!";
// 1. 生成 SM4 密钥
KeyGenerator keyGen = KeyGenerator.getInstance("SM4", "BC");
keyGen.init(128); // SM4 密钥长度固定为 128 位
SecretKey secretKey = keyGen.generateKey();
byte[] keyBytes = secretKey.getEncoded();
// 2. 加密
Cipher cipher = Cipher.getInstance("SM4/ECB/PKCS5Padding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, "SM4"));
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
String encodedEncrypted = Base64.getEncoder().encodeToString(encryptedBytes);
System.out.println("SM4 Encrypted: " + encodedEncrypted);
// 3. 解密
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBytes, "SM4"));
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encodedEncrypted));
System.out.println("SM4 Decrypted: " + new String(decryptedBytes, "UTF-8"));
}
}
SM4 Encrypted: [一段每次运行都不同的Base64编码字符串]
SM4 Decrypted: 你好,国密SM4对称加密!
怎么玩 (SM2 非对称加密)
SM2 性能优于 RSA,在同等安全强度下密钥更短。同样用于加密和签名。
Java Demo: SM2 加解密 复制
java
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import java.security.*;
import java.util.Base64;
public class SM2Example {
static {
Security.addProvider(new BouncyCastleProvider());
}
public static void main(String[] args) throws Exception {
String plainText = "你好,国密SM2非对称加密!";
// 1. 生成SM2密钥对
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("EC", "BC");
// SM2推荐使用256位椭圆曲线
keyPairGen.initialize(256);
KeyPair keyPair = keyPairGen.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
// 2. 加密
Cipher cipher = Cipher.getInstance("SM2", "BC");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
String encodedEncrypted = Base64.getEncoder().encodeToString(encryptedBytes);
System.out.println("SM2 Encrypted: " + encodedEncrypted);
// 3. 解密
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encodedEncrypted));
System.out.println("SM2 Decrypted: " + new String(decryptedBytes, "UTF-8"));
}
}
SM2 Encrypted: [一段每次运行都不同的Base64编码字符串]
SM2 Decrypted: 你好,国密SM2非对称加密!
小总结
国密 SM2/SM4 是符合中国国家标准的高强度现代密码算法。在需要满足国内合规性要求(如金融、政务等)的项目中,应优先使用国密算法。SM2 相比 RSA 在性能和密钥长度上有优势,而 SM4 则提供了与 AES 同等级别的对称加密能力。
整体学习建议
学习路径建议
- 第一站:编码 (Base64) - 理解数据如何在不同系统中"变形"传输,彻底分清编码和加密的区别。
- 第二站:摘要 (MD5, SHA-256) - 理解哈希函数的单向性和抗碰撞性,学会用它来校验数据完整性,并明白为何 MD5 已被淘汰。
- 第三站:对称加密 (SM4, AES) - 学习使用单一密钥进行高效数据加解密,理解其在加密海量数据时的核心作用。
- 第四站:非对称加密 (RSA, SM2) - 掌握公私钥体系,理解它如何解决密钥分发难题,并学会用它进行加密和数字签名。
- 第五站:融会贯通 - 学习 HTTPS、数字证书等实际应用是如何将以上技术组合起来,构建出强大的安全体系的。
持续学习资源
- Bouncy Castle 官网 : https://www.bouncycastle.org/ - 国密算法和其他密码学算法最权威的 Java 实现之一。
- 《图解密码技术》: 一本非常适合新手的图文并茂的密码学入门书籍。
- RFC 文档: 阅读相关算法的官方标准文档 (RFCs) 是最硬核但最准确的学习方式。
- OWASP (开放式Web应用程序安全项目): 学习密码学在 Web 安全中的应用和最佳实践。