openHiTLS Java SDK,兼容JCE
当前访问频次受限,请登录后继续访问
HiTLS4J
HiTLS4J 是一个 Java 密码扩展(JCE)Provider,用于封装原生 openHiTLS 密码库。它提供完整的 JCE API 实现,使 Java 应用能够通过标准 Java 安全接口使用 openHiTLS 提供的密码算法。
概述
HiTLS4J 通过 JNI(Java Native Interface)将 openHiTLS 密码库与 Java 应用集成。它实现了一个 JCE Provider,可注册到 Java Security 框架,从而通过标准 Java API 使用各种密码算法。
功能特性
HiTLS4J 提供以下密码功能:
对称密码算法
- AES:支持 ECB、CBC、CTR 和 GCM 模式,并提供多种填充选项
- SM4:支持 ECB、CBC、CTR、GCM、CFB、OFB 和 XTS 模式,并提供多种填充选项
非对称密码算法
- RSA:支持基于 PKCS#1 填充的加密/解密
- SM2:中国基于椭圆曲线的非对称加密标准
消息摘要
- SHA 系列:SHA-1、SHA-224、SHA-256、SHA-384、SHA-512
- SHA3 系列:SHA3-224、SHA3-256、SHA3-384、SHA3-512
- SM3:中国密码哈希函数
消息认证码(MAC)
- HMAC:基于全部受支持的哈希算法(SHA 系列、SHA3 系列、SM3)
确定性随机比特生成器
- Hash_DRBG:SHA-1、SHA-224、SHA-256、SHA-384、SHA-512 和 SM3
- 带推导函数的 CTR_DRBG:AES-128、AES-192、AES-256 和 SM4
数字签名
- RSA:支持 SHA-224、SHA-256、SHA-384、SHA-512 和 SM3 哈希算法
- RSA-PSS:基于多种哈希算法的概率签名方案
- DSA:支持多种哈希算法的数字签名算法
- ECDSA:椭圆曲线数字签名算法
- SM2:中国基于椭圆曲线的数字签名标准
密钥生成
- RSA:密钥对生成
- DSA:密钥对生成
- EC/ECDSA/SM2:支持多种曲线的密钥对生成(secp256r1、secp384r1、secp521r1、sm2p256v1)
- 对称密钥:支持生成 AES 和 SM4 密钥
Provider 集成
- 外部 openHiTLS Provider:HiTLS4J 可加载外部 openHiTLS Provider,以使用该 Provider 暴露的算法
要求
- Java 8 或更高版本
- Maven 3.x
- 系统中构建好的 openHiTLS 头文件和共享库
- 用于构建 JNI 组件的 GCC 编译器
安装
前提条件
- 在系统中安装 openHiTLS 库
- 将
JAVA_HOME环境变量设置为你 JDK 的安装路径 - 确保 PATH 中已包含 GCC
从源码构建
-
克隆仓库:
git clone https://github.com/yourusername/hitls4j.git cd hitls4j -
为 Maven 配置 openHiTLS 根目录:
export OPENHITLS_ROOT=/path/to/openhitls你也可以将
-Dopenhitls.root=/path/to/openhitls传递给 Maven,或者将-Dopenhitls.root=/path/to/openhitls添加到.mvn/maven.config中。 该设置用于在构建时查找 openHiTLS 头文件和库。 -
构建项目:
mvn clean package
默认构建仅需 openHiTLS。它会构建 libhitls_crypto_jni.so,
将所需的 libhitls_*.so 文件复制到 target/native,并在 JAR 中
将这些原生库打包到 META-INF/native 下。它不会复制或
打包外部 Provider 库。
你也可以直接传递 openHiTLS 根目录:
mvn clean package -Dopenhitls.root=/path/to/openhitls
本地库加载
运行时,OPENHITLS_ROOT 和 openhitls.root 不会被视为本机库目录,也不会用于定位 hitls4j 的 JNI 库。
如果从本地构建运行,请传入完整的本机输出目录:
java -Dopenhitls.native.path=target/native ...
由 openhitls.native.path 配置的目录必须同时包含
libhitls_crypto_jni.so 和所需的 openHiTLS 共享库。
如果未配置本地路径,HiTLS4J 会回退使用打包的本地库。
打包的本地库直接存储在 META-INF/native 下;每个 JAR
只包含一个本地构建,并且不会在运行时选择特定架构的
子目录。
使用 JAR 中的外部 Provider
对于普通应用,HiTLS4J JAR 提供 Java JCE Provider 和 JNI 桥接。外部 openHiTLS Provider 会单独从目标机器上的 Provider 目录加载。
如果使用 mvn package 生成的 JAR,所需的 HiTLS4J/openHiTLS
本地库已经打包在 JAR 中,并会自动解包。如果改为从未打包的
本地构建运行,请按照上文说明传入 openhitls.native.path。
在应用启动期间、创建 HiTLS4J 加密对象之前,加载一次外部 openHiTLS Provider:
import java.security.MessageDigest;
import java.security.Security;
import org.openhitls.crypto.jce.provider.HiTls4jProvider;
import org.openhitls.crypto.jce.provider.ProviderConfig;
public final class ProviderExample {
public static void main(String[] args) throws Exception {
// Directory containing lib<providerName>.so.
String providerPath = "/path/to/openhitls/providers";
String providerName = "custom_hsm";
ProviderConfig.loadProvider(providerPath, providerName);
Security.addProvider(new HiTls4jProvider());
// Use algorithms exposed by the loaded provider.
MessageDigest md = MessageDigest.getInstance("SM3", HiTls4jProvider.PROVIDER_NAME);
byte[] digest = md.digest(new byte[] {1, 2, 3});
}
}
将 HiTLS4J JAR 加入类路径后,运行应用程序:
java -cp hitls4j-1.0.jar:your-app.jar com.example.ProviderExample
传递给 ProviderConfig.loadProvider(...) 的 provider 路径,是包含外部 provider 共享库的目录。对于名称为 custom_hsm 的 provider,openHiTLS 会从该目录加载 libcustom_hsm.so。
某些外部 provider 需要自身配置,例如硬件 SDK 库或后端共享库。请在启动 JVM 之前,按照外部 provider 自身的规则配置这些依赖项。HiTLS4J 不定义 provider 专属参数;ProviderConfig.loadProvider(...) 仅负责加载 openHiTLS provider,并将其选定供后续 HiTLS4J 操作使用。
provider 生命周期限制:
- 将
ProviderConfig.loadProvider(...)和unloadProvider()视为进程范围内的 provider 选择操作。 - 同一时间只能有一个外部 provider 处于激活状态。在已有 provider 加载的情况下调用
loadProvider(...)会失败;不支持 provider 替换/切换。 unloadProvider()会释放已加载的原生 provider 库上下文,并使新的 HiTLS4J 操作回归默认的 openHiTLS 实现。- 请勿将 provider 的加载或卸载与 HiTLS4J 加密上下文的创建或操作并发执行。
- 仅在应用静默点加载或卸载外部 provider:即在工作线程创建加密对象之前,且所有基于已加载 provider 创建的加密对象完成终结之后。
- 在基于已加载 provider 创建的任何加密对象仍有效、正在使用或等待终结时,请勿调用
unloadProvider()。待执行的终结器可能仍需要 provider 库上下文来释放原生资源。
尽可能在 HiTLS4J、外部 provider 及其 provider 专属依赖项之间使用同一套一致的 openHiTLS 构建。混合使用基于不同 openHiTLS 代码树构建的共享库,可能导致 provider 加载或符号解析失败。
使用
注册 provider
import java.security.Security;
import org.openhitls.crypto.jce.provider.HiTls4jProvider;
// Register the provider
Security.addProvider(new HiTls4jProvider());
使用对称加密(SM4)
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import org.openhitls.crypto.jce.provider.HiTls4jProvider;
// Create 128-bit SM4 key
byte[] keyBytes = new byte[16];
new java.security.SecureRandom().nextBytes(keyBytes);
SecretKeySpec key = new SecretKeySpec(keyBytes, "SM4");
// ECB mode with NoPadding
Cipher cipher = Cipher.getInstance("SM4/ECB/NoPadding", HiTls4jProvider.PROVIDER_NAME);
cipher.init(Cipher.ENCRYPT_MODE, key);
// Data must be block-aligned (16 bytes for SM4) when using NoPadding
byte[] plaintext = new byte[32]; // 2 blocks
new java.security.SecureRandom().nextBytes(plaintext);
byte[] ciphertext = cipher.doFinal(plaintext);
// Decrypt
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decrypted = cipher.doFinal(ciphertext);
使用对称加密(AES)
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import org.openhitls.crypto.jce.provider.HiTls4jProvider;
// Create key and IV
byte[] keyBytes = new byte[16]; // 128-bit key
byte[] ivBytes = new byte[16]; // 16-byte IV
// ... initialize key and IV with secure random data
// Create key specification
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec iv = new IvParameterSpec(ivBytes);
// Create and initialize cipher
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", HiTls4jProvider.PROVIDER_NAME);
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
// Encrypt data
byte[] plaintext = "Hello, world!".getBytes();
byte[] ciphertext = cipher.doFinal(plaintext);
// Decrypt data
cipher.init(Cipher.DECRYPT_MODE, key, iv);
byte[] decrypted = cipher.doFinal(ciphertext);
使用消息摘要(SHA-256)
import java.security.MessageDigest;
import org.openhitls.crypto.jce.provider.HiTls4jProvider;
// Create message digest
MessageDigest md = MessageDigest.getInstance("SHA-256", HiTls4jProvider.PROVIDER_NAME);
// Compute hash
byte[] data = "Hello, world!".getBytes();
byte[] hash = md.digest(data);
使用 HMAC
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.openhitls.crypto.jce.provider.HiTls4jProvider;
// Create key
byte[] keyBytes = new byte[32]; // 256-bit key
// ... initialize key with secure random data
SecretKeySpec key = new SecretKeySpec(keyBytes, "HMACSHA256");
// Create and initialize HMAC
Mac mac = Mac.getInstance("HMACSHA256", HiTls4jProvider.PROVIDER_NAME);
mac.init(key);
// Compute HMAC
byte[] data = "Hello, world!".getBytes();
byte[] hmac = mac.doFinal(data);
使用 PBKDF2
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.SecureRandom;
import java.util.Arrays;
import org.openhitls.crypto.jce.provider.HiTls4jProvider;
char[] password = "password".toCharArray();
byte[] salt = new byte[16];
new SecureRandom().nextBytes(salt);
PBEKeySpec keySpec = new PBEKeySpec(password, salt, 100_000, 256);
SecretKey key = null;
byte[] derivedKey = null;
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance(
"PBKDF2WithHmacSHA256", HiTls4jProvider.PROVIDER_NAME);
key = factory.generateSecret(keySpec);
derivedKey = key.getEncoded();
// Use derivedKey.
} finally {
if (derivedKey != null) {
Arrays.fill(derivedKey, (byte) 0);
}
keySpec.clearPassword();
Arrays.fill(password, '\0');
if (key != null) {
key.destroy();
}
}
使用 DRBG
import java.security.SecureRandom;
import org.openhitls.crypto.jce.provider.HiTls4jProvider;
// The standard JCA name selects Hash_DRBG with SHA-256.
SecureRandom random = SecureRandom.getInstance(
"DRBG", HiTls4jProvider.PROVIDER_NAME);
byte[] nonce = new byte[32];
random.nextBytes(nonce);
// Provider-specific names select an explicit openHiTLS DRBG mechanism.
SecureRandom sm4Drbg = SecureRandom.getInstance(
"CTR-DRBG-SM4", HiTls4jProvider.PROVIDER_NAME);
byte[] keyMaterial = sm4Drbg.generateSeed(32);
HiTLS4J 面向 Java 8,因此实现 Java 8 的 SecureRandomSpi
契约。后续 JCA 标准名称规范中引入的标准 DRBG 服务名称已得到支持,而 Java 9 的 DrbgParameters 重载方法并非 Java 8 API 的一部分。显式机制名称是 HiTLS4J 提供者的扩展。
使用 RSA 签名
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Signature;
import org.openhitls.crypto.jce.provider.HiTls4jProvider;
// Generate key pair
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA", HiTls4jProvider.PROVIDER_NAME);
keyGen.initialize(2048);
KeyPair keyPair = keyGen.generateKeyPair();
// Create and initialize signature
Signature signature = Signature.getInstance("SHA256withRSA", HiTls4jProvider.PROVIDER_NAME);
signature.initSign(keyPair.getPrivate());
// Sign data
byte[] data = "Hello, world!".getBytes();
signature.update(data);
byte[] signatureBytes = signature.sign();
// Verify signature
signature.initVerify(keyPair.getPublic());
signature.update(data);
boolean valid = signature.verify(signatureBytes);
RSA 密钥编码与解码
RSA 密钥导入和导出是 HiTLS4J 提供者契约的一部分。RSA 公钥支持 X.509 SubjectPublicKeyInfo 编码,RSA 私钥使用 PKCS#8 编码。编码后的 RSA 密钥可通过标准 JCE 密钥规范接受,例如 X509EncodedKeySpec、PKCS8EncodedKeySpec、RSAPublicKeySpec 和 RSAPrivateKeySpec。
RSA 公钥 DER 操作和 RSA 私钥 PKCS#8 编码使用 openHiTLS 密钥编解码 API(CRYPT_EAL_EncodeBuffKey 和 CRYPT_EAL_DecodeBuffKey)。仅包含 n、e 和 d 但不含 CRT 参数的 RSA 私钥可以进行 PKCS#8 编码和解码。仅包含 n 和 d 并从 RSAPrivateKeySpec 导入的最简私钥,其公钥指数保持未知;HiTLS4J 不会为这些密钥合成 65537,不声明支持它们的 PKCS#8 编码,并拒绝需要 e 的私钥操作。HiTLS4J 在解析或重新编码 RSA 密钥时,不依赖厂商特定的 JDK 提供者,例如 SunRsaSign。
使用 ECDSA 签名
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Signature;
import java.security.spec.ECGenParameterSpec;
import org.openhitls.crypto.jce.provider.HiTls4jProvider;
// Generate key pair
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDSA", HiTls4jProvider.PROVIDER_NAME);
keyGen.initialize(new ECGenParameterSpec("secp256r1"));
KeyPair keyPair = keyGen.generateKeyPair();
// Create and initialize signature
Signature signature = Signature.getInstance("SHA256withECDSA", HiTls4jProvider.PROVIDER_NAME);
signature.initSign(keyPair.getPrivate());
// Sign data
byte[] data = "Hello, world!".getBytes();
signature.update(data);
byte[] signatureBytes = signature.sign();
// Verify signature
signature.initVerify(keyPair.getPublic());
signature.update(data);
boolean valid = signature.verify(signatureBytes);
EC 密钥编码与解码
HiTLS4J 注册了通用的 EC 密钥生成、密钥工厂和算法参数服务,以保持兼容性。它还注册了显式的 ECDSA 和 SM2 密钥对生成器以及密钥工厂服务,用于特定曲线族场景。
EC 算法参数通过通用的 EC 服务暴露。ECDSA 接受 secp256r1、secp384r1 和 secp521r1;SM2 接受 sm2p256v1;通用 EC 接受所有受支持的 EC 曲线。
EC 公钥可以通过 X.509 SubjectPublicKeyInfo 导入和导出,EC 私钥可以通过 PKCS#8 导入和导出。EC 密钥工厂会解析命名曲线 OID,并拒绝曲线族与请求工厂不匹配的编码密钥,例如通过 ECDSA 工厂导入 SM2 密钥。
EC 算法参数可以针对 secp256r1、secp384r1、secp521r1 和 sm2p256v1 以 DER 命名曲线对象标识符形式导入和导出。
标准 JDK EC 公钥和私钥对象可被 ECDSA 签名实现接受。当证书使用受支持的 ECDSA 曲线时,由 Java 默认 CertificateFactory 解析的证书可以使用 HiTLS4J 签名进行验证。HiTLS4J 不自行实现 X.509 CertificateFactory;请使用 JDK CertificateFactory 解析证书。
使用 MLDSA
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.Signature;
import org.openhitls.crypto.jce.spec.MLDSAGenParameterSpec;
import org.openhitls.crypto.jce.HiTls4jProvider;
import static org.junit.jupiter.api.Assertions.assertTrue;
// Generate key pair
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ML-DSA", HiTls4jProvider.PROVIDER_NAME);
keyGen.initialize(new MLDSAGenParameterSpec("ML-DSA-44"), new SecureRandom());
KeyPair keyPair = keyGen.generateKeyPair();
// Sign data
byte[] data = "Hello, world!".getBytes();
Signature signer = Signature.getInstance("SHA256withMLDSA", HiTls4jProvider.PROVIDER_NAME);
signer.initSign(keyPair.getPrivate());
signer.update(data);
byte[] signature = signer.sign();
// Verify signature
signer.initVerify(keyPair.getPublic());
signer.update(data);
boolean verified = signer.verify(signature);
使用 MLKEM
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.KeyAgreement;
import org.openhitls.crypto.jce.spec.MLKEMGenParameterSpec;
import org.openhitls.crypto.jce.spec.MLKEMCiphertextKey;
import org.openhitls.crypto.jce.HiTls4jProvider;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
// Generate key pair
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ML-KEM", HiTls4jProvider.PROVIDER_NAME);
keyGen.initialize(new MLKEMGenParameterSpec("ML-KEM-512"), new SecureRandom());
KeyPair keyPair = keyGen.generateKeyPair();
// Encapsulation side
KeyAgreement kaSender = KeyAgreement.getInstance("ML-KEM", HiTls4jProvider.PROVIDER_NAME);
kaSender.init(keyPair.getPublic());
byte[] ciphertext = kaSender.doPhase(null, true).getEncoded();
byte[] senderSharedKey = kaSender.generateSecret();
// Decapsulation side
KeyAgreement kaReceiver = KeyAgreement.getInstance("ML-KEM", HiTls4jProvider.PROVIDER_NAME);
kaReceiver.init(keyPair.getPrivate());
kaReceiver.doPhase(new MLKEMCiphertextKey(ciphertext), true);
byte[] receiverSharedKey = kaReceiver.generateSecret();
支持的算法
加密算法
AES(工作模式:ECB、CBC、CTR、GCM)SM4(工作模式:ECB、CBC、CTR、GCM、CFB、OFB、XTS)RSASM2
消息摘要算法
SHA-1SHA-224、SHA-256、SHA-384、SHA-512SHA3-224、SHA3-256、SHA3-384、SHA3-512SM3
MAC 算法
HMACSHA1HMACSHA224、HMACSHA256、HMACSHA384、HMACSHA512HMACSHA3-224、HMACSHA3-256、HMACSHA3-384、HMACSHA3-512HMACSM3
基于口令的密钥派生算法
PBKDF2WithHmacSHA1PBKDF2WithHmacSHA224、PBKDF2WithHmacSHA256、PBKDF2WithHmacSHA384、PBKDF2WithHmacSHA512PBKDF2WithHmacSHA3-224、PBKDF2WithHmacSHA3-256、PBKDF2WithHmacSHA3-384、PBKDF2WithHmacSHA3-512
安全随机数算法
DRBG(Hash_DRBG,采用 SHA-256)HASH-DRBG-SHA-1、HASH-DRBG-SHA-224、HASH-DRBG-SHA-256、HASH-DRBG-SHA-384、HASH-DRBG-SHA-512HASH-DRBG-SM3CTR-DRBG-AES-128、CTR-DRBG-AES-192、CTR-DRBG-AES-256(已启用派生函数)CTR-DRBG-SM4(已启用派生函数)
签名算法
SHA1withRSA、SHA224withRSA、SHA256withRSA、SHA384withRSA、SHA512withRSA、SM3withRSA- RSA 别名:
SHA1withRSAEncryption、SHA224withRSAEncryption、SHA256withRSAEncryption、SHA384withRSAEncryption、SHA512withRSAEncryption - RSA OID 别名:
1.2.840.113549.1.1.5、1.2.840.113549.1.1.14、1.2.840.113549.1.1.11、1.2.840.113549.1.1.12、1.2.840.113549.1.1.13 SHA224withRSA/PSS、SHA256withRSA/PSS、SHA384withRSA/PSS、SHA512withRSA/PSS- 不支持
SM3withRSA/PSS,因为原生 RSA-PSS 参数路径会拒绝 SM3。 SHA256withECDSA、SHA384withECDSA、SHA512withECDSASM3withSM2
密钥生成算法
RSADSAEC(通用,曲线:secp256r1、secp384r1、secp521r1、sm2p256v1)ECDSA(曲线:secp256r1、secp384r1、secp521r1)SM2(曲线:sm2p256v1)AESSM4
PQC 算法
ML-KEM-512、ML-KEM-768、ML-KEM-1024ML-DSA-44、ML-DSA-65、ML-DSA-87SLH-DSA-SHA2-128s、SLH-DSA-SHA2-128f、SLH-DSA-SHA2-192s、SLH-DSA-SHA2-192f、SLH-DSA-SHA2-256s、SLH-DSA-SHA2-256fSLH-DSA-SHAKE-128s、SLH-DSA-SHAKE-128f、SLH-DSA-SHAKE-192s、SLH-DSA-SHAKE-192f、SLH-DSA-SHAKE-256s、SLH-DSA-SHAKE-256fFrodoKEM-640-SHAKE、FrodoKEM-640-AES、FrodoKEM-976-SHAKE、FrodoKEM-976-AES、FrodoKEM-1344-SHAKE、FrodoKEM-1344-AESMcEliece-6688128、McEliece-6688128f、McEliece-6688128pc、McEliece-6688128pcfMcEliece-6960119、McEliece-6960119f、McEliece-6960119pc、McEliece-6960119pcfMcEliece-8192128、McEliece-8192128f、McEliece-8192128pc、McEliece-8192128pcf
许可证
本项目按照仓库中所包含许可证的条款进行授权。
致谢
- 本项目基于 openHiTLS 密码学库
- 感谢所有参与本项目开发的贡献者