数据安全系列6:从SM4国密算法谈到Bouncy Castle

传送门

数据安全系列1:开篇

数据安全系列2:单向散列函数概念

数据安全系列3:密码技术概述

数据安全系列4:密码技术的应用-接口调用的身份识别

数据安全系列5:常用的对称算法浅析

在开始写下这篇文字的时候,为了补充传送门的地址快速看了一下上节的内容。心中隐约发现:

  1. 里面有一个字打错了(严格意义上是写错了,因为是五笔输入法),天塌下来的 写成了**"踏"**
  2. 里面的附录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
  • 设计目标:
  1. 提供统一的加密服务访问方式
  2. 支持算法独立性(应用程序不依赖特定算法)
  3. 支持Provider机制(可插拔的加密实现)
  4. 遵循"一次编写,到处运行"的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.cryptojavax.crypto.interfacesjavax.crypto.spec
  • 关键类CipherKeyGeneratorSecretKeyFactoryMac

**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 的关系

现在我们可以清晰地描述它们的关系了:

  1. 整体与部分的关系

    • JCA是一个大的框架,定义了Java安全服务的总体架构和核心基础功能(如摘要、签名)。

    • JCE是构建在JCA框架之上的一个特定服务集 ,它利用JCA的"提供者"架构,专门为加密这个细分领域提供服务。

  2. 架构与实现的关系

    • JCA提供了"插座"和"接口"的标准(即SPI - Service Provider Interface)。

    • JCE(以及其他的服务如JSSE)是插在这个"插座"上的"电器",它们遵循JCA的标准接口,提供了具体的加密功能实现。

  3. 历史演进关系

    • 早期由于出口限制,加密功能(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 提供的)存在一些限制:

  1. 算法不全:早期版本的 JDK 缺少很多重要的、现代的加密算法(如 AES、ECC 在较老的 JDK 中可能不支持)。

  2. 出口限制:由于历史上美国的加密软件出口管制法律,JDK 提供的加密强度是受限的(例如,密钥长度被限制)。虽然这些限制后来大部分都放开了,但在一些旧版本和特定环境下仍然是个问题。

  3. 功能有限:对于更高级的密码学标准和格式(如 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/CMSS/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非对称算法的实现及应用!

相关推荐
從南走到北2 小时前
JAVA露营基地预约户外露营预约下单系统小程序
java·开发语言·小程序
曹牧2 小时前
Java:实现List的定长截取
java·开发语言·list
水无痕simon3 小时前
8 shiro的web整合
java
CodeCraft Studio3 小时前
全球知名的Java Web开发平台Vaadin上线慧都网
java·开发语言·前端·vaadin·java开发框架·java全栈开发·java ui 框架
我是华为OD~HR~栗栗呀3 小时前
前端面经-高级开发(华为od)
java·前端·后端·python·华为od·华为·面试
城管不管3 小时前
Java EE、Java SE 和 Spring Boot
java·spring boot·java-ee
xdpcxq10293 小时前
EF Core框架数据库连接管理
java·jvm·数据库
熙客3 小时前
分布式ID解决方案
java·分布式·spring cloud·微服务
菜鸟小九4 小时前
SSM(MybatisPlus)
java·开发语言·spring boot·后端