惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Blog — PlanetScale
Blog — PlanetScale
C
Cybersecurity and Infrastructure Security Agency CISA
K
Kaspersky official blog
L
LangChain Blog
I
Intezer
N
News and Events Feed by Topic
C
Check Point Blog
MyScale Blog
MyScale Blog
I
InfoQ
Last Week in AI
Last Week in AI
腾讯CDC
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
L
LINUX DO - 热门话题
T
Threatpost
Y
Y Combinator Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
H
Help Net Security
The GitHub Blog
The GitHub Blog
V
Vulnerabilities – Threatpost
NISL@THU
NISL@THU
云风的 BLOG
云风的 BLOG
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
A
About on SuperTechFans
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Martin Fowler
Martin Fowler
P
Privacy International News Feed
AWS News Blog
AWS News Blog
Simon Willison's Weblog
Simon Willison's Weblog
S
Securelist
T
Tor Project blog
C
CERT Recently Published Vulnerability Notes
Scott Helme
Scott Helme
A
Arctic Wolf
有赞技术团队
有赞技术团队
博客园 - 三生石上(FineUI控件)
T
Tenable Blog
The Hacker News
The Hacker News
博客园_首页
Cisco Talos Blog
Cisco Talos Blog
Spread Privacy
Spread Privacy
Microsoft Security Blog
Microsoft Security Blog
N
News and Events Feed by Topic
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
V
V2EX
G
Google Developers Blog
Webroot Blog
Webroot Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
WordPress大学
WordPress大学
P
Palo Alto Networks Blog
T
Threat Research - Cisco Blogs

博客园 - buguge

从 `int` 到 `Duration`:一个缓存 API 的三次演进教会我的事 最差实践(bad-practice):开发者在方法里直接实例化线程池对象,然后...(应用gg了) 【HttpClient最差实践(bad-practice)】开发者在 http 工具方法中直接实例化 HttpClient,然后…… 知识VS技能:如何优雅判空? 20260604SR超时问题排查 推敲见文章:从 `try..catch` 看异常日志打印的正确姿势 #解决问题要彻底# 慢SQL治理完成后,如何防止同类问题“死灰复燃”? 从合同甲方是荒谬的“JD”谈起:软件开发不应遗忘的“常识” 别留小尾巴/尽快剪掉小尾巴:从一次“ABA”字段重命名,谈谈“解决问题要彻底” 常见的OOM错误 ( OutOfMemoryError全类型详解) 开发者暴露了一个无需授权访问的裸接口,我问:如果有人暴力请求怎么办? 【SQL性能优化篇】有了!治理慢SQL“WHERE create_time ORDER BY id”的良药---规避“Using filesort”性能杀手 高效查询商户日终余额:一个SQL的优化实践 Hutool 的 `TimedCache` 到期会自动清理吗? ——————hutool cache的"惰性清理"和"定期清理" Fastjson枚举反序列化:当字符串不是枚举常量名时,会发生什么? fastjson-EnumDeserializer类及源码分析 随笔20260309:我们都是围城里的人 `UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only` 异常解析 认识2个单词:goal/target —————— 为什么Maven是 "goal" 而不是 "target"? 聚合系统设计:策略模式(Strategy Pattern)在银行通道对接场景中的应用 这样构建对象,太帅了!—— 阶梯式Builder模式与代码整洁之道 注意!字段数据类型不匹配,这个sql会很慢 还在用ArrayList?用HashSet吧!--性能对比 分页查询还在用create_time去做降序? 从 synchronized 到 ConcurrentHashMap:一个小小的并发控制策略升级优化,证明我还是初级程序员 面试题沟通整理 【研发笔记20260120】值得记录:靠谱程序员的回聘 研发笔记:如何消除长字符串的秘钥数据对RPC负荷、日志量、系统安全所带来的伤害? 未给entity的主属性赋值,Mybatisplus却抛出了type mismatch异常。——————分享一下Mybatisplus主键填充机制 在java中实现c#的int.TryParse方法 灵活用工平台-连续劳务所得税-计算器-工具类,拿走不谢 Apollo场景建议配置指南:充分发挥分布式配置中心优势
Crypto、Cipher与Password:Java加密开发的三个核心概念
buguge · 2026-07-07 · via 博客园 - buguge

引言:三个“密码”,三种含义

在Java安全开发中,有三个极易混淆的术语:CryptoCipherPassword。它们的中文翻译都与“密码”有关,但在计算机科学中各自承担着截然不同的角色。

  • Crypto:密码学的总称,是一个领域或工具箱
  • Cipher:加密/解密的具体算法或工具
  • Password:用户输入的身份认证凭据

