gateway2/java-client/native/gmssl_jni.c
2026-07-11 13:40:57 +08:00

293 lines
8.9 KiB
C

/*
* gmssl_jni.c - JNI ????? Tongsuo SSL ?
* ? C ??? TCP ?? + SSL ????? Java fd ??
*
* ??:
* gcc -shared -fPIC -o libgmssl_jni.so \
* -I$JAVA_HOME/include -I$JAVA_HOME/include/linux \
* -I/opt/tongsuo-8.4.0/include \
* -L/opt/tongsuo-8.4.0/lib64 \
* gmssl_jni.c -lssl -lcrypto -ldl
*/
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/bio.h>
#include "com_gateway_ssl_jni_GmSSLJNI.h"
/* ==================== ???? ==================== */
static void throw_ssl_error(JNIEnv *env, const char *msg) {
unsigned long err;
char errbuf[256];
err = ERR_get_error();
ERR_error_string_n(err, errbuf, sizeof(errbuf));
char fullmsg[512];
snprintf(fullmsg, sizeof(fullmsg), "%s: %s", msg, errbuf);
jclass exClass = (*env)->FindClass(env, "java/lang/RuntimeException");
if (exClass != NULL) {
(*env)->ThrowNew(env, exClass, fullmsg);
}
}
static char* jstring_to_cstr(JNIEnv *env, jstring jstr) {
if (jstr == NULL) return NULL;
const char *chars = (*env)->GetStringUTFChars(env, jstr, NULL);
char *copy = strdup(chars);
(*env)->ReleaseStringUTFChars(env, jstr, chars);
return copy;
}
/* ==================== ???? ==================== */
JNIEXPORT jlong JNICALL Java_com_gateway_ssl_jni_GmSSLJNI_init
(JNIEnv *env, jclass cls) {
SSL_library_init();
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
ERR_load_crypto_strings();
return (jlong)1;
}
JNIEXPORT void JNICALL Java_com_gateway_ssl_jni_GmSSLJNI_cleanup
(JNIEnv *env, jclass cls, jlong ctx) {}
/* ==================== SSL ??? ==================== */
JNIEXPORT jlong JNICALL Java_com_gateway_ssl_jni_GmSSLJNI_createSSLContext
(JNIEnv *env, jclass cls, jlong ctx, jboolean isServer, jboolean tls13) {
SSL_CTX *sslCtx = SSL_CTX_new(TLS_method());
if (sslCtx == NULL) {
throw_ssl_error(env, "SSL_CTX_new failed");
return 0;
}
SSL_CTX_set_mode(sslCtx, SSL_MODE_AUTO_RETRY);
return (jlong)(intptr_t)sslCtx;
}
JNIEXPORT void JNICALL Java_com_gateway_ssl_jni_GmSSLJNI_setCipherSuites
(JNIEnv *env, jclass cls, jlong sslCtx, jstring cipherList) {
SSL_CTX *ctx = (SSL_CTX *)(intptr_t)sslCtx;
char *ciphers = jstring_to_cstr(env, cipherList);
if (SSL_CTX_set_ciphersuites(ctx, ciphers) != 1) {
throw_ssl_error(env, "SSL_CTX_set_ciphersuites failed");
}
SSL_CTX_set_cipher_list(ctx, ciphers);
free(ciphers);
}
JNIEXPORT void JNICALL Java_com_gateway_ssl_jni_GmSSLJNI_loadCertificate
(JNIEnv *env, jclass cls, jlong sslCtx, jstring certPath, jstring keyPath) {
SSL_CTX *ctx = (SSL_CTX *)(intptr_t)sslCtx;
char *cert = jstring_to_cstr(env, certPath);
char *key = jstring_to_cstr(env, keyPath);
if (SSL_CTX_use_certificate_file(ctx, cert, SSL_FILETYPE_PEM) != 1) {
throw_ssl_error(env, "SSL_CTX_use_certificate_file failed");
free(cert); free(key);
return;
}
if (SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) != 1) {
throw_ssl_error(env, "SSL_CTX_use_PrivateKey_file failed");
free(cert); free(key);
return;
}
if (SSL_CTX_check_private_key(ctx) != 1) {
throw_ssl_error(env, "Private key does not match certificate");
}
free(cert);
free(key);
}
JNIEXPORT void JNICALL Java_com_gateway_ssl_jni_GmSSLJNI_loadEncCertificate
(JNIEnv *env, jclass cls, jlong sslCtx, jstring certPath, jstring keyPath) {}
JNIEXPORT void JNICALL Java_com_gateway_ssl_jni_GmSSLJNI_loadCAFile
(JNIEnv *env, jclass cls, jlong sslCtx, jstring caFile) {
SSL_CTX *ctx = (SSL_CTX *)(intptr_t)sslCtx;
char *ca = jstring_to_cstr(env, caFile);
if (SSL_CTX_load_verify_locations(ctx, ca, NULL) != 1) {
throw_ssl_error(env, "SSL_CTX_load_verify_locations failed");
}
free(ca);
}
JNIEXPORT void JNICALL Java_com_gateway_ssl_jni_GmSSLJNI_setVerifyPeer
(JNIEnv *env, jclass cls, jlong sslCtx, jboolean verify) {
SSL_CTX *ctx = (SSL_CTX *)(intptr_t)sslCtx;
if (verify) {
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
} else {
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
}
}
/* ==================== SSL ?????? C ?? ==================== */
/**
* ?? TCP ??
*/
static int tcp_connect(const char *host, int port) {
struct sockaddr_in addr;
struct hostent *he;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
// ?? IP ??
if (inet_pton(AF_INET, host, &addr.sin_addr) != 1) {
// ?? DNS ??
he = gethostbyname(host);
if (he == NULL) return -1;
memcpy(&addr.sin_addr, he->h_addr_list[0], he->h_length);
}
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) return -1;
struct timeval tv;
tv.tv_sec = 10;
tv.tv_usec = 0;
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
close(sock);
return -1;
}
return sock;
}
JNIEXPORT jlong JNICALL Java_com_gateway_ssl_jni_GmSSLJNI_connect
(JNIEnv *env, jclass cls, jlong sslCtx, jstring jhost, jint port) {
SSL_CTX *ctx = (SSL_CTX *)(intptr_t)sslCtx;
char *host = jstring_to_cstr(env, jhost);
// TCP ??
int sock = tcp_connect(host, (int)port);
if (sock < 0) {
char errbuf[256];
snprintf(errbuf, sizeof(errbuf), "TCP connect failed to %s:%d", host, (int)port);
free(host);
throw_ssl_error(env, errbuf);
return 0;
}
free(host);
// ?? SSL ??
SSL *ssl = SSL_new(ctx);
if (ssl == NULL) {
close(sock);
throw_ssl_error(env, "SSL_new failed");
return 0;
}
SSL_set_fd(ssl, sock);
// ??
int ret = SSL_connect(ssl);
if (ret != 1) {
int err = SSL_get_error(ssl, ret);
char errbuf[256];
snprintf(errbuf, sizeof(errbuf), "SSL_connect failed: error=%d", err);
SSL_free(ssl);
close(sock);
throw_ssl_error(env, errbuf);
return 0;
}
return (jlong)(intptr_t)ssl;
}
JNIEXPORT jint JNICALL Java_com_gateway_ssl_jni_GmSSLJNI_send
(JNIEnv *env, jclass cls, jlong ssl, jbyteArray data, jint len) {
SSL *sslObj = (SSL *)(intptr_t)ssl;
jbyte *bytes = (*env)->GetByteArrayElements(env, data, NULL);
int ret = SSL_write(sslObj, bytes, len);
(*env)->ReleaseByteArrayElements(env, data, bytes, JNI_ABORT);
if (ret <= 0) {
int err = SSL_get_error(sslObj, ret);
return -err;
}
return ret;
}
JNIEXPORT jint JNICALL Java_com_gateway_ssl_jni_GmSSLJNI_recv
(JNIEnv *env, jclass cls, jlong ssl, jbyteArray buf, jint maxLen) {
SSL *sslObj = (SSL *)(intptr_t)ssl;
jbyte *bytes = (*env)->GetByteArrayElements(env, buf, NULL);
int ret = SSL_read(sslObj, bytes, maxLen);
(*env)->ReleaseByteArrayElements(env, buf, bytes, 0);
if (ret <= 0) {
int err = SSL_get_error(sslObj, ret);
if (err == SSL_ERROR_ZERO_RETURN) return 0;
return -err;
}
return ret;
}
JNIEXPORT void JNICALL Java_com_gateway_ssl_jni_GmSSLJNI_close
(JNIEnv *env, jclass cls, jlong ssl) {
SSL *sslObj = (SSL *)(intptr_t)ssl;
int fd = SSL_get_fd(sslObj);
SSL_shutdown(sslObj);
SSL_free(sslObj);
if (fd >= 0) close(fd);
}
/* ==================== ???? ==================== */
JNIEXPORT jstring JNICALL Java_com_gateway_ssl_jni_GmSSLJNI_getCipherName
(JNIEnv *env, jclass cls, jlong ssl) {
SSL *sslObj = (SSL *)(intptr_t)ssl;
return (*env)->NewStringUTF(env, SSL_get_cipher_name(sslObj));
}
JNIEXPORT jstring JNICALL Java_com_gateway_ssl_jni_GmSSLJNI_getVersion
(JNIEnv *env, jclass cls, jlong ssl) {
SSL *sslObj = (SSL *)(intptr_t)ssl;
return (*env)->NewStringUTF(env, SSL_get_version(sslObj));
}
JNIEXPORT jstring JNICALL Java_com_gateway_ssl_jni_GmSSLJNI_getPeerCertificate
(JNIEnv *env, jclass cls, jlong ssl) {
SSL *sslObj = (SSL *)(intptr_t)ssl;
X509 *cert = SSL_get_peer_certificate(sslObj);
if (cert == NULL) {
return (*env)->NewStringUTF(env, "(none)");
}
char subject[256];
X509_NAME_oneline(X509_get_subject_name(cert), subject, sizeof(subject));
X509_free(cert);
return (*env)->NewStringUTF(env, subject);
}
JNIEXPORT jstring JNICALL Java_com_gateway_ssl_jni_GmSSLJNI_getErrorString
(JNIEnv *env, jclass cls, jlong ssl, jint ret) {
SSL *sslObj = (SSL *)(intptr_t)ssl;
unsigned long err = ERR_get_error();
char errbuf[256];
if (err != 0) {
ERR_error_string_n(err, errbuf, sizeof(errbuf));
} else {
int sslErr = SSL_get_error(sslObj, (int)ret);
snprintf(errbuf, sizeof(errbuf), "SSL error code: %d", sslErr);
}
return (*env)->NewStringUTF(env, errbuf);
}