hitls4j:基于 openHiTLS 的 JCE 加密服务提供者项目

openHiTLS Java SDK,兼容JCE

分支1Tags0

HiTLS4J

HiTLS4J is a Java Cryptography Extension (JCE) and Java Secure Socket Extension (JSSE) provider that wraps the native openHiTLS library. It provides a complete implementation of the JCE API, allowing Java applications to use the cryptographic algorithms provided by openHiTLS through standard Java security interfaces, and a JSSE implementation that brings the openHiTLS protocol stack — including TLS 1.2, TLS 1.3, TLCP and DTLCP — to standard SSLContext / SSLSocket / SSLEngine APIs.

Overview

HiTLS4J integrates the openHiTLS cryptographic library with Java applications through JNI (Java Native Interface). It implements a JCE provider that can be registered with the Java Security framework, enabling the use of various cryptographic algorithms through standard Java APIs.

Features

HiTLS4J provides the following cryptographic functionalities:

Secure Transport Protocols (JSSE)

  • TLS 1.2 / TLS 1.3: SSLSocket, SSLServerSocket and SSLEngine
  • TLCP 1.1 (GB/T 38636-2020, 国密双证书): SSLSocket, SSLServerSocket and SSLEngine
  • DTLCP 1.1 / DTLS 1.2: SSLEngine (datagram transports)

Symmetric Ciphers

  • AES: Supports ECB, CBC, CTR, and GCM modes with various padding options
  • SM4: Supports ECB, CBC, CTR, GCM, CFB, OFB, and XTS modes with various padding options

Asymmetric Ciphers

  • RSA: Encryption/decryption with PKCS#1 padding
  • SM2: Chinese standard for elliptic curve-based asymmetric encryption

Message Digests

  • SHA Family: SHA-1, SHA-224, SHA-256, SHA-384, SHA-512
  • SHA3 Family: SHA3-224, SHA3-256, SHA3-384, SHA3-512
  • SM3: Chinese cryptographic hash function

Message Authentication Codes (MACs)

  • HMAC: With all supported hash algorithms (SHA family, SHA3 family, SM3)

Digital Signatures

  • RSA: With SHA-224, SHA-256, SHA-384, SHA-512, and SM3 hash algorithms
  • RSA-PSS: Probabilistic Signature Scheme with various hash algorithms
  • DSA: Digital Signature Algorithm with various hash algorithms
  • ECDSA: Elliptic Curve Digital Signature Algorithm
  • SM2: Chinese standard for elliptic curve-based digital signatures

Key Generation

  • RSA: Key pair generation
  • DSA: Key pair generation
  • EC: Key pair generation for various curves (secp256r1, secp384r1, secp521r1, sm2p256v1)
  • Symmetric Keys: Generation for AES and SM4

Requirements

  • Java 17 or higher
  • openHiTLS library installed on the system
  • GCC compiler for building the JNI component

Installation

Prerequisites

  1. Install the openHiTLS library on your system
  2. Set the JAVA_HOME environment variable to your JDK installation
  3. Ensure GCC is available in your PATH

Building from Source

  1. Clone the repository:

    git clone https://github.com/openhitls/hitls4j.git
    cd hitls4j
    
  2. Build the project with the openHiTLS root directory:

    mvn clean package -Dopenhitls.root=/path/to/openhitls
    

    You can also configure the default path in pom.xml:

    <properties>
        <openhitls.root>/path/to/openhitls</openhitls.root>
    </properties>
    

Usage

Registering the Provider

import java.security.Security;
import org.openhitls.crypto.jce.provider.HiTls4jProvider;

// Register the provider
Security.addProvider(new HiTls4jProvider());

Using Symmetric Encryption (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);

Using Message Digest (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);

Using 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);

Using RSA Signatures

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);

Using ECDSA Signatures

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("EC", 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);

Using 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);

Using 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();

Using the JSSE Provider (TLS / TLCP / DTLCP)

The provider registers the following SSLContext algorithms:

Name Protocols APIs
TLS TLS 1.2 + TLS 1.3 (negotiated) sockets + engine
TLSv1.2 TLS 1.2 sockets + engine
TLSv1.3 TLS 1.3 sockets + engine
TLCP (aliases TLCPv1.1, GMTLS) TLCP 1.1 sockets + engine
DTLCP (alias DTLCPv1.1) DTLCP 1.1 engine only
DTLSv1.2 DTLS 1.2 engine only

