1. 项目概述:为什么Java开发者必须掌握加密算法?
在当今这个数据即资产的时代,无论你是开发一个简单的用户登录模块,还是构建一个处理敏感交易的金融系统,数据安全都是悬在头顶的达摩克利斯之剑。我见过太多项目,初期为了快速上线,对密码直接进行MD5存储,甚至明文传输,等到用户数据泄露、公司声誉受损时才追悔莫及。加密,早已不是可选项,而是每一位合格Java开发者的必备技能栈。
这个“实战宝典”的目的,就是帮你彻底打通任督二脉。它不仅仅是一份API调用清单,而是深入6种核心加密技术的骨髓,从古典的对称加密到现代的国密算法,从散列校验到数字签名。我会结合我十多年踩坑填坑的经验,不仅告诉你“怎么用”,更会剖析“为什么这么用”,以及那些官方文档里不会写的“坑在哪里”。无论你是正在准备面试,被“RSA加密过程”、“AES的CBC和GCM模式区别”这类八股文困扰,还是在实际项目中遇到了“Java: OutOfMemoryError”或Lombok版本兼容性问题,这里都有你能直接“抄作业”的解决方案和深度解析。
2. 加密技术全景与核心选型逻辑
在动手写代码之前,我们必须建立一个清晰的认知地图。加密算法不是一把万能钥匙,不同的场景需要不同的“锁”。盲目选型,轻则性能瓶颈,重则安全漏洞。
2.1 加密算法的三大门派:对称、非对称与散列
对称加密 ,好比你和朋友共用一把钥匙的密码箱。加密和解密使用同一把密钥,速度快,效率高,适合加密 大量数据 。典型的如AES、DES(已不安全)、SM4。它的核心挑战在于:如何安全地把这把“共享钥匙”交给对方?密钥管理是最大痛点。
非对称加密 ,则像是一个配有两把钥匙的保险箱:一把公钥,可以公开给任何人,用来锁上箱子(加密);一把私钥,必须严格保密,只有你自己有,用来打开箱子(解密)。RSA、ECC(椭圆曲线)、SM2都属于此类。它完美解决了密钥分发问题,但计算复杂,速度比对称加密慢几个数量级,通常只用于加密 小数据量 (如会话密钥)或进行 数字签名 。
散列算法 ,更像是一个单向的指纹提取器。你把任意长度的数据(如文件、密码)丢进去,它会输出一个固定长度的、唯一的“指纹”(哈希值)。这个过程不可逆,你无法从指纹反推出原始数据。MD5、SHA-256、SM3是代表。它主要用于验证数据完整性(文件是否被篡改)和密码存储(不存明文,只存哈希值)。
2.2 实战选型决策树:告别选择困难症
面对具体需求,你可以遵循这个决策路径:
-
目标是什么?
- 机密性 (防止内容被看):首选对称加密(AES、SM4)。如需远程交换密钥,则用非对称加密(RSA、SM2)来保护对称密钥的传输。
- 完整性/认证 (防止内容被改):使用散列算法(SHA-256、SM3)生成摘要,或使用非对称加密进行数字签名(如RSA签名)。
- 身份认证与不可否认 (证明是谁发的):必须使用数字签名(RSA/ECC/SM2的签名算法)。
-
性能与数据量考量 :
- 加密GB级的文件?毫不犹豫用AES。
- 只是加密一个几十字节的密码或令牌?RSA可以胜任。
- 需要同时保证机密性和完整性?采用 混合加密体系 :用AES加密数据,用RSA加密AES密钥,再用SHA-256生成数据摘要。
-
合规性要求 :
- 国内商业项目,特别是政务、金融领域, 必须优先考虑国密算法 (SM2、SM3、SM4)以满足网络安全等级保护制度等合规要求。
- 国际项目或开源项目,AES、RSA、SHA-256仍是通用标准。
注意 :绝对不要使用已被证明不安全的算法,如DES、MD5(用于密码存储和签名验证)、SHA-1。MD5仅能用于非安全场景的快速校验,如重复文件识别。
3. 六大核心加密技术深度剖析与代码实战
下面,我们将深入每一类算法的核心,并附上生产级可用的Java代码示例。所有示例基于Java 8+,并会指出高版本JDK中的最佳实践。
3.1 对称加密之王:AES算法实战
AES(高级加密标准)是目前全球最通用的对称加密算法。它关键的概念在于 工作模式 和 填充模式 。
模式选择是成败关键 :
- ECB模式 :最简单,每个数据块独立加密。 致命缺陷 :相同的明文块会加密成相同的密文块,无法隐藏数据模式。 绝不用于加密有意义的数据 ,可用于加密随机密钥。
- CBC模式 :最经典的模式。需要一个初始化向量(IV)来增加随机性,且需要填充。它提供了良好的机密性,但由于是串行处理,不利于并行计算。
- GCM模式 :现代首选。它不仅提供机密性,还同时提供 完整性认证 (Authenticated Encryption)。它不需要填充,且支持并行计算。在TLS 1.3中已成为强制标准。
Java实现AES-GCM示例 :
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import java.security.SecureRandom;
import java.util.Base64;
public class AesGcmDemo {
private static final int AES_KEY_SIZE = 256; // 密钥长度:128, 192, 256
private static final int GCM_TAG_LENGTH = 128; // 认证标签长度(位)
private static final int GCM_IV_LENGTH = 12; // 推荐IV长度:12字节(96位)
public static void main(String[] args) throws Exception {
String plainText = "这是一段需要加密的敏感数据";
// 1. 生成密钥(实际生产中,密钥应从安全的密钥管理系统获取)
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(AES_KEY_SIZE);
SecretKey secretKey = keyGen.generateKey();
// 2. 加密
byte[] cipherTextWithIV = encrypt(plainText.getBytes(), secretKey);
System.out.println("加密后(Base64):" + Base64.getEncoder().encodeToString(cipherTextWithIV));
// 3. 解密
String decryptedText = decrypt(cipherTextWithIV, secretKey);
System.out.println("解密后:" + decryptedText);
}
public static byte[] encrypt(byte[] plaintext, SecretKey key) throws Exception {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] iv = new byte[GCM_IV_LENGTH];
new SecureRandom().nextBytes(iv); // 生成随机IV,每次加密都应不同
GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec);
byte[] cipherText = cipher.doFinal(plaintext);
// 将IV和密文拼接在一起存储/传输。IV不是秘密,但必须唯一。
byte[] cipherTextWithIV = new byte[GCM_IV_LENGTH + cipherText.length];
System.arraycopy(iv, 0, cipherTextWithIV, 0, GCM_IV_LENGTH);
System.arraycopy(cipherText, 0, cipherTextWithIV, GCM_IV_LENGTH, cipherText.length);
return cipherTextWithIV;
}
public static String decrypt(byte[] cipherTextWithIV, SecretKey key) throws Exception {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
// 分离IV和密文
byte[] iv = new byte[GCM_IV_LENGTH];
System.arraycopy(cipherTextWithIV, 0, iv, 0, GCM_IV_LENGTH);
byte[] cipherText = new byte[cipherTextWithIV.length - GCM_IV_LENGTH];
System.arraycopy
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/weixin_29324401/article/details/162618786



