openHiTLS Java SDK,兼容JCE
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 17 天前 | ||
| 1 个月前 | ||
| 26 天前 | ||
| 20 天前 | ||
| 17 天前 |
HiTLS4J
HiTLS4J 是一款 Java 加密扩展(JCE)提供程序,它封装了原生 openHiTLS 加密库。该提供程序完整实现了 JCE API,使 Java 应用程序能够通过标准 Java 安全接口使用 openHiTLS 提供的加密算法。
概述
HiTLS4J 通过 JNI(Java 原生接口)将 openHiTLS 加密库与 Java 应用程序集成。它实现了一个可注册到 Java 安全框架的 JCE 提供程序,从而能够通过标准 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)
数字签名
- 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 的密钥生成
提供程序集成
- 外部 openHiTLS 提供程序:HiTLS4J 可以加载外部 openHiTLS 提供程序,以使用该提供程序公开的算法
要求
- Java 8 或更高版本
- Maven 3.x
- 系统上已构建的 openHiTLS 头文件和共享库
- 用于构建 JNI 组件的 GCC 编译器
安装
前提条件
- 在系统上安装 openHiTLS 库
- 将
JAVA_HOME环境变量设置为您的 JDK 安装路径 - 确保 GCC 在您的 PATH 中可用
从源代码构建
-
克隆仓库:
git clone https://github.com/yourusername/hitls4j.git cd hitls4j -
为 Maven 配置 openHiTLS 根目录:
export OPENHITLS_ROOT=/path/to/openhitls您也可以向 Maven 传递
-Dopenhitls.root=/path/to/openhitls,或在.mvn/maven.config中添加-Dopenhitls.root=/path/to/openhitls。 此设置在构建时用于查找 openHiTLS 头文件和库。 -
构建项目:
mvn clean package
默认构建仅需要 openHiTLS。它会构建 libhitls_crypto_jni.so,将所需的 libhitls_*.so 文件复制到 target/native,并将这些原生库打包到 JAR 中的 META-INF/native 目录下。它不会复制或打包外部提供程序库。
您也可以直接传递 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 使用外部提供者
对于普通应用程序,HiTLS4J JAR 提供 Java JCE 提供者和 JNI 桥接。外部 openHiTLS 提供者从目标机器上的提供者目录单独加载。
如果使用 mvn package 生成的 JAR,所需的 HiTLS4J/openHiTLS 本地库会被打包到 JAR 中,并自动提取。如果从未打包的本地构建运行,则按上述说明传递 openhitls.native.path。
在应用程序启动期间,创建 HiTLS4J 加密对象之前,加载一次外部 openHiTLS 提供者:
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(...) 的提供程序路径是包含外部提供程序共享库的目录。对于提供程序名称 custom_hsm,openHiTLS 会从该目录加载 libcustom_hsm.so。
某些外部提供程序需要自身的配置,例如硬件 SDK 库或后端共享库。在启动 JVM 之前,请根据外部提供程序自身的规则配置这些依赖项。HiTLS4J 不定义特定于提供程序的参数;ProviderConfig.loadProvider(...) 仅加载 openHiTLS 提供程序,并将其选择用于后续的 HiTLS4J 操作。
提供程序生命周期限制:
- 将
ProviderConfig.loadProvider(...)和unloadProvider()视为进程范围的提供程序选择操作。 - 一次只能有一个外部提供程序处于活动状态。在已加载提供程序的情况下调用
loadProvider(...)将失败;不支持提供程序替换/切换。 unloadProvider()释放已加载的原生提供程序库上下文,并将新的 HiTLS4J 操作返回到默认的 openHiTLS 实现。- 不要在与 HiTLS4J 加密上下文创建或操作并发时调用加载或卸载。
- 仅在应用程序静止点加载和卸载外部提供程序,即在工作线程创建加密对象之前,以及在已加载提供程序下创建的所有加密对象都已完成之后。
- 当已加载提供程序下创建的任何加密对象处于活动状态、正在使用中或等待完成时,不要调用
unloadProvider()。挂起的完成器可能仍需要提供程序库上下文来释放原生资源。
尽可能在 HiTLS4J、外部提供程序及其特定于提供程序的依赖项之间使用一致的 openHiTLS 构建。混合使用针对不同 openHiTLS 树构建的共享库可能会导致提供程序加载或符号解析失败。
用法
注册提供程序
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);
使用 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 不需要特定供应商的 JDK 提供程序(如 SunRsaSign)来进行 RSA 密钥解析或重新编码。
使用 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 算法参数可作为 DER 命名曲线对象标识符导入和导出,支持的曲线包括 secp256r1、secp384r1、secp521r1 和 sm2p256v1。
ECDSA 签名实现接受标准 JDK 的 EC 公钥和私钥对象。这使得当证书使用受支持的 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
签名算法
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
后量子密码算法
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 密码库开发
- 感谢所有为项目开发提供帮助的贡献者