/* * 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 #include #include #include #include #include #include #include #include #include #include #include #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); }