理解这三者的区别,是写出安全、正确的Java加密代码的第一步。本文将从概念辨析到实战代码,带你彻底理清这三个核心概念。

一、Crypto:密码学的总称

CryptoCryptography(密码学) 的缩写,词源来自希腊语kryptós(隐藏/秘密)。它是一个宏观概念,涵盖了整个密码学领域——包括加密、解密、哈希、数字签名、密钥交换、消息认证码(MAC)等所有相关技术。

在Java中,javax.crypto包就是“Crypto”的具体体现。根据官方文档,该包“为加密操作提供类和接口”,涵盖的操作包括加密、密钥生成和密钥协商、消息认证码(MAC)生成等。

类比理解Crypto就像“厨房”——整个烹饪的场所。里面有不同的区域(洗菜、切菜、炒菜、烘焙),有各种各样的工具。

Crypto ≠ Cipher:Crypto是整个领域,Cipher只是这个领域下的一个具体工具

二、Cipher:Java加密的核心引擎

2.1 什么是Cipher?

javax.crypto.CipherJava加密扩展(JCE,Java Cryptography Extension)框架的核心类,提供加密和解密的密码功能。它不保存数据,而是执行具体的加解密运算。

用官方文档的话说:“此类为加密和解密提供密码功能,构成了JCE框架的核心。”

2.2 Cipher的核心概念:Transformation(转换模式)

创建Cipher对象时,需要指定一个转换模式(Transformation) ,格式为:

算法/工作模式/填充模式

例如:

Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");

三个组成部分的含义:

组成部分 说明 示例
算法 具体的加密算法 AES、DES、RSA
工作模式 分组密码的处理方式(仅对分组密码有效) ECB、CBC、GCM、CTR
填充模式 数据长度不足块大小时如何补齐 PKCS5Padding、NoPadding

工作模式的选择直接影响安全性

  • ECB(电子密码本) :相同明文生成相同密文,不安全,不推荐使用
  • CBC(密码分组链接) :需要随机IV,比ECB安全
  • GCM(伽罗瓦/计数器模式) :AEAD模式,同时提供加密和认证,现代推荐首选

2.3 Cipher的核心方法

使用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):完成加密/解密操作

2.4 完整代码示例: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 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:用户身份认证的凭据

3.1 什么是Password?

Password(口令/密码) 是用户用于身份验证的一组字符串。它是“你输入的那个东西”——比如登录系统时输入的mySecret123

3.2 Password与Cipher的本质区别

维度 Cipher(加密器) Password(口令)
本质 算法/工具 数据/凭据
作用 执行加密/解密运算 验证用户身份
在代码中 Cipher类的实例 Stringchar[]变量
是否可逆 可逆(加密后可解密) 不可逆存储(存哈希值)
安全性依赖 算法强度和密钥管理 密码复杂度和存储方式

3.3 重要规则:Cipher不能直接使用Password

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);

3.4 Password的存储:加盐哈希

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                                │
│                   加密/解密                                     │
└─────────────────────────────────────────────────────────────────┘

五、常见误区与最佳实践

误区一:把Password直接当密钥用

// ❌ 错误
String password = "123456";
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, password);  // 类型不匹配

正确做法:使用PBKDF2、bcrypt等KDF将Password派生为密钥。

误区二:使用ECB模式

// ❌ 不安全:ECB模式下相同明文产生相同密文
Cipher.getInstance("AES/ECB/PKCS5Padding");

正确做法:使用GCM(推荐)或CBC模式。

误区三:重复使用IV

GCM模式下重复使用IV会引发伪造攻击。每次加密都应生成新的随机IV。

最佳实践清单

  1. Cipher:使用AES/GCM/NoPadding,256位密钥
  2. IV:每次加密生成新的随机IV(GCM使用12字节)
  3. Password存储:使用bcrypt或Argon2加盐哈希,绝不存储明文
  4. Password → 密钥:使用PBKDF2WithHmacSHA256,迭代次数≥10000
  5. 密钥管理:密钥不得硬编码在代码中,应使用密钥管理服务(KMS)

六、总结

概念 一句话定义 在Java中的体现
Crypto 密码学的总称/领域 javax.crypto
Cipher 执行加解密的具体工具 javax.crypto.Cipher
Password 用户身份认证的凭据 String / char[],需加盐哈希存储

记住这三个关键点

  1. Crypto是领域,包含Cipher、哈希、签名等所有密码学技术
  2. Cipher是工具,用来加密和解密数据
  3. Password是凭据,用来验证你是谁,不能直接当密钥用

理解这三者的区别,你就能在Java加密开发中少走弯路,写出更安全、更规范的代码。