传送门
在开始写下这篇文字的时候,为了补充传送门的地址快速看了一下上节的内容。心中隐约发现:
- 里面有一个字打错了(严格意义上是写错了,因为是五笔输入法),天塌下来的塌 写成了**"踏"**
- 里面的附录JCA与JCE关系章节(取自百度百科),内容已经完全没印象了
于是在想如果是AI的话,首先错别字的低级错误肯定不会犯;其次就算是技术类知识对它来说,也不会存在忘记的可能。所以现在AI导致博客内容创作的价值降低了,并且我内心产生了一些是否继续维护博客的犹豫。因为实在是真切感受到了AI它的强大,无论是代码还是文章创作在某些方面表现出来的效率几乎比人要高:无论是语言还是框架、从技术选型到方案输出,也都颇为可靠。只要打开对话框,将自己的问题告诉它就行了,比起以前到搜索引擎里面自己筛选内容也方便了许多。
如果到了某一天,几乎所有的内容都是AI创作,那大家会不会反而追求、怀念那些由人创作的**"原创"**内容呢?或者这可以做为继续坚持下去的动力。
使用AI,了解AI,拥抱AI,将它做为一个好用的工具!所以下面的有些内容、尤其是归纳总结性的会出于AI生成!
回顾JCA与JCE关系
由于上面谈到JCA与JCE关系模糊,这里再次回顾一下两者的定义及关系。
JCA
- 本质:一套服务提供者接口(SPI)和应用程序接口(API)的集合
- 核心包:java.security、java.security.cert、java.security.interfaces
- 设计目标:
- 提供统一的加密服务访问方式
- 支持算法独立性(应用程序不依赖特定算法)
- 支持Provider机制(可插拔的加密实现)
- 遵循"一次编写,到处运行"的Java原则
JCA是Java平台的加密体系基础架构 ,它是一个框架性设计 而非具体实现。比如Provider机制里面,默认的Provider就是JDK自带的**"SUN"** ;而上一节里面的SM4就是通过Bouncy Castle包来扩展的,它的Provider名字是**"BC"。**
查看系统中安装的所有的JCE提供者
可以通过一个例子来查看本地java系统中安装的所有的JCE提供者:
java
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.Provider;
import java.security.Security;
/**
* @author
* @date 2025/9/20
*/
public class SecurityUtil {
public static void main(String[] args) {
// 查看本地java系统中安装的所有的JCE提供者,和一些相应算法
System.out.println("-------列出默认的加密服务提供者-----");
Provider[] providers = Security.getProviders();
printProvider(providers);
System.out.println("-------列出扩展以后的加密服务提供者-----");
// 再添加一个Provider为Bouncy Castle库
Security.addProvider(new BouncyCastleProvider());
printProvider(Security.getProviders());
System.out.println("-------列出系统支持的消息摘要算法:");
Security.getAlgorithms("MessageDigest").forEach(System.out::println);
System.out.println("-------列出系统支持的生成公钥和私钥对的算法:");
Security.getAlgorithms("KeyPairGenerator").forEach(System.out::println);
}
private static void printProvider(Provider[] providers) {
System.out.println("======================begin print===============================");
for (Provider p : providers) {
System.out.println("Provider:" + p.getName() + " - version:" + p.getVersion() + ", info" + p.getInfo());
}
System.out.println("======================end print===============================");
}
}
JCE
JCE 核心类与概念
JCE是JCA的官方扩展 ,专注于提供对称加密、非对称加密和密钥协商功能。
- 本质:JCA框架的扩展规范
- 核心包 :
javax.crypto
、javax.crypto.interfaces
、javax.crypto.spec
- 关键类 :
Cipher
、KeyGenerator
、SecretKeyFactory
、Mac
**jce.jar是JCE规范的具体实现文件。**JCE 架构的核心包括以下几个关键类和接口:
|------------------|-------------------------------------|
| 类/接口 | 作用描述 |
| KeyGenerator | 用于生成对称密钥。 |
| KeyPairGenerator | 用于生成非对称密钥对(公钥和私钥)。 |
| Cipher | 提供加密和解密功能的核心类,支持对称加密、非对称加密、流加密等。 |
| MessageDigest | 用于计算数据的消息摘要(哈希值),例如 SHA-256、MD5。 |
| Mac | 用于生成消息认证码(MAC),例如 HmacSHA256。 |
| Signature | 用于生成和验证数字签名。 |
| Security | 用于管理已安装的安全提供者(如动态添加 Bouncy Castle)。 |
JCE与BouncyCastle的关系
前面在介绍各种对称加密算法DES、AES、3DES、SM4的代码时,对SM4实现其实通过引用了第三方包BouncyCastle来处理的。所以这里也会探讨二者之间的关系,详见后面。
JCA 与 JCE 的关系
现在我们可以清晰地描述它们的关系了:
整体与部分的关系:
JCA是一个大的框架,定义了Java安全服务的总体架构和核心基础功能(如摘要、签名)。
JCE是构建在JCA框架之上的一个特定服务集 ,它利用JCA的"提供者"架构,专门为加密这个细分领域提供服务。
架构与实现的关系:
JCA提供了"插座"和"接口"的标准(即SPI - Service Provider Interface)。
JCE(以及其他的服务如JSSE)是插在这个"插座"上的"电器",它们遵循JCA的标准接口,提供了具体的加密功能实现。
历史演进关系:
早期由于出口限制,加密功能(JCE)与核心安全功能(JCA)是分离的。
现在,JCE已经完全融入JCA体系,成为Java标准版中不可分割的一部分。当你谈论现代的Java密码学时,JCA通常指的是包括JCE功能在内的整个体系。
一个简单的类比:
JCA 就像是一家汽车的 底盘平台和电气架构。
- 它定义了发动机、变速箱、方向盘应该如何连接和通信。
JCE 就像是这个平台上安装的 一台高性能发动机和变速箱。
- 它是底盘架构的一个具体、强大的实现,专门负责提供动力(加密/解密)。
其他组件,如JSSE (Java Secure Socket Extension,用于SSL/TLS),就像是平台上的 车轮和悬挂系统,负责另一类功能(安全通信)。
总结对比表
特性 JCA (Java Cryptography Architecture) JCE (Java Cryptography Extension) 定位 框架和架构 架构内的功能扩展/实现 核心功能 消息摘要、数字签名、密钥对生成、随机数、证书 加密/解密、密钥协商、消息认证码(MAC) 设计模式 提供者(Provider)架构 遵循并扩展JCA的提供者架构 主要包 java.security.*
javax.crypto.*
关键类 MessageDigest
,Signature
,KeyPairGenerator
Cipher
,KeyGenerator
,SecretKey
关系 基础 ,是整体 扩展 ,是部分 因此,在现代Java开发中,当你需要进行加密操作(如AES加密数据)时,你是在使用JCE的API (如
Cipher
),而整个调用过程是基于JCA的提供者架构来完成的。它们共同构成了Java强大且灵活的安全密码学基础。-----------------------------------------------------------------------------以上总结引用内容来自DeepSeek
从SM4谈起
在前面介绍常用的对称算法时,提到过DES、3DES、AES及SM4,并通过表格列出了各自的场景及特点:
算法 | 密钥长度 | 块大小 | 状态 | 特点 |
---|---|---|---|---|
DES | 56位 | 64位 | 已淘汰 | 早期标准,易被暴力破解 |
3DES | 112/168位 | 64位 | 逐渐淘汰 | DES的三重应用,安全性提高但效率低 |
AES | 128/192/256位 | 128位 | 推荐使用 | 目前最广泛使用的标准,安全高效 |
SM4 | 128位 | 128位 | 推荐使用 | 适用于需要国密合规的场景,如金融、政务等 |
Blowfish | 32-448位 | 64位 | 可用 | 速度快,但块大小较小 |
RC4 | 40-2048位 | 流密码 | 不推荐 | 曾广泛使用,现发现多处漏洞 |
其中现在主流的是AES与SM4,而SM4又被称为"国密算法"。
国密算法
国密算法是由国家密码管理局认定的自主可控密码算法体系,包含SM系列及祖冲之算法等核心技术。该体系涵盖对称加密(SM1、SM4、SM7)、非对称加密(SM2、SM9)、哈希算法(SM3)与流密码(ZUC)等类型,形成完整的密码技术生态 [1-3]。
算法采用国产标准曲线和迭代结构设计,具备抗量子攻击特性与高效运算能力,可替代国际通用算法。在电子政务、无线通信、区块链等领域实现规模化应用,并通过《密码法》等法规推动重点行业密码改造。
国密算法体系由7类核心算法构成:
对称算法:包含SM1(128位分组密码)、SM4(32轮迭代结构)、SM7(非接IC卡专用),采用分组加密技术实现数据保护
非对称算法:SM2(椭圆曲线公钥算法)涵盖签名/加密/密钥交换功能,SM9(标识密码算法)以用户身份标识作为公钥
哈希算法:SM3生成256位摘要值,应用于数字签名验证与随机数生成
流密码:ZUC算法提供4G通信加密与完整性保护
关于国密的概念介绍,推荐看看这个文章一文读懂什么是国密算法,图文并茂讲的比较不错。
国密算法标准
国密算法的标准,首先肯定是通过中国国家密码管理局来制定,里面会有各种政策性的文件。并作为主管单位,负责批准和发布密码行业标准。