Key material can be supplied two ways:

  1. Standard JSSE: X509KeyManager/X509TrustManager from KeyManagerFactory/TrustManagerFactory are supported for conventional TLS server flows. Private keys must be PKCS#8-encodable. JCA X509KeyManager client-certificate selection is not used because the native stack does not expose CertificateRequest issuer hints to Java.
  2. Raw DER material via HiTlsKeyManager/HiTlsTrustManager: passes DER bytes straight to the native stack. This is required for client certificates, SM2 keys and TLCP double certificates, which the JDK cannot model.

TLS 1.3 client and server

import java.security.Security;
import javax.net.ssl.*;
import org.openhitls.crypto.jce.provider.HiTls4jProvider;
import org.openhitls.tls.jsse.HiTlsKeyManager;
import org.openhitls.tls.jsse.HiTlsTrustManager;

Security.addProvider(new HiTls4jProvider());

// Server: certificate chain + PKCS#8/SEC1 private key as DER bytes
HiTlsKeyManager serverKeys = HiTlsKeyManager.create(serverCertDer, serverKeyDer, interCaDer);
SSLContext serverCtx = SSLContext.getInstance("TLSv1.3", HiTls4jProvider.PROVIDER_NAME);
serverCtx.init(new KeyManager[] {serverKeys}, null, null);
SSLServerSocket server = (SSLServerSocket) serverCtx.getServerSocketFactory().createServerSocket(8443);

// Client: trust anchors as DER bytes
SSLContext clientCtx = SSLContext.getInstance("TLSv1.3", HiTls4jProvider.PROVIDER_NAME);
clientCtx.init(null, new TrustManager[] {HiTlsTrustManager.fromDer(rootCaDer)}, null);
SSLSocket client = (SSLSocket) clientCtx.getSocketFactory().createSocket("example.com", 8443);
SSLParameters sslParameters = client.getSSLParameters();
sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
client.setSSLParameters(sslParameters);
client.startHandshake();

TLCP 1.1 with SM2 double certificates

TLCP requires a signing certificate and a separate encryption certificate.

HiTlsKeyManager tlcpKeys = HiTlsKeyManager.createTlcp(
    signCertDer, signKeyDer,   // signing certificate + key
    encCertDer, encKeyDer,     // encryption certificate + key
    interCaDer);               // optional chain

SSLContext serverCtx = SSLContext.getInstance("TLCP", HiTls4jProvider.PROVIDER_NAME);
serverCtx.init(new KeyManager[] {tlcpKeys}, null, null);
SSLServerSocket server = (SSLServerSocket) serverCtx.getServerSocketFactory().createServerSocket(9443);

SSLContext clientCtx = SSLContext.getInstance("TLCP", HiTls4jProvider.PROVIDER_NAME);
clientCtx.init(null, new TrustManager[] {HiTlsTrustManager.fromDer(tlcpCaDer)}, null);
SSLSocket client = (SSLSocket) clientCtx.getSocketFactory().createSocket("127.0.0.1", 9443);
client.setEnabledCipherSuites(new String[] {"ECC_SM4_CBC_SM3"});
client.startHandshake();

TLCP cipher suites: ECC_SM4_CBC_SM3, ECC_SM4_GCM_SM3, ECDHE_SM4_CBC_SM3, ECDHE_SM4_GCM_SM3. ECC_* suites use the server static encryption certificate for key exchange. ECDHE_* suites provide forward secrecy via ephemeral ECDH. For mutual TLS, supply client TLCP signing/encryption credentials via HiTlsKeyManager.createTlcp(...) and enable setNeedClientAuth(true) or setWantClientAuth(true); the same client-credential requirement applies to both ECC_* and ECDHE_* suites.

DTLCP with SSLEngine

Datagram protocols (DTLCP, DTLS 1.2) follow the JDK DTLS model: they are engine-only, and each wrap() produces one datagram to send.

