


























在Java安全开发中,有三个极易混淆的术语:Crypto、Cipher和Password。它们的中文翻译都与“密码”有关,但在计算机科学中各自承担着截然不同的角色。
理解这三者的区别,是写出安全、正确的Java加密代码的第一步。本文将从概念辨析到实战代码,带你彻底理清这三个核心概念。
Crypto是Cryptography(密码学) 的缩写,词源来自希腊语kryptós(隐藏/秘密)。它是一个宏观概念,涵盖了整个密码学领域——包括加密、解密、哈希、数字签名、密钥交换、消息认证码(MAC)等所有相关技术。
在Java中,javax.crypto包就是“Crypto”的具体体现。根据官方文档,该包“为加密操作提供类和接口”,涵盖的操作包括加密、密钥生成和密钥协商、消息认证码(MAC)生成等。
类比理解:Crypto就像“厨房”——整个烹饪的场所。里面有不同的区域(洗菜、切菜、炒菜、烘焙),有各种各样的工具。
Crypto ≠ Cipher:Crypto是整个领域,Cipher只是这个领域下的一个具体工具。
javax.crypto.Cipher是Java加密扩展(JCE,Java Cryptography Extension)框架的核心类,提供加密和解密的密码功能。它不保存数据,而是执行具体的加解密运算。
用官方文档的话说:“此类为加密和解密提供密码功能,构成了JCE框架的核心。”
创建Cipher对象时,需要指定一个转换模式(Transformation) ,格式为:
算法/工作模式/填充模式
例如:
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
三个组成部分的含义:
| 组成部分 | 说明 | 示例 |
|---|---|---|
| 算法 | 具体的加密算法 | AES、DES、RSA |
| 工作模式 | 分组密码的处理方式(仅对分组密码有效) | ECB、CBC、GCM、CTR |
| 填充模式 | 数据长度不足块大小时如何补齐 | PKCS5Padding、NoPadding |
工作模式的选择直接影响安全性:
使用Cipher的标准流程:
// 1. 创建Cipher实例
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
// 2. 初始化(指定模式 + 密钥)
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
// 3. 执行加密/解密
byte[] result = cipher.doFinal(inputData);
关键方法说明:
getInstance(String transformation):创建Cipher实例init(int opmode, Key key):初始化,指定加密/解密模式和密钥update(byte[] input):分块处理数据(适用于大数据)doFinal(byte[] input):完成加密/解密操作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 AesGcmExample {
// GCM推荐使用96位(12字节)IV
private static final int GCM_IV_LENGTH = 12;
private static final int GCM_TAG_LENGTH = 128;
public static void main(String[] args) throws Exception {
// 1. 生成AES密钥(256位)
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
SecretKey secretKey = keyGen.generateKey();
String plaintext = "Hello, 这是一个敏感数据!";
// 2. 加密
byte[] ciphertext = encrypt(plaintext, secretKey);
System.out.println("密文(Base64): " + Base64.getEncoder().encodeToString(ciphertext));
// 3. 解密
String decrypted = decrypt(ciphertext, secretKey);
System.out.println("解密结果: " + decrypted);
}
public static byte[] encrypt(String plaintext, SecretKey key) throws Exception {
// 生成随机IV
byte[] iv = new byte[GCM_IV_LENGTH];
SecureRandom.getInstanceStrong().nextBytes(iv);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
byte[] ciphertext = cipher.doFinal(plaintext.getBytes("UTF-8"));
// 将IV和密文合并(IV + 密文)
byte[] result = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, result, 0, iv.length);
System.arraycopy(ciphertext, 0, result, iv.length, ciphertext.length);
return result;
}
public static String decrypt(byte[] encrypted, SecretKey key) throws Exception {
// 分离IV和密文
byte[] iv = new byte[GCM_IV_LENGTH];
byte[] ciphertext = new byte[encrypted.length - GCM_IV_LENGTH];
System.arraycopy(encrypted, 0, iv, 0, GCM_IV_LENGTH);
System.arraycopy(encrypted, GCM_IV_LENGTH, ciphertext, 0, ciphertext.length);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
cipher.init(Cipher.DECRYPT_MODE, key, spec);
byte[] plaintext = cipher.doFinal(ciphertext);
return new String(plaintext, "UTF-8");
}
}
注意:GCM模式要求每次加密使用不同的IV,重复使用IV会引发伪造攻击。
Password(口令/密码) 是用户用于身份验证的一组字符串。它是“你输入的那个东西”——比如登录系统时输入的mySecret123。
| 维度 | Cipher(加密器) | Password(口令) |
|---|---|---|
| 本质 | 算法/工具 | 数据/凭据 |
| 作用 | 执行加密/解密运算 | 验证用户身份 |
| 在代码中 | Cipher类的实例 |
String或char[]变量 |
| 是否可逆 | 可逆(加密后可解密) | 不可逆存储(存哈希值) |
| 安全性依赖 | 算法强度和密钥管理 | 密码复杂度和存储方式 |
Cipher不接受String类型的Password作为密钥!
AES算法要求密钥长度必须是16、24或32字节,而用户输入的Password(如"123456")长度不固定。必须通过密钥派生函数(KDF) 将Password转化为固定长度的SecretKey。
// ❌ 错误:Cipher不能直接使用Password
String password = "mySecret123";
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, password); // 编译错误!
// ✅ 正确:通过PBKDF2派生密钥
String password = "mySecret123";
byte[] salt = new byte[16];
new SecureRandom().nextBytes(salt);
PBEKeySpec spec = new PBEKeySpec(
password.toCharArray(),
salt,
10000, // 迭代次数
256 // 密钥长度(位)
);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
SecretKey key = factory.generateSecret(spec);
// 现在key可以作为Cipher的密钥
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key);
Password绝不能明文存储。正确的做法是使用加盐哈希(如bcrypt、Argon2)进行不可逆存储。
// 使用Spring Security的BCrypt
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String hashedPassword = encoder.encode("userPassword"); // 存储此值
boolean isValid = encoder.matches("userPassword", hashedPassword); // 验证
┌─────────────────────────────────────────────────────────────────┐
│ CRYPTO(密码学) │
│ 整个加密领域的总称 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 哈希算法 │ │ 数字签名 │ │ 密钥交换 │ │
│ │ (SHA-256) │ │ (RSA) │ │ (DH/ECDH) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ CIPHER(加密器) │ │
│ │ 执行加密/解密运算的具体工具 │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ AES │ │ DES │ │ RSA │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ PASSWORD(口令) │
│ 用户输入的认证凭据 │
│ 如:"mySecret123"、"hello123" │
│ ↓ 通过KDF派生 │
│ SecretKey │
│ ↓ 交给Cipher │
│ 加密/解密 │
└─────────────────────────────────────────────────────────────────┘
// ❌ 错误
String password = "123456";
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, password); // 类型不匹配
正确做法:使用PBKDF2、bcrypt等KDF将Password派生为密钥。
// ❌ 不安全:ECB模式下相同明文产生相同密文
Cipher.getInstance("AES/ECB/PKCS5Padding");
正确做法:使用GCM(推荐)或CBC模式。
GCM模式下重复使用IV会引发伪造攻击。每次加密都应生成新的随机IV。
AES/GCM/NoPadding,256位密钥| 概念 | 一句话定义 | 在Java中的体现 |
|---|---|---|
| Crypto | 密码学的总称/领域 | javax.crypto包 |
| Cipher | 执行加解密的具体工具 | javax.crypto.Cipher类 |
| Password | 用户身份认证的凭据 | String / char[],需加盐哈希存储 |
记住这三个关键点:
理解这三者的区别,你就能在Java加密开发中少走弯路,写出更安全、更规范的代码。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。