密码行业标准化技术委员会
受国家密码管理局委托,提出标准规划、组织标准的编写、审查、复审等工作。

这些行业标准可以在行业标准信息服务平台查询,比如我们讨论的各种国密算法:

ISO国际标准
在密码信息技术发展历程中,必须承认的是国外的领先地位。一个算法的重要的衡量指标就是是否成为国际标准!国密标准(中国商用密码算法标准)与ISO/IEC国际标准之间,是一个从自主研制 到积极融入 ,再到双向贡献的动态发展关系。这不仅显著提升了我国在密码领域的国际话语权,也为全球密码技术体系提供了重要的"中国方案":

下面这个表格梳理了主要国密算法成为国际标准的关键时间点。
算法名称 | 算法类型 | 成为ISO/IEC国际标准的关键时间点 |
---|---|---|
ZUC(祖冲之)算法 | 序列密码 | 2011年纳入国际电信联盟(ITU)的4G标准1,2010年其算法本身也成为ISO/IEC国际标准 |
SM2算法 | 非对称密码(椭圆曲线公钥密码) | 2017年正式成为ISO/IEC国际标准 |
SM9算法 | 非对称密码(标识密码) | 2017年正式成为ISO/IEC国际标准 |
SM3算法 | 密码杂凑算法(哈希函数) | 2018年正式成为ISO/IEC国际标准 |
SM4算法 | 对称密码(分组密码) | 2021年正式成为ISO/IEC国际标准 |
要说明的是:因为SM1算法未公开,所以不在此列。
对JCA框架的实现和JCE的补充
那再回过头来看在Java领域里面,既然有了JAC框架与JCE标准内置实现,为什么还要有BouncyCastle这个库来实现SM4算法呢?让我们回顾一下SM4的CBC模式算法:
java
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.Security;
public class SM4Example {
static {
// 注册Bouncy Castle提供者
Security.addProvider(new BouncyCastleProvider());
}
// SM4加密
public static byte[] encrypt(byte[] data, byte[] key) throws Exception {
SecretKey secretKey = new SecretKeySpec(key, "SM4");
Cipher cipher = Cipher.getInstance("SM4/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return cipher.doFinal(data);
}
// SM4解密
public static byte[] decrypt(byte[] cipherText, byte[] key) throws Exception {
SecretKey secretKey = new SecretKeySpec(key, "SM4");
Cipher cipher = Cipher.getInstance("SM4/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return cipher.doFinal(cipherText);
}
public static void main(String[] args) throws Exception {
String plainText = "Hello, SM4!";
byte[] key = "1234567890abcdef".getBytes(); // 16字节密钥
// 加密
byte[] encrypted = encrypt(plainText.getBytes(), key);
System.out.println("加密结果(Base64): " + java.util.Base64.getEncoder().encodeToString(encrypted));
// 解密
byte[] decrypted = decrypt(encrypted, key);
System.out.println("解密结果: " + new String(decrypted));
}
}
观察一下代码
- 首先创建一个SecretKey,指定为**"SM4"**算法
- 获取Cipher实例,指定算法格式(算法/工作模式/填充模式):SM4/ECB/PKCS5Padding
- 然后初始化,通过不同的模式模型区分:ENCRYPT_MODE、DECRYPT_MODE
- 最后执行加解密
上面所有的类及方法,其实都是JCE包里面的,这个代码流程跟AES也没有什么两样。来看一个JCE生成的AES的ECB模式算法:
java
public class SunAESExample {
public static final String MODE_ECB_NOPADDING = "AES/ECB/PKCS5Padding";
/**
* 使用 ECB 模式加密数据
*
* @param plaintext 明文数据
* @param key AES 密钥
* @return 加密结果
*/
public static byte[] encryptECB(byte[] plaintext, byte[] key) throws Exception {
SecretKey secretKey = new SecretKeySpec(key, AES);
// 初始化加密器
Cipher cipher = Cipher.getInstance(MODE_ECB_NOPADDING);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
// 执行加密
return cipher.doFinal(plaintext);
}
/**
* 使用 ECB 模式解密数据
*
* @param ciphertext 密文数据
* @param key AES 密钥
* @return 解密后的原始数据
*/
public static byte[] decryptECB(byte[] ciphertext, byte[] key) throws Exception {
SecretKey secretKey = new SecretKeySpec(key, AES);
// 初始化解密器
Cipher cipher = Cipher.getInstance(MODE_ECB_NOPADDING);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
// 执行解密
return cipher.doFinal(ciphertext);
}
public static void main(String[] args) {
System.out.println("================================ECB模式===============================");
String plainText2 = "Hello, SM4!";
byte[] key = "1234567890abcdef".getBytes(); // 16字节密钥
byte[] encrypted = SunAESUtil.encryptECB(plainText2.getBytes("UTF-8"), key);
System.out.println("加密结果(Base64): " + java.util.Base64.getEncoder().encodeToString(encrypted));
byte[] decrypted = SunAESUtil.decryptECB(encrypted, key);
System.out.println("解密结果: " + new String(decrypted));
} catch (Exception e) {
System.err.println("错误: " + e.getMessage());
e.printStackTrace();
}
}
}
除了上面的静态代码块注册的BouncyCastleProvider:
java
static {
// 注册Bouncy Castle提供者
Security.addProvider(new BouncyCastleProvider());
}
而里面的BouncyCastleProvider来自下面的maven引入的包:
XML
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version> <!-- 使用最新版本 -->
</dependency>
这一切的原因是JCE并不默认支持SM4,所以才需要BouncyCastle这种第三方库,所以说BouncyCastle是对JCE的补充和对JCA的扩展(BouncyCastleProvider是通过Security注册的,Security来自security包,这样就利用SPI机制实现了扩展的算法!):
Java 开发平台自带了一个名为 JCA(Java Cryptography Architecture) 和 JCE(Java Cryptography Extension) 的安全框架。但是,标准 JDK 自带的加密算法提供商(通常是 Sun/Oracle 提供的)存在一些限制:
算法不全:早期版本的 JDK 缺少很多重要的、现代的加密算法(如 AES、ECC 在较老的 JDK 中可能不支持)。
出口限制:由于历史上美国的加密软件出口管制法律,JDK 提供的加密强度是受限的(例如,密钥长度被限制)。虽然这些限制后来大部分都放开了,但在一些旧版本和特定环境下仍然是个问题。
功能有限:对于更高级的密码学标准和格式(如 OpenPGP、S/MIME、较新版本的 ASN.1 处理等),标准 JDK 的支持非常有限或根本没有。
Bouncy Castle 的出现就是为了填补这些空白,它提供了:
一个完整的、无强度限制的 JCE 提供商。
大量 JDK 未内置的加密算法。
对多种密码学标准和数据格式的读写和生成支持。
密码学领域的瑞士军刀
由于国密算法不仅仅是SM4,而是一整套的密码算法解决方案。BouncyCastle对相关算法都能很好的支持,可以称之为Java生态系统中密码学领域的事实标准 和瑞士军刀,是开发安全应用程序时不可或缺的强大工具。
什么是BouncyCastle
Bouncy Castle 是一个开源的、轻量级的、基于 Java 和 C# 的密码学算法库。它提供了 Java 平台标准(JCA/JCE)未能提供的、以及许多其他的密码学算法和标准实现。
简单来说,它是一个功能极其丰富的"密码学工具箱",被广泛应用于需要加密、解密、数字签名、证书处理等安全功能的软件开发中。
主要特性与功能
Bouncy Castle 的功能非常庞大,主要包括以下几个方面:
1. 丰富的对称加密算法:
- AES, DES, Triple-DES, Blowfish, Twofish, CAST5, RC2, RC4, RC5, RC6, IDEA, ChaCha, Salsa20, SM4 等。
2. 丰富的非对称加密与密钥协商算法:
- RSA, DSA, DH (Diffie-Hellman), ECDSA (椭圆曲线数字签名算法), ECDH (椭圆曲线 Diffie-Hellman 密钥协商), ElGamal, SM2 (中国国密算法) 等。
3. 丰富的消息摘要(哈希)算法:
- MD5, SHA-1, SHA-2家族(SHA-224, SHA-256, SHA-384, SHA-512), SHA-3, RIPEMD, Tiger, Whirlpool, SM3 (中国国密算法) 等。
4. 消息认证码(MAC)算法:
- HMac (基于各种哈希算法, 如 HMac-SHA256), CMAC, GMAC, Poly1305 等。
5. 对密码学标准和格式的广泛支持(这是其一大亮点):
X.509 证书:生成、解析、转换(PEM/DER)、验证(CRL, OCSP)。
PKCS#7/CMS 和 S/MIME:用于加密和签名消息的标准。
PKCS#10:证书签名请求(CSR)。
PKCS#12:个人信息交换格式(.p12, .pfx 文件)。
OpenPGP:对 PGP 密钥环、加密、签名消息的完整支持。
ASN.1:编解码和处理能力。
TLS/SSL:提供了轻量级的 API 用于构建 TLS 引擎(虽然通常不直接用于替换 JSSE,但可用于特殊场景)。
6. 轻量级密码学 API(Lightweight API):
- 除了作为 JCE 提供商,Bouncy Castle 还提供了一套独立的、不依赖于 JCE 的轻量级 API。这套 API 设计得更简单、更直接,适用于那些不希望引入整个 JCE 复杂性的场景,尤其是在资源受限的环境(如 Android 早期版本)中非常有用。
作为 JCE 提供商
首先将 BouncyCastle 的 JAR 包(如 bcprov-jdk18on.jar
)添加到你的项目依赖中(通过 Maven, Gradle 或手动添加):
XML
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version> <!-- 使用最新版本 -->
</dependency>
然后在代码中注册提供商,并正常使用 JCA/JCE 的 API:
java
static {
// 注册Bouncy Castle提供者
Security.addProvider(new BouncyCastleProvider());
}
而BouncyCastle的主要功能里面是支持AES算法的,而JCE又默认支持AES算法,所以在使用中到底该如何选择使用哪个库呢?这就可以通过指定Provider参数:
java
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.Security;
import javax.crypto.Cipher;
import java.security.Key;
public class BCExample {
public static void main(String[] args) throws Exception {
// 1. 注册 Bouncy Castle 提供商
Security.addProvider(new BouncyCastleProvider());
// 2. 正常使用 JCE API,指定算法时使用 "BC" 提供商
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
// ... 后续的初始化、加密、解密操作
// KeyGenerator, KeyPairGenerator 等也是如此
}
}
跟进一下JCE的Cipher类的getInstance方法:

总结
优点:
-
功能极其强大且全面,几乎涵盖了所有常见的密码学需求。
-
开源免费,代码可审计,安全性有保障。
-
活跃的社区和持续的维护。
-
提供了两种风格的 API(JCE 提供商和轻量级API)以适应不同需求。
注意事项:
-
学习曲线:由于其功能庞大,API 可能比较复杂,需要花时间学习。
-
性能:在某些情况下,其实现可能不如高度优化的本地库(如 OpenSSL)快,但对于绝大多数应用来说已经足够。
-
正确使用密码学:和所有密码学库一样,Bouncy Castle 只是一个工具,能否构建出安全的系统,更大程度上取决于开发者是否正确使用了这些算法和模式。
总而言之,BouncyCastle 是 Java 生态系统中密码学领域的事实标准 和瑞士军刀,是开发安全应用程序时不可或缺的强大工具。在后面一节,我们通过BouncyCastle来看看SM2非对称算法的实现及应用!