SSLContext ctx = SSLContext.getInstance("DTLCP", HiTls4jProvider.PROVIDER_NAME);
ctx.init(new KeyManager[] {tlcpKeys}, null, null);
SSLEngine engine = ctx.createSSLEngine();
engine.setUseClientMode(false);
// drive engine.wrap()/engine.unwrap() against a DatagramSocket/DatagramChannel

Notes

  • Mutual authentication uses the standard setNeedClientAuth(true) / setWantClientAuth(true) APIs.
  • ALPN is supported through SSLParameters.setApplicationProtocols and getApplicationProtocol().
  • Certificate verification uses openHiTLS trust anchors when native verification is configured; standard/custom Java X509TrustManager checks may run afterward or instead, depending on the configured trust manager. HiTlsTrustManager.insecureTrustAll() disables peer verification (testing only) and is not compatible with server-side client authentication.
  • For peer certificates whose algorithms the JDK cannot model (SM2), SSLSession.getPeerCertificates() returns a structural X509Certificate view (DerX509Certificate) exposing subject/issuer/validity/encoding; getPublicKey() is unsupported on it.
  • Java-side SSLSessionContext caching is exposed with timeout and capacity limits; native session resumption (session ID/ticket abbreviated handshakes) and renegotiation are not currently wired up.

Supported Algorithms

Cipher Algorithms

  • AES (with modes: ECB, CBC, CTR, GCM)
  • SM4 (with modes: ECB, CBC, CTR, GCM, CFB, OFB, XTS)
  • RSA
  • SM2

Message Digest Algorithms

  • SHA-1
  • SHA-224, SHA-256, SHA-384, SHA-512
  • SHA3-224, SHA3-256, SHA3-384, SHA3-512
  • SM3

MAC Algorithms

  • HMACSHA1
  • HMACSHA224, HMACSHA256, HMACSHA384, HMACSHA512
  • HMACSHA3-224, HMACSHA3-256, HMACSHA3-384, HMACSHA3-512
  • HMACSM3

Signature Algorithms

  • SHA224withRSA, SHA256withRSA, SHA384withRSA, SHA512withRSA, SM3withRSA
  • SHA224withRSA/PSS, SHA256withRSA/PSS, SHA384withRSA/PSS, SHA512withRSA/PSS, SM3withRSA/PSS
  • SHA256withECDSA, SHA384withECDSA, SHA512withECDSA
  • SM3withSM2

Key Generation Algorithms

  • RSA
  • DSA
  • EC (with curves: secp256r1, secp384r1, secp521r1, sm2p256v1)
  • AES
  • SM4

TLS Protocols (JSSE)

  • TLS (TLS 1.2 + TLS 1.3), TLSv1.2, TLSv1.3
  • TLCP (TLCP 1.1, GB/T 38636-2020), DTLCP (engine only)
  • DTLSv1.2 (engine only)

PQC Algorithms

  • ML-KEM-512, ML-KEM-768, ML-KEM-1024
  • ML-DSA-44, ML-DSA-65, ML-DSA-87
  • SLH-DSA-SHA2-128s, SLH-DSA-SHA2-128f, SLH-DSA-SHA2-192s, SLH-DSA-SHA2-192f, SLH-DSA-SHA2-256s, SLH-DSA-SHA2-256f
  • SLH-DSA-SHAKE-128s, SLH-DSA-SHAKE-128f, SLH-DSA-SHAKE-192s, SLH-DSA-SHAKE-192f, SLH-DSA-SHAKE-256s, SLH-DSA-SHAKE-256f
  • FrodoKEM-640-SHAKE, FrodoKEM-640-AES, FrodoKEM-976-SHAKE, FrodoKEM-976-AES, FrodoKEM-1344-SHAKE, FrodoKEM-1344-AES
  • McEliece-6688128, McEliece-6688128f, McEliece-6688128pc, McEliece-6688128pcf
  • McEliece-6960119, McEliece-6960119f, McEliece-6960119pc, McEliece-6960119pcf
  • McEliece-8192128, McEliece-8192128f, McEliece-8192128pc, McEliece-8192128pcf

License

This project is licensed under the terms of the license included in the repository.

Acknowledgments

  • This project is based on the openHiTLS cryptographic library
  • Thanks to all contributors who have helped with the development of this project

项目介绍

openHiTLS Java SDK,兼容JCE

定制我的领域