SDF4J JCE Provider
基于 GM/T 0018-2023 《密码设备应用接口规范》的 Java Cryptography Extension (JCE) Provider,提供标准 JCA 接口访问国密算法硬件设备。
功能特性
- SM3 消息摘要 -
MessageDigest.getInstance("SM3", "SDF") - SM4 对称加密 - 支持 ECB/CBC/GCM 模式,支持 NoPadding/PKCS5Padding/PKCS7Padding
- SM2 非对称加密/签名 -
Signature.getInstance("SM3withSM2", "SDF"),遵循 GM/T 0009-2012 标准 - 硬件随机数 -
SecureRandom.getInstance("SDF", "SDF") - HMAC-SM3 -
Mac.getInstance("HmacSM3", "SDF") - SM4-MAC -
Mac.getInstance("SM4-MAC", "SDF")
快速开始
1. 添加依赖
编译并安装到本地 Maven 仓库:
cd sdf4j
mvn clean install -DskipTests
在项目中添加依赖:
<dependency>
<groupId>org.openhitls</groupId>
<artifactId>sdf4j-jce</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
2. 初始化 Provider
import org.openhitls.sdf4j.jce.SDFProvider;
import java.security.Security;
// 创建并注册 Provider
SDFProvider provider = new SDFProvider();
Security.addProvider(provider);
3. 使用 JCE 接口
// SM3 摘要
MessageDigest md = MessageDigest.getInstance("SM3", "SDF");
byte[] hash = md.digest("Hello".getBytes());
// SM4 加密
KeyGenerator kg = KeyGenerator.getInstance("SM4", "SDF");
SecretKey key = kg.generateKey();
Cipher cipher = Cipher.getInstance("SM4/CBC/PKCS5Padding", "SDF");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(new byte[16]));
byte[] ciphertext = cipher.doFinal("Hello".getBytes());
// SM2 签名
KeyPairGenerator kpg = KeyPairGenerator.getInstance("SM2", "SDF");
KeyPair keyPair = kpg.generateKeyPair();
Signature signer = Signature.getInstance("SM3withSM2", "SDF");
signer.initSign(keyPair.getPrivate());
signer.setParameter(new SM2ParameterSpec((SM2PublicKey) keyPair.getPublic()));
signer.update("Hello".getBytes());
byte[] signature = signer.sign();
// 硬件随机数
SecureRandom sr = SecureRandom.getInstance("SDF", "SDF");
byte[] random = new byte[32];
sr.nextBytes(random);
4. 设备内部密钥使用 (Internal Keys)
SDF 原生密码库除了提供独立的密码运算能力,还支持使用预置在设备内部的密钥(不可导出)。通过本项目的拓展,可与标准 JCE 结合:
SM2 非对称内部密钥
import org.openhitls.sdf4j.jce.key.SDFInternalPrivateKey;
import org.openhitls.sdf4j.jce.key.SDFInternalPublicKey;
import org.openhitls.sdf4j.jce.key.SDFInternalPrivateKey.KeyUsage;
// 1. 获取内部私钥引用
// 参数说明:
// - keyIndex: 设备中预置的密钥索引(通常是 1~N)
// - password: 访问内部私钥的密码(若设备不需要密码可传 null)
// - usage: 密钥用途,取值 KeyUsage.SIGN (签名/验签) 或 KeyUsage.ENCRYPT (加密/解密)
SDFInternalPrivateKey privKey = new SDFInternalPrivateKey(1, "password".toCharArray(), KeyUsage.SIGN);
// 2. 初始化签名并使用内部私钥
Signature signer = Signature.getInstance("SM3withSM2", "SDF");
signer.initSign(privKey);
signer.update("Hello".getBytes());
byte[] signature = signer.sign();
// 3. 内部公钥验证 (参数与私钥类似)
SDFInternalPublicKey pubKey = new SDFInternalPublicKey(1, KeyUsage.SIGN);
Signature verifier = Signature.getInstance("SM3withSM2", "SDF");
verifier.initVerify(pubKey);
verifier.update("Hello".getBytes());
boolean valid = verifier.verify(signature);
SM4 对称内部密钥 (基于 KEK)
对于 SM4 对称加密,SDF 规范支持通过 内部密钥加密密钥 (KEK) 来动态协商或派生会话密钥,数据不出密码机:
import org.openhitls.sdf4j.jce.key.SDFInternalSymmetricKey;
// 1. 获取基于 KEK 的内部对称密钥引用
// 参数说明:
// - kekIndex: 密码设备中配置的 KEK(Key Encryption Key)索引
// - password: 访问 KEK 的密码(若不需要可传 null)
SDFInternalSymmetricKey sm4Key = new SDFInternalSymmetricKey(4, "password".toCharArray());
// 2. 使用内部对称密钥进行加解密运算,此时 SDF4J 将自动调用底层设备进行 KEK 加解密及运算
Cipher cipher = Cipher.getInstance("SM4/CBC/NoPadding", "SDF");
cipher.init(Cipher.ENCRYPT_MODE, sm4Key, new IvParameterSpec(new byte[16]));
byte[] ciphertext = cipher.doFinal("Hello internal SM4".getBytes());
配置说明
SDF 库路径配置
SDF4J JCE Provider 需要加载符合 GM/T 0018-2023 标准的 SDF 设备驱动库。 支持以下几种配置方式(按优先级排序):
1. Java 系统属性(最高优先级)
# 方式1:指定库目录 + 名称(推荐)
java -Dsdf.library.path=/opt/sdf/lib -Dsdf.library.name=sdf_openhitls ...
# 方式2:直接指定完整库文件路径
java -Dsdf.library.path=/opt/sdf/lib/libsdf_openhitls.so ...
# Maven 测试时使用(与 sdf4j 模块使用相同的属性名)
mvn test -pl sdf4j-jce -Dsdf.library.path=/opt/sdf/lib -Dsdf.library.name=sdf_openhitls
注意:sdf.library.* 属性与 sdf4j 模块共享,可在父 pom 中统一配置。
2. 配置文件
在类路径下创建 sdf4j-jce.properties 文件:
# 方式1:指定库目录 + 名称
library.path=/opt/sdf/lib
library.name=sdf_openhitls
# 方式2:直接指定完整库文件路径
library.path=/opt/sdf/lib/libsdf_openhitls.so
3. Maven 编译时配置
# 编译时指定库配置,将写入 JAR 内的 sdf4j-jce.properties
mvn clean package -Dsdf.library.path=/opt/sdf/lib -Dsdf.library.name=sdf_openhitls
4. 环境变量(最低优先级)
# 设置 SDF 平台库路径
export SDF_LIBRARY_PATH=/path/to/libsdf_openhitls.so
# 运行测试
mvn test -pl sdf4j-jce
使用 OpenHiTLS 平台库
本项目提供了基于 OpenHiTLS 的 SDF 平台库实现(位于 platform/sdfx 目录):
# 编译 OpenHiTLS 平台库
cd platform/sdfx/build
cmake ..
make -j$(nproc)
# 使用方式1:环境变量
export SDF_LIBRARY_PATH=/path/to/platform/sdfx/build/libsdf_openhitls.so
# 使用方式2:Maven 命令行参数(与 sdf4j 模块统一)
mvn test -pl sdf4j-jce -Dsdf.library.path=./platform/sdfx/build/ -Dsdf.library.name=sdf_openhitls
无设备测试
没有 SDF 设备时,可以跳过集成测试:
# 只运行单元测试(不依赖硬件)
mvn test -pl sdf4j-jce -Dtest=SM2KeyTest,SDFProviderTest
编译
系统要求
- JDK 8 或更高版本
- GCC 4.8 或更高版本
- CMake 3.10 或更高版本
- JNI 头文件(通常随 JDK 一起安装)
编译步骤
# 克隆仓库
git clone https://github.com/openhitls/sdf4j.git
cd sdf4j
# 编译全部模块
mvn clean install
# 只编译 JCE Provider
mvn clean install -pl sdf4j-jce -am
编译输出
sdf4j-jce/target/
├── sdf4j-jce-1.0.0-SNAPSHOT.jar # JAR 包(包含 native 库)
├── sdf4j-jce-1.0.0-SNAPSHOT-sources.jar # 源码包
└── native/
└── libsdf4j-jce.so # 独立的 native 库
测试
运行全部测试
# 需要先设置 环境变量 指向 SDF 平台库,或者设置SDF_LIBRARY_PATH
export SDF_LIBRARY_PATH=/path/to/libsdf_openhitls.so
mvn test -pl sdf4j-jce
或者
mvn test -pl sdf4j-jce -Dsdf.library.path=/opt/sdf/lib -Dsdf.library.name=sdf_openhitls
API 文档
支持的算法
| 算法 | 类型 | JCA 名称 | 说明 |
|---|---|---|---|
| SM3 | MessageDigest | SM3 |
国密摘要算法 |
| SM4 | Cipher | SM4/ECB/NoPadding |
SM4 ECB 模式 |
| SM4 | Cipher | SM4/ECB/PKCS5Padding |
SM4 ECB + PKCS5 填充 |
| SM4 | Cipher | SM4/ECB/PKCS7Padding |
PKCS5Padding 别名 |
| SM4 | Cipher | SM4/CBC/NoPadding |
SM4 CBC 模式 |
| SM4 | Cipher | SM4/CBC/PKCS5Padding |
SM4 CBC + PKCS5 填充 |
| SM4 | Cipher | SM4/CBC/PKCS7Padding |
PKCS5Padding 别名 |
| SM4 | Cipher | SM4/GCM/NoPadding |
SM4 GCM 模式(AEAD) |
| SM2 | Cipher | SM2 |
SM2 非对称加密 |
| SM2 | Signature | SM3withSM2 |
SM2 签名(GM/T 0009-2012) |
| SM2 | KeyPairGenerator | SM2 |
SM2 密钥对生成 |
| SM4 | KeyGenerator | SM4 |
SM4 密钥生成 |
| SDF | SecureRandom | SDF |
硬件随机数 |
| HmacSM3 | Mac | HmacSM3 |
HMAC-SM3 |
| SM4-MAC | Mac | SM4-MAC |
SM4 CBC-MAC |
SDFProvider 方法
// 构造函数
SDFProvider() // 自动从 SDF_LIBRARY_PATH 加载库
SDFProvider(String libraryPath) // libraryPath 参数已废弃,使用环境变量
// 状态检查
boolean isInitialized() // 检查是否已初始化
void shutdown() // 关闭 Provider 并释放资源
// 注册 Provider
Security.addProvider(provider)
Security.removeProvider("SDF")
故障排查
UnsatisfiedLinkError: 找不到 SDF 库
java.lang.UnsatisfiedLinkError: /path/to/libsdf_openhitls.so: cannot open shared object
解决方法:
- 检查库路径是否正确:
ls -l $SDF_LIBRARY_PATH - 检查库依赖:
ldd /path/to/libsdf_openhitls.so - 设置 LD_LIBRARY_PATH:
export LD_LIBRARY_PATH=/path/to/platform/libsdf_openhitls.so:$LD_LIBRARY_PATH
Unsupported JNI version
java.lang.UnsatisfiedLinkError: unsupported JNI version 0xFFFFFFFF
解决方法:清理旧的临时库文件
rm -rf /tmp/sdf4j-jce-native/
mvn clean package
许可证
Copyright (c) 2025 OpenHitls
SDF4J is licensed under Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2