All Downloads are FREE. Search and download functionalities are using the official Maven repository.

penssl.wildfly-openssl-linux-x86_64.2.2.2.SP01.source-code.ssl.c Maven / Gradle / Ivy

The newest version!

#include "wfssl.h"

#if defined(SSL_OP_NO_TLSv1_1)
#define HAVE_TLSV1_1
#endif

#if defined(SSL_OP_NO_TLSv1_2)
#define HAVE_TLSV1_2
#endif

#ifdef __APPLE__
#define LIBCRYPTO_NAME "libcrypto.dylib"
#define LIBSSL_NAME "libssl.dylib"
#else
#ifdef WIN32
#define LIBCRYPTO_NAME "libeay32.dll"
#define LIBSSL_NAME "ssleay32.dll"
#define FALLBACK_LIBSSL_NAME "libssl32.dll"
#else
#define LIBCRYPTO_NAME "libcrypto.so"
#define LIBSSL_NAME "libssl.so"
#endif
#endif

#define WF_OPENSSL_MIN_VERSION 0x010001000L /* minimum required version of OpenSSL to work with */
#define WF_OPENSSL_VERSION_1_1_0 0x10100000L
#define WF_OPENSSL_VERSION_1_1_0_F 0x1010006fL
#define WF_OPENSSL_VERSION_3_0_0 0x30000000L

static int ssl_initialized = 0;
static jclass byteArrayClass, stringClass;

ssl_dynamic_methods ssl_methods;
crypto_dynamic_methods crypto_methods;

/**
 * The cached SSL context class
 */
static jclass    ssl_context_class;
static jmethodID sni_java_callback;

/* indexes for customer SSL data */
static int SSL_app_data1_idx = -1; /* connection metadata */
static int SSL_app_data2_idx = -1; /* context metadata */
static int SSL_app_data3_idx = -1; /* handshake count */
static int SSL_CTX_app_data1_idx = -1; /* context metadata */

static int OPENSSL_PROTOCOLS[6] = { SSL3_VERSION, SSL3_VERSION, TLS1_VERSION, TLS1_1_VERSION, TLS1_2_VERSION, TLS1_3_VERSION};

WF_OPENSSL(jint, initialize) (JNIEnv *e, jobject o, jstring libCryptoPath, jstring libSSLPath);
WF_OPENSSL(jlong, makeSSLContext)(JNIEnv *e, jobject o, jint protocol, jint mode);
WF_OPENSSL(jobjectArray, getCiphers)(JNIEnv *e, jobject o, jlong ssl);
WF_OPENSSL(jboolean, setCipherSuites)(JNIEnv *e, jobject o, jlong ssl, jstring ciphers);
WF_OPENSSL(jboolean, setCipherSuitesTLS13)(JNIEnv *e, jobject o, jlong ssl, jstring ciphers);
WF_OPENSSL(jboolean, setServerNameIndication)(JNIEnv *e, jobject o, jlong ssl, jstring ciphers);
WF_OPENSSL(jint, freeSSLContext)(JNIEnv *e, jobject o, jlong ctx);
WF_OPENSSL(void, setSSLContextOptions)(JNIEnv *e, jobject o, jlong ctx, jlong opt);
WF_OPENSSL(void, clearSSLContextOptions)(JNIEnv *e, jobject o, jlong ctx, jlong opt);
WF_OPENSSL(void, setSSLOptions)(JNIEnv *e, jobject o, jlong ssl, jlong opt);
WF_OPENSSL(void, clearSSLOptions)(JNIEnv *e, jobject o, jlong ssl, jlong opt);
WF_OPENSSL(jboolean, setCipherSuite)(JNIEnv *e, jobject o, jlong ctx, jstring ciphers);
WF_OPENSSL(jboolean, setCipherSuiteTLS13)(JNIEnv *e, jobject o, jlong ctx, jstring ciphers);
WF_OPENSSL(jboolean, setCARevocation)(JNIEnv *e, jobject o, jlong ctx, jstring file, jstring path);
WF_OPENSSL(jboolean, setCertificate)(JNIEnv *e, jobject o, jlong ctx, jbyteArray javaCert, jobjectArray intermediateCerts, jbyteArray javaKey, jint idx);
WF_OPENSSL(void, setCertVerifyCallback)(JNIEnv *e, jobject o, jlong ctx, jobject verifier);
WF_OPENSSL(jboolean, setSessionIdContext)(JNIEnv *e, jobject o, jlong ctx, jbyteArray sidCtx);
WF_OPENSSL(jlong, newSSL)(JNIEnv *e, jobject o, jlong ctx /* tcn_ssl_ctxt_t * */, jboolean server);
WF_OPENSSL(void, freeSSL)(JNIEnv *e, jobject o, jlong ssl /* SSL * */);
WF_OPENSSL(jlong, bufferAddress)(JNIEnv *e, jobject o, jobject bb);
WF_OPENSSL(jlong, makeNetworkBIO)(JNIEnv *e, jobject o, jlong ssl /* SSL * */);
WF_OPENSSL(jint, doHandshake)(JNIEnv *e, jobject o, jlong ssl /* SSL * */);
WF_OPENSSL(void, saveServerCipher)(JNIEnv *e, jobject o, jlong ssl /* SSL * */, jint server_cipher);
WF_OPENSSL(jint, getSSLError)(JNIEnv *e, jobject o, jlong ssl, jlong code);
WF_OPENSSL(jint, renegotiate)(JNIEnv *e, jobject o, jlong ssl /* SSL * */);
WF_OPENSSL(jint, getLastErrorNumber)(JNIEnv *e, jobject o);
WF_OPENSSL(jint /* nbytes */, pendingWrittenBytesInBIO)(JNIEnv *e, jobject o, jlong bio /* BIO * */);
WF_OPENSSL(jint, pendingReadableBytesInSSL)(JNIEnv *e, jobject o, jlong ssl /* SSL * */);
WF_OPENSSL(jint /* status */, writeToBIO)(JNIEnv *e, jobject o, jlong bio /* BIO * */, jlong wbuf /* char* */, jint wlen /* sizeof(wbuf) */);
WF_OPENSSL(jint /* status */, readFromBIO)(JNIEnv *e, jobject o, jlong bio /* BIO * */, jlong rbuf /* char * */, jint rlen /* sizeof(rbuf) - 1 */);
WF_OPENSSL(jint /* status */, writeToSSL)(JNIEnv *e, jobject o, jlong ssl /* SSL * */, jlong wbuf /* char * */, jint wlen /* sizeof(wbuf) */);
WF_OPENSSL(jint /* status */, readFromSSL)(JNIEnv *e, jobject o, jlong ssl /* SSL * */, jlong rbuf /* char * */, jint rlen /* sizeof(rbuf) - 1 */);
WF_OPENSSL(jint /* status */, getShutdown)(JNIEnv *e, jobject o, jlong ssl /* SSL * */);
WF_OPENSSL(jint, isInInit)(JNIEnv *e, jobject o, jlong ssl /* SSL * */);
WF_OPENSSL(void, freeBIO)(JNIEnv *e, jobject o, jlong bio /* BIO * */);
WF_OPENSSL(jstring, getErrorString)(JNIEnv *e, jobject o, jlong number);
WF_OPENSSL(jstring, getCipherForSSL)(JNIEnv *e, jobject o, jlong ssl /* SSL * */);
WF_OPENSSL(jstring, getVersion)(JNIEnv *e, jobject o, jlong ssl /* SSL * */);
WF_OPENSSL(jobjectArray, getPeerCertChain)(JNIEnv *e, jobject o, jlong ssl /* SSL * */);
WF_OPENSSL(jint , shutdownSSL)(JNIEnv *e, jobject o, jlong ssl);
WF_OPENSSL(jbyteArray, getPeerCertificate)(JNIEnv *e, jobject o, jlong ssl /* SSL * */);
WF_OPENSSL(jstring, version)(JNIEnv *e);
WF_OPENSSL(jlong, versionNumber)(JNIEnv *e);
WF_OPENSSL(void, setMinProtoVersion)(JNIEnv *e, jobject o, jlong ssl, jint version);
WF_OPENSSL(void, setMaxProtoVersion)(JNIEnv *e, jobject o, jlong ssl, jint version);
WF_OPENSSL(jint, getMinProtoVersion)(JNIEnv *e, jobject o, jlong ssl);
WF_OPENSSL(jint, getMaxProtoVersion)(JNIEnv *e, jobject o, jlong ssl);
WF_OPENSSL(jboolean, getSSLSessionReused)(JNIEnv *e, jobject o, jlong ssl);
void init_app_data_idx(void);
void SSL_set_app_data1(SSL *ssl, tcn_ssl_conn_t *arg);
void SSL_set_app_data2(SSL *ssl, tcn_ssl_ctxt_t *arg);
void *SSL_get_app_data3(const SSL *ssl);
void SSL_set_app_data3(SSL *ssl, void *arg);
void SSL_CTX_set_app_data1(SSL_CTX *ssl, void *arg);
void SSL_BIO_close(BIO *bi);
int ssl_callback_ServerNameIndication(SSL *ssl, int *al, tcn_ssl_ctxt_t *c);
int load_openssl_dynamic_methods(JNIEnv *e, const char * libCryptoPath, const char * libSSLPath);
void setupDH(JNIEnv *e, SSL_CTX * ctx);

void init_app_data_idx(void)
{
    if (SSL_app_data1_idx > -1) {
        return;
    }

    if(ssl_methods.SSL_get_ex_new_index != NULL) {
        SSL_app_data1_idx = ssl_methods.SSL_get_ex_new_index(0, "First Application Data for SSL", NULL, NULL, NULL);
        SSL_app_data2_idx = ssl_methods.SSL_get_ex_new_index(0, "Second Application Data for SSL", NULL, NULL, NULL);
        SSL_app_data3_idx = ssl_methods.SSL_get_ex_new_index(0, "Third Application Data for SSL", NULL, NULL, NULL);
        SSL_CTX_app_data1_idx = ssl_methods.SSL_CTX_get_ex_new_index(0, "First Application Data for SSL_CTX", NULL, NULL, NULL);
    } else {
        SSL_app_data1_idx = crypto_methods.CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL, 0, "First Application Data for SSL", NULL, NULL, NULL);
        SSL_app_data2_idx = crypto_methods.CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL, 0, "Second Application Data for SSL", NULL, NULL, NULL);
        SSL_app_data3_idx = crypto_methods.CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL, 0, "Third Application Data for SSL", NULL, NULL, NULL);
        SSL_CTX_app_data1_idx = crypto_methods.CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL_CTX, 0, "First Application Data for SSL_CTX", NULL, NULL, NULL);
    }
}
/*the the SSL context structure associated with the context*/
tcn_ssl_conn_t *SSL_get_app_data1(const SSL *ssl)
{
    return (tcn_ssl_conn_t *)ssl_methods.SSL_get_ex_data(ssl, SSL_app_data1_idx);
}

void SSL_set_app_data1(SSL *ssl, tcn_ssl_conn_t *arg)
{
    ssl_methods.SSL_set_ex_data(ssl, SSL_app_data1_idx, (char *)arg);
}
/*the the SSL context structure associated with the context*/
tcn_ssl_ctxt_t *SSL_get_app_data2(const SSL *ssl)
{
    return (tcn_ssl_ctxt_t *)ssl_methods.SSL_get_ex_data(ssl, SSL_app_data2_idx);
}

void SSL_set_app_data2(SSL *ssl, tcn_ssl_ctxt_t *arg)
{
    ssl_methods.SSL_set_ex_data(ssl, SSL_app_data2_idx, (char *)arg);
}

void *SSL_get_app_data3(const SSL *ssl)
{
    return (void *)ssl_methods.SSL_get_ex_data(ssl, SSL_app_data3_idx);
}

void SSL_set_app_data3(SSL *ssl, void *arg)
{
    ssl_methods.SSL_set_ex_data(ssl, SSL_app_data3_idx, (char *)arg);
}

tcn_ssl_ctxt_t *SSL_CTX_get_app_data1(const SSL_CTX *ssl)
{
    return (tcn_ssl_ctxt_t *)ssl_methods.SSL_CTX_get_ex_data(ssl, SSL_CTX_app_data1_idx);
}

void SSL_CTX_set_app_data1(SSL_CTX *ssl, void *arg)
{
    ssl_methods.SSL_CTX_set_ex_data(ssl, SSL_CTX_app_data1_idx, (char *)arg);
}

void SSL_BIO_close(BIO *bi)
{
    if (bi == NULL)
        return;
    else
        crypto_methods.BIO_free(bi);
}

/* Callback used when OpenSSL receives a client hello with a Server Name
 * Indication extension.
 */
int ssl_callback_ServerNameIndication(SSL *ssl, int *al, tcn_ssl_ctxt_t *c)
{
    /* TODO: Is it better to cache the JNIEnv* during the call to handshake? */

    /* Get the JNI environment for this callback */
    JavaVM *javavm = tcn_get_java_vm();
    JNIEnv *env;
    const char *servername;
    jstring hostname;
    jlong original_ssl_context, new_ssl_context;
    (*javavm)->AttachCurrentThread(javavm, (void **)&env, NULL);

    /*  Get the host name presented by the client */
    servername = ssl_methods.SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);

    /*  Convert parameters ready for the method call */
    hostname = (*env)->NewStringUTF(env, servername);
    original_ssl_context = P2J(c->ctx);

    /*  Make the call */
    new_ssl_context = (*env)->CallStaticLongMethod(env,
                                                            ssl_context_class,
                                                            sni_java_callback,
                                                            original_ssl_context,
                                                            hostname);

    if (original_ssl_context != new_ssl_context) {
        ssl_methods.SSL_set_SSL_CTX(ssl, J2P(new_ssl_context, SSL_CTX *));
    }

    return SSL_TLSEXT_ERR_OK;
}

#ifdef WIN32

#define REQUIRE_SYMBOL(handle, symb, target) target = (void*)GetProcAddress(handle, #symb); if(target == 0) { throwIllegalStateException(e, "Could not load required symbol from" #handle " " #symb); return 1;}
#define GET_SYMBOL(handle, symb, target) target = (void*)GetProcAddress(handle, #symb);

#define REQUIRE_SSL_SYMBOL_ALIAS(symb, alias) ssl_methods.alias = (void*)GetProcAddress(ssl, #symb); if(ssl_methods.alias == 0) { throwIllegalStateException(e, "Could not load required symbol from libssl: " #symb); return 1;}
#define GET_SSL_SYMBOL(symb) ssl_methods.symb = (void*)GetProcAddress(ssl, #symb);
#define REQUIRE_CRYPTO_SYMBOL_ALIAS(symb, alias) crypto_methods.alias = (void*)GetProcAddress(crypto, #symb); if(crypto_methods.alias == 0) { throwIllegalStateException(e, "Could not load required symbol from libcrypto: " #symb); return 1;}
#define GET_CRYPTO_SYMBOL(symb)  crypto_methods.symb = (void*)GetProcAddress(crypto, #symb);

#else

#define REQUIRE_SSL_SYMBOL_ALIAS(symb, alias) ssl_methods.alias = dlsym(ssl, #symb); if(ssl_methods.alias == 0) {throwIllegalStateException(e, "Could not load required symbol from libssl: " #symb); return 1;}
#define GET_SSL_SYMBOL(symb) ssl_methods.symb = dlsym(ssl, #symb);
#define REQUIRE_CRYPTO_SYMBOL_ALIAS(symb, alias) crypto_methods.alias = dlsym(crypto, #symb); if(crypto_methods.alias == 0) {throwIllegalStateException(e, "Could not load required symbol from libcrypto: " #symb); return 1;}
#define GET_CRYPTO_SYMBOL(symb) crypto_methods.symb = dlsym(crypto, #symb);
#endif

#define REQUIRE_SSL_SYMBOL(symb) REQUIRE_SSL_SYMBOL_ALIAS(symb, symb);
#define REQUIRE_CRYPTO_SYMBOL(symb) REQUIRE_CRYPTO_SYMBOL_ALIAS(symb, symb);

int load_openssl_dynamic_methods(JNIEnv *e, const char * libCryptoPath, const char * libSSLPath) {

#ifdef WIN32
	HMODULE crypto, ssl;

    if(libCryptoPath == NULL) {
	    crypto = LoadLibrary(LIBCRYPTO_NAME);
	} else {
        crypto = LoadLibrary(libCryptoPath);
	}
    if( crypto == NULL ) {
        throwIllegalStateException(e, "Could not load libeay32.dll");
        return 1;
    }
    GET_SYMBOL(crypto, SSLeay, ssl_methods.SSLeay);
    GET_SYMBOL(crypto, SSLeay_version, ssl_methods.SSLeay_version);
    if(ssl_methods.SSLeay == NULL) {
        REQUIRE_SYMBOL(crypto, OpenSSL_version_num, ssl_methods.SSLeay);
        REQUIRE_SYMBOL(crypto, OpenSSL_version, ssl_methods.SSLeay_version);
    }
    if(libSSLPath == NULL) {
	    ssl = LoadLibrary(LIBSSL_NAME);
        if(ssl == NULL) {
            ssl = LoadLibrary(FALLBACK_LIBSSL_NAME);
        }
	} else {
        ssl = LoadLibrary(libSSLPath);
    }
    if( ssl == NULL ) {
        throwIllegalStateException(e, "Could not load ssleay32.dll or libssl32.dll");
        return 1;
    }
#else
    void *ssl, *crypto;

    if(libCryptoPath == NULL) {
        crypto = dlopen(LIBCRYPTO_NAME, RTLD_LAZY);
    } else {
        crypto = dlopen(libCryptoPath, RTLD_LAZY);
    }

    if(libSSLPath == NULL) {
        ssl = dlopen(LIBSSL_NAME, RTLD_LAZY);
    } else {
        ssl = dlopen(libSSLPath, RTLD_LAZY);
    }

    if( ssl == NULL ) {
        printf("%s", dlerror());
        throwIllegalStateException(e, "Could not load libssl");
        return 1;
    }
    if( crypto == NULL ) {
        throwIllegalStateException(e, "Could not load libcrypto");
        return 1;
    }
    GET_SSL_SYMBOL(SSLeay);
    GET_SSL_SYMBOL(SSLeay_version);
    if(ssl_methods.SSLeay == NULL) {
        REQUIRE_SSL_SYMBOL_ALIAS(OpenSSL_version_num, SSLeay);
        REQUIRE_SSL_SYMBOL_ALIAS(OpenSSL_version, SSLeay_version);
    }
#endif

    if (ssl_methods.SSLeay() < WF_OPENSSL_MIN_VERSION) {
        ssl_initialized = 0;
        throwIllegalStateException(e, "Invalid OpenSSL Version (required OpenSSL 1.0.1 or newer)");
        return 1;
    }

    GET_SSL_SYMBOL(SSL_CTX_get_ex_new_index);
    REQUIRE_SSL_SYMBOL(SSL_get_ex_data);
    REQUIRE_SSL_SYMBOL(SSL_set_ex_data);
    REQUIRE_SSL_SYMBOL(SSL_CTX_set_ex_data);
    REQUIRE_SSL_SYMBOL(SSL_get_ex_data_X509_STORE_CTX_idx);
    REQUIRE_SSL_SYMBOL(SSL_CTX_get_ex_data);
    if(ssl_methods.SSL_CTX_get_ex_new_index != NULL) {
        REQUIRE_SSL_SYMBOL(SSL_get_ex_new_index);
    } else {
        REQUIRE_CRYPTO_SYMBOL(CRYPTO_get_ex_new_index)
    }

    REQUIRE_SSL_SYMBOL(SSL_CIPHER_get_name);
    REQUIRE_SSL_SYMBOL(SSL_CTX_callback_ctrl);
    REQUIRE_SSL_SYMBOL(SSL_CTX_check_private_key);
    REQUIRE_SSL_SYMBOL(SSL_CTX_ctrl);
    REQUIRE_SSL_SYMBOL(SSL_CTX_free);
    REQUIRE_SSL_SYMBOL(SSL_CTX_get_cert_store);
    REQUIRE_SSL_SYMBOL(SSL_CTX_get_client_CA_list);
    REQUIRE_SSL_SYMBOL(SSL_CTX_get_timeout);
    REQUIRE_SSL_SYMBOL(SSL_CTX_load_verify_locations);
    REQUIRE_SSL_SYMBOL(SSL_CTX_new);
    REQUIRE_SSL_SYMBOL(SSL_CTX_sess_set_new_cb);
    REQUIRE_SSL_SYMBOL(SSL_CTX_ctrl);
    REQUIRE_SSL_SYMBOL(SSL_CIPHER_get_name);
    REQUIRE_SSL_SYMBOL(SSL_CTX_callback_ctrl);
    REQUIRE_SSL_SYMBOL(SSL_CTX_ctrl);
    REQUIRE_SSL_SYMBOL(SSL_CTX_sess_set_remove_cb);
    REQUIRE_SSL_SYMBOL(SSL_get_error);
    GET_SSL_SYMBOL(SSL_set_alpn_protos);
    GET_SSL_SYMBOL(SSL_CTX_set_alpn_select_cb);
    GET_SSL_SYMBOL(SSL_get0_alpn_selected);
    REQUIRE_SSL_SYMBOL(SSL_CTX_set_cert_verify_callback);
    REQUIRE_SSL_SYMBOL(SSL_CTX_set_cipher_list);
    GET_SSL_SYMBOL(SSL_set_ciphersuites);
    GET_SSL_SYMBOL(SSL_CTX_set_ciphersuites);
    REQUIRE_SSL_SYMBOL(SSL_CTX_set_default_verify_paths);
    REQUIRE_SSL_SYMBOL(SSL_CTX_set_session_id_context);
    REQUIRE_SSL_SYMBOL(SSL_CTX_set_timeout);
    REQUIRE_SSL_SYMBOL(SSL_CTX_set_verify);
    REQUIRE_SSL_SYMBOL(SSL_CTX_use_PrivateKey);
    REQUIRE_SSL_SYMBOL(SSL_CTX_use_certificate);
    REQUIRE_SSL_SYMBOL(SSL_SESSION_free);
    REQUIRE_SSL_SYMBOL(SSL_SESSION_get_id);
    REQUIRE_SSL_SYMBOL(SSL_SESSION_get_time);
    REQUIRE_SSL_SYMBOL(SSL_SESSION_set_timeout);
    REQUIRE_SSL_SYMBOL(SSL_add_file_cert_subjects_to_stack);
    REQUIRE_SSL_SYMBOL(SSL_ctrl);
    REQUIRE_SSL_SYMBOL(SSL_do_handshake);
    REQUIRE_SSL_SYMBOL(SSL_free);
    REQUIRE_SSL_SYMBOL(SSL_get_ciphers);
    REQUIRE_SSL_SYMBOL(SSL_get_current_cipher);
    REQUIRE_SSL_SYMBOL(SSL_get_peer_cert_chain);

    GET_SSL_SYMBOL(SSL_get_peer_certificate);
    if(ssl_methods.SSL_get_peer_certificate == NULL) {
        REQUIRE_SSL_SYMBOL_ALIAS(SSL_get1_peer_certificate, SSL_get_peer_certificate);
    }

    REQUIRE_SSL_SYMBOL(SSL_get_privatekey);
    REQUIRE_SSL_SYMBOL(SSL_get_servername);
    REQUIRE_SSL_SYMBOL(SSL_get_session);
    REQUIRE_SSL_SYMBOL(SSL_get1_session);
    REQUIRE_SSL_SYMBOL(SSL_set_session);
    REQUIRE_SSL_SYMBOL(SSL_get_shutdown);
    REQUIRE_SSL_SYMBOL(SSL_get_version);

    GET_SSL_SYMBOL(SSL_library_init);
    if(ssl_methods.SSL_library_init == NULL) {
        REQUIRE_SSL_SYMBOL(OPENSSL_init_ssl);
    } else {
        REQUIRE_SSL_SYMBOL(SSL_load_error_strings);
    }

    REQUIRE_SSL_SYMBOL(SSL_load_client_CA_file);
    REQUIRE_SSL_SYMBOL(SSL_new);
    REQUIRE_SSL_SYMBOL(SSL_pending);
    REQUIRE_SSL_SYMBOL(SSL_set_read_ahead);
    REQUIRE_SSL_SYMBOL(SSL_read);
    REQUIRE_SSL_SYMBOL(SSL_renegotiate);
    REQUIRE_SSL_SYMBOL(SSL_renegotiate_pending);
    REQUIRE_SSL_SYMBOL(SSL_set_SSL_CTX);
    REQUIRE_SSL_SYMBOL(SSL_set_accept_state);
    REQUIRE_SSL_SYMBOL(SSL_set_bio);
    REQUIRE_SSL_SYMBOL(SSL_set_cipher_list);
    REQUIRE_SSL_SYMBOL(SSL_set_connect_state);
    REQUIRE_SSL_SYMBOL(SSL_set_verify);
    REQUIRE_SSL_SYMBOL(SSL_set_verify_result);
    REQUIRE_SSL_SYMBOL(SSL_shutdown);
    REQUIRE_SSL_SYMBOL(SSL_set_info_callback);
    REQUIRE_SSL_SYMBOL(SSL_write);
    GET_SSL_SYMBOL(TLSv1_1_server_method);
    GET_SSL_SYMBOL(TLSv1_2_server_method);
    GET_SSL_SYMBOL(SSLv23_client_method);
    GET_SSL_SYMBOL(SSLv23_method);
    GET_SSL_SYMBOL(SSLv23_server_method);
    GET_SSL_SYMBOL(TLS_client_method);
    GET_SSL_SYMBOL(TLS_server_method);
    GET_SSL_SYMBOL(TLS_method);
    GET_SSL_SYMBOL(SSL_set_options);
    GET_SSL_SYMBOL(SSL_get_options);
    GET_SSL_SYMBOL(SSL_clear_options);
    GET_SSL_SYMBOL(SSL_CTX_set_options);
    GET_SSL_SYMBOL(SSL_CTX_get_options);
    GET_SSL_SYMBOL(SSL_CTX_clear_options);
    GET_SSL_SYMBOL(SSL_session_reused);

    REQUIRE_CRYPTO_SYMBOL(ASN1_INTEGER_cmp);
    REQUIRE_CRYPTO_SYMBOL(BIO_ctrl);
    REQUIRE_CRYPTO_SYMBOL(BIO_ctrl_pending);
    REQUIRE_CRYPTO_SYMBOL(BIO_free);
    REQUIRE_CRYPTO_SYMBOL(BIO_new);
    REQUIRE_CRYPTO_SYMBOL(BIO_new_bio_pair);
    REQUIRE_CRYPTO_SYMBOL(BIO_printf);
    REQUIRE_CRYPTO_SYMBOL(BIO_read);
    REQUIRE_CRYPTO_SYMBOL(BIO_s_file);
    REQUIRE_CRYPTO_SYMBOL(BIO_s_mem);
    REQUIRE_CRYPTO_SYMBOL(BIO_write);
    REQUIRE_CRYPTO_SYMBOL(CRYPTO_free);
    GET_CRYPTO_SYMBOL(CRYPTO_num_locks);
    GET_CRYPTO_SYMBOL(CRYPTO_set_dynlock_create_callback);
    GET_CRYPTO_SYMBOL(CRYPTO_set_dynlock_destroy_callback);
    GET_CRYPTO_SYMBOL(CRYPTO_set_dynlock_lock_callback);
    GET_CRYPTO_SYMBOL(CRYPTO_set_id_callback);
    GET_CRYPTO_SYMBOL(CRYPTO_set_locking_callback);
    REQUIRE_CRYPTO_SYMBOL(CRYPTO_set_mem_functions);
    REQUIRE_CRYPTO_SYMBOL(ERR_error_string);
    REQUIRE_CRYPTO_SYMBOL(ERR_print_errors);
    REQUIRE_CRYPTO_SYMBOL(ERR_get_error);
    GET_CRYPTO_SYMBOL(ERR_load_crypto_strings);
    GET_CRYPTO_SYMBOL(OPENSSL_add_all_algorithms_noconf);
    REQUIRE_CRYPTO_SYMBOL(EVP_Digest);

    GET_CRYPTO_SYMBOL(EVP_PKEY_bits);
    if (crypto_methods.EVP_PKEY_bits == NULL) {
      REQUIRE_CRYPTO_SYMBOL_ALIAS(EVP_PKEY_get_bits, EVP_PKEY_bits);
    }

    REQUIRE_CRYPTO_SYMBOL(EVP_PKEY_free);
    REQUIRE_CRYPTO_SYMBOL(EVP_PKEY_type);
    REQUIRE_CRYPTO_SYMBOL(EVP_sha1);
    REQUIRE_CRYPTO_SYMBOL(OPENSSL_load_builtin_modules);
    REQUIRE_CRYPTO_SYMBOL(PEM_read_bio_PrivateKey);
    REQUIRE_CRYPTO_SYMBOL(X509_CRL_verify);
    REQUIRE_CRYPTO_SYMBOL(X509_LOOKUP_ctrl);
    REQUIRE_CRYPTO_SYMBOL(X509_LOOKUP_file);
    REQUIRE_CRYPTO_SYMBOL(X509_LOOKUP_hash_dir);
    REQUIRE_CRYPTO_SYMBOL(X509_STORE_CTX_cleanup);
    REQUIRE_CRYPTO_SYMBOL(X509_STORE_CTX_get_current_cert);
    REQUIRE_CRYPTO_SYMBOL(X509_STORE_CTX_get_error);
    REQUIRE_CRYPTO_SYMBOL(X509_STORE_CTX_get_error_depth);
    REQUIRE_CRYPTO_SYMBOL(X509_STORE_CTX_get_ex_data);
    REQUIRE_CRYPTO_SYMBOL(X509_STORE_CTX_init);
    REQUIRE_CRYPTO_SYMBOL(X509_STORE_CTX_set_error);
    REQUIRE_CRYPTO_SYMBOL(X509_STORE_CTX_new);
    REQUIRE_CRYPTO_SYMBOL(X509_STORE_CTX_free);
    REQUIRE_CRYPTO_SYMBOL(X509_STORE_add_lookup);
    REQUIRE_CRYPTO_SYMBOL(X509_STORE_free);
    REQUIRE_CRYPTO_SYMBOL(X509_STORE_new);
    REQUIRE_CRYPTO_SYMBOL(X509_STORE_set_flags);
    REQUIRE_CRYPTO_SYMBOL(X509_cmp_current_time);
    REQUIRE_CRYPTO_SYMBOL(X509_free);
    REQUIRE_CRYPTO_SYMBOL(X509_get_issuer_name);
    REQUIRE_CRYPTO_SYMBOL(X509_get_pubkey);
    REQUIRE_CRYPTO_SYMBOL(X509_get_serialNumber);
    REQUIRE_CRYPTO_SYMBOL(X509_get_subject_name);
    REQUIRE_CRYPTO_SYMBOL(d2i_X509);
    REQUIRE_CRYPTO_SYMBOL(i2d_X509);
    GET_CRYPTO_SYMBOL(sk_num);
    if(crypto_methods.sk_num == NULL) {
        REQUIRE_CRYPTO_SYMBOL_ALIAS(OPENSSL_sk_num, sk_num)
    }
    GET_CRYPTO_SYMBOL(sk_value);
    if(crypto_methods.sk_value == NULL) {
        REQUIRE_CRYPTO_SYMBOL_ALIAS(OPENSSL_sk_value, sk_value)
    }

    REQUIRE_CRYPTO_SYMBOL(X509_free);
    GET_CRYPTO_SYMBOL(ENGINE_load_builtin_engines);
    GET_CRYPTO_SYMBOL(X509_STORE_CTX_get0_untrusted);
    REQUIRE_CRYPTO_SYMBOL(DH_free);
    REQUIRE_CRYPTO_SYMBOL(PEM_read_bio_DHparams);

    return 0;
}

WF_OPENSSL(jint, initialize) (JNIEnv *e, jobject o, jstring libCryptoPath, jstring libSSLPath) {
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    jclass clazz;
    jclass sClazz;
    const char * cPath = NULL;
    const char * sPath = NULL;
    TCN_ALLOC_CSTRING(libCryptoPath);
    TCN_ALLOC_CSTRING(libSSLPath);
    if(libCryptoPath != NULL) {
        cPath = J2S(libCryptoPath);
    }
    if(libSSLPath != NULL) {
        sPath = J2S(libSSLPath);
    }
    if(load_openssl_dynamic_methods(e, cPath, sPath) != 0) {
        TCN_FREE_CSTRING(libCryptoPath);
        TCN_FREE_CSTRING(libSSLPath);
        return 0;
    }
    TCN_FREE_CSTRING(libCryptoPath);
    TCN_FREE_CSTRING(libSSLPath);

    /* Check if already initialized */
    if (ssl_initialized++) {
        return 0;
    }
    /* We must register the library in full, to ensure our configuration
     * code can successfully test the SSL environment.
     */
    crypto_methods.CRYPTO_set_mem_functions(malloc, realloc, free);
    if(ssl_methods.SSL_library_init != NULL) {
        /*OpenSSL 1.0.x */
        ssl_methods.SSL_load_error_strings();
        crypto_methods.ERR_load_crypto_strings();
        ssl_methods.SSL_library_init();
        crypto_methods.OPENSSL_add_all_algorithms_noconf();
    } else {
        ssl_methods.OPENSSL_init_ssl(0, NULL);
    }
    init_app_data_idx();
    if(crypto_methods.ENGINE_load_builtin_engines != NULL) {
        crypto_methods.ENGINE_load_builtin_engines();
    }
    crypto_methods.OPENSSL_load_builtin_modules();

    ssl_thread_setup();

    /* TODO: engine support? */

    /* Cache the byte[].class for performance reasons */
    clazz = (*e)->FindClass(e, "[B");
    byteArrayClass = (jclass) (*e)->NewGlobalRef(e, clazz);

    /* Cache the String.class for performance reasons */
    sClazz = (*e)->FindClass(e, "java/lang/String");
    stringClass = (jclass) (*e)->NewGlobalRef(e, sClazz);

    alpn_init(e);
    session_init(e);

    return (jint)0;
}

/* Initialize server context */
WF_OPENSSL(jlong, makeSSLContext)(JNIEnv *e, jobject o, jint protocol, jint mode)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    tcn_ssl_ctxt_t *c = NULL;
    SSL_CTX *ctx = NULL;
    jclass clazz;
    int i;
    int protoVersion = 0;

    if (protocol == SSL_PROTOCOL_NONE) {
        throwIllegalStateException(e, "No SSL protocols requested");
        goto init_failed;
    }
    if (ssl_methods.TLS_server_method != NULL) {
        if (mode == SSL_MODE_CLIENT)
            ctx = ssl_methods.SSL_CTX_new(ssl_methods.TLS_client_method());
        else if (mode == SSL_MODE_SERVER)
            ctx = ssl_methods.SSL_CTX_new(ssl_methods.TLS_server_method());
        else
            ctx = ssl_methods.SSL_CTX_new(ssl_methods.TLS_method());
    } else {
        if (mode == SSL_MODE_CLIENT)
            ctx = ssl_methods.SSL_CTX_new(ssl_methods.SSLv23_client_method());
        else if (mode == SSL_MODE_SERVER)
            ctx = ssl_methods.SSL_CTX_new(ssl_methods.SSLv23_server_method());
        else
            ctx = ssl_methods.SSL_CTX_new(ssl_methods.SSLv23_method());
    }
    if (!ctx) {
        char err[2048];
        generate_openssl_stack_error(e, err, sizeof(err));
        throwIllegalStateException(e, err);
        goto init_failed;
    }
    if ((c = malloc(sizeof(tcn_ssl_ctxt_t))) == NULL) {
        throwIllegalStateException(e, "malloc failed");
        goto init_failed;
    }
    memset(c, 0, sizeof(*c));

    c->protocol = protocol;
    c->mode     = mode;
    c->ctx      = ctx;
    if (ssl_methods.SSLeay() >= WF_OPENSSL_VERSION_3_0_0) {
        set_CTX_options_internal(c->ctx, SSL_OP_ALL_3_0_0);
    } else if (ssl_methods.SSLeay() >= WF_OPENSSL_VERSION_1_1_0_F) {
        set_CTX_options_internal(c->ctx, SSL_OP_ALL_1_1_0_F);
    } else {
        set_CTX_options_internal(c->ctx, SSL_OP_ALL_0_9_7);
    }
    if (ssl_methods.SSLeay() < WF_OPENSSL_VERSION_1_1_0 || ssl_methods.SSLeay() <= WF_OPENSSL_VERSION_1_1_0_F) {
        /* always disable SSLv2, as per RFC 6176 */
        set_CTX_options_internal((c->ctx), SSL_OP_NO_SSLv2);
        if (!(protocol & SSL_PROTOCOL_SSLV3))
            set_CTX_options_internal((c->ctx), SSL_OP_NO_SSLv3);
        if (!(protocol & SSL_PROTOCOL_TLSV1))
            set_CTX_options_internal((c->ctx), SSL_OP_NO_TLSv1);
        if(ssl_methods.TLSv1_1_server_method != NULL) { /* we use the presence of the method to test if it is supported */
            if (!(protocol & SSL_PROTOCOL_TLSV1_1))
                set_CTX_options_internal((c->ctx), SSL_OP_NO_TLSv1_1);
        }
        if(ssl_methods.TLSv1_2_server_method != NULL) {
            if (!(protocol & SSL_PROTOCOL_TLSV1_2))
                set_CTX_options_internal((c->ctx), SSL_OP_NO_TLSv1_2);
        }
    } else {
        if (protocol != SSL_PROTOCOL_ALL) {
            for (i = 0; i < 6; i++) {
                if (protocol & (1 << i)) {
                    protoVersion = OPENSSL_PROTOCOLS[i];
                    break;
                }
            }
            ssl_methods.SSL_CTX_ctrl((c->ctx), SSL_CTRL_SET_MIN_PROTO_VERSION, (protoVersion), NULL);
            ssl_methods.SSL_CTX_ctrl((c->ctx), SSL_CTRL_SET_MAX_PROTO_VERSION, (protoVersion), NULL);
        }
    }

    /*
     * Configure additional context ingredients
     */
    if (ssl_methods.SSLeay() < WF_OPENSSL_VERSION_1_1_0) {
        set_CTX_options_internal((c->ctx), SSL_OP_SINGLE_DH_USE);
        set_CTX_options_internal((c->ctx), SSL_OP_SINGLE_ECDH_USE);
    }
    /* TODO: what do we do with these defines? */
    #ifdef SSL_OP_NO_COMPRESSION
        /* Disable SSL compression to be safe */
        set_CTX_options_internal((c->ctx), SSL_OP_NO_COMPRESSION);
    #endif


        /** To get back the tomcat wrapper from CTX */
        SSL_CTX_set_app_data1(ctx, c);

    #ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
        /*
         * Disallow a session from being resumed during a renegotiation,
         * so that an acceptable cipher suite can be negotiated.
         */
        set_CTX_options_internal((c->ctx), SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
    #endif
    #ifdef SSL_MODE_RELEASE_BUFFERS
        /* Release idle buffers to the SSL_CTX free list */
        ssl_methods.SSL_CTX_ctrl((c->ctx),SSL_CTRL_MODE,(SSL_MODE_RELEASE_BUFFERS),NULL);
    #endif
        /*if (mode == SSL_MODE_CLIENT) {
            setup_client_session_context(e, c);
        } else {*/
        setup_session_context(e, c);
        /*}*/
        crypto_methods.EVP_Digest((const unsigned char *)SSL_DEFAULT_VHOST_NAME,
                   (unsigned long)((sizeof SSL_DEFAULT_VHOST_NAME) - 1),
                   &(c->context_id[0]), NULL, crypto_methods.EVP_sha1(), NULL);

        /* Set default Certificate verification level
         * and depth for the Client Authentication
         */
        c->verify_depth  = 1;
        c->verify_mode   = SSL_CVERIFY_UNSET;
        c->shutdown_type = SSL_SHUTDOWN_TYPE_UNSET;

        /* Cache Java side SNI callback if not already cached */
        if (ssl_context_class == NULL) {
            ssl_context_class = (*e)->NewGlobalRef(e, o);
            sni_java_callback = (*e)->GetStaticMethodID(e, ssl_context_class,
                                                        "sniCallBack", "(JLjava/lang/String;)J");
        }

        ssl_methods.SSL_CTX_ctrl(c->ctx,SSL_CTRL_SET_ECDH_AUTO,1,NULL);

        /* Set up OpenSSL call back if SNI is provided by the client */
        ssl_methods.SSL_CTX_callback_ctrl(c->ctx,SSL_CTRL_SET_TLSEXT_SERVERNAME_CB,(void (*)(void))ssl_callback_ServerNameIndication);
        ssl_methods.SSL_CTX_ctrl(c->ctx,SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG,0, (void *)c);

        /* Cache the byte[].class for performance reasons */
        clazz = (*e)->FindClass(e, "[B");
        byteArrayClass = (jclass) (*e)->NewGlobalRef(e, clazz);

        setupDH(e, ctx);
        return P2J(c);
    init_failed:
        return 0;
}

WF_OPENSSL(jobjectArray, getCiphers)(JNIEnv *e, jobject o, jlong ssl)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    STACK_OF_SSL_CIPHER *sk;
    int len;
    jobjectArray array;
    SSL_CIPHER *cipher;
    const char *name;
    int i;
    jstring c_name;
    SSL *ssl_ = J2P(ssl, SSL *);

    if (ssl_ == NULL) {
        throwIllegalStateException(e, "ssl is null");
        return NULL;
    }

    sk = ssl_methods.SSL_get_ciphers(ssl_);

    len = crypto_methods.sk_num(sk);

    if (len <= 0) {
        /* No peer certificate chain as no auth took place yet, or the auth was not successful. */
        return NULL;
    }

    /* Create the byte[][] array that holds all the certs */
    array = (*e)->NewObjectArray(e, len, stringClass, NULL);

    for (i = 0; i < len; i++) {
        cipher = (SSL_CIPHER*) crypto_methods.sk_value(sk, i);
        name = ssl_methods.SSL_CIPHER_get_name(cipher);

        c_name = (*e)->NewStringUTF(e, name);
        (*e)->SetObjectArrayElement(e, array, i, c_name);
    }
    return array;
}

WF_OPENSSL(jboolean, setCipherSuites)(JNIEnv *e, jobject o, jlong ssl, jstring ciphers)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    jboolean rv = JNI_TRUE;
    SSL *ssl_ = J2P(ssl, SSL *);
    TCN_ALLOC_CSTRING(ciphers);

    if (ssl_ == NULL) {
        TCN_FREE_CSTRING(ciphers);
        throwIllegalStateException(e, "ssl is null");
        return JNI_FALSE;
    }

    UNREFERENCED(o);
    if (!J2S(ciphers)) {
        TCN_FREE_CSTRING(ciphers);
        return JNI_FALSE;
    }
    if (!ssl_methods.SSL_set_cipher_list(ssl_, J2S(ciphers))) {
        char err[2048];
        generate_openssl_stack_error(e, err, sizeof(err));
        throwIllegalStateException(e, err);
        rv = JNI_FALSE;
    }
    TCN_FREE_CSTRING(ciphers);
    return rv;
}

WF_OPENSSL(jboolean, setCipherSuitesTLS13)(JNIEnv *e, jobject o, jlong ssl, jstring ciphers)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    jboolean rv = JNI_TRUE;
    SSL *ssl_ = J2P(ssl, SSL *);
    TCN_ALLOC_CSTRING(ciphers);

    if (ssl_methods.SSL_set_ciphersuites == NULL) {
        TCN_FREE_CSTRING(ciphers);
        throwIllegalStateException(e, "OpenSSL version does not support method SSL_set_ciphersuites");
        return JNI_FALSE;
    }

    if (ssl_ == NULL) {
        TCN_FREE_CSTRING(ciphers);
        throwIllegalStateException(e, "ssl is null");
        return JNI_FALSE;
    }

    UNREFERENCED(o);
    if (!J2S(ciphers)) {
        TCN_FREE_CSTRING(ciphers);
        return JNI_FALSE;
    }
    if (!ssl_methods.SSL_set_ciphersuites(ssl_, J2S(ciphers))) {
        char err[2048];
        generate_openssl_stack_error(e, err, sizeof(err));
        throwIllegalStateException(e, err);
        rv = JNI_FALSE;
    }
    TCN_FREE_CSTRING(ciphers);
    return rv;
}

WF_OPENSSL(jint, freeSSLContext)(JNIEnv *e, jobject o, jlong ctx)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
    UNREFERENCED_STDARGS;
    /* Run and destroy the cleanup callback */
    if (c) {
        int i;
        if (c->crl) {
            crypto_methods.X509_STORE_free(c->crl);
        }
        c->crl = NULL;
        if (c->ctx) {
            ssl_methods.SSL_CTX_free(c->ctx);
        }
        c->ctx = NULL;
        for (i = 0; i < SSL_AIDX_MAX; i++) {
            if (c->certs[i]) {
                crypto_methods.X509_free(c->certs[i]);
                c->certs[i] = NULL;
            }
            if (c->keys[i]) {
                crypto_methods.EVP_PKEY_free(c->keys[i]);
                c->keys[i] = NULL;
            }
        }

        if (c->verifier) {
            JNIEnv *e;
            tcn_get_java_env(&e);
            (*e)->DeleteGlobalRef(e, c->verifier);
            c->verifier = NULL;
        }
        c->verifier_method = NULL;

        if (c->next_proto_data) {
            free(c->next_proto_data);
            c->next_proto_data = NULL;
        }
        c->next_proto_len = 0;
    }
    return 0;
}

WF_OPENSSL(void, setSSLContextOptions)(JNIEnv *e, jobject o, jlong ctx, jlong opt)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);

    UNREFERENCED_STDARGS;
    TCN_ASSERT(ctx != 0);
    /* Clear the flag if not supported */
    if (opt & 0x00040000) {
        opt &= ~0x00040000;
    }
	set_CTX_options_internal(c->ctx, opt);
}

WF_OPENSSL(void, clearSSLContextOptions)(JNIEnv *e, jobject o, jlong ctx, jlong opt)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);

    UNREFERENCED_STDARGS;
    TCN_ASSERT(ctx != 0);
    if (ssl_methods.SSL_CTX_clear_options != NULL) {
        ssl_methods.SSL_CTX_clear_options(c->ctx, opt);
    } else {
        ssl_methods.SSL_CTX_ctrl((c->ctx),SSL_CTRL_CLEAR_OPTIONS,(opt),NULL);
    }
}

WF_OPENSSL(void, setSSLOptions)(JNIEnv *e, jobject o, jlong ssl, jlong opt)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    SSL *c = J2P(ssl, SSL *);

    UNREFERENCED_STDARGS;
    /* Clear the flag if not supported */
    if (opt & 0x00040000)
        opt &= ~0x00040000;
    set_options_internal(c, opt);
}

WF_OPENSSL(void, clearSSLOptions)(JNIEnv *e, jobject o, jlong ssl, jlong opt)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    SSL *c = J2P(ssl, SSL *);

    UNREFERENCED_STDARGS;
    if (ssl_methods.SSL_clear_options != NULL) {
        ssl_methods.SSL_clear_options(c, opt);
    } else {
        ssl_methods.SSL_ctrl(c,SSL_CTRL_CLEAR_OPTIONS,(opt),NULL);
    }
}

WF_OPENSSL(jboolean, setCipherSuite)(JNIEnv *e, jobject o, jlong ctx, jstring ciphers)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
    TCN_ALLOC_CSTRING(ciphers);
    jboolean rv = JNI_TRUE;
#ifndef HAVE_EXPORT_CIPHERS
    size_t len;
    char *buf;
#endif

    UNREFERENCED(o);
    TCN_ASSERT(ctx != 0);
    if (!J2S(ciphers))
        return JNI_FALSE;

#ifndef HAVE_EXPORT_CIPHERS
    /*
     *  Always disable NULL and export ciphers,
     *  no matter what was given in the config.
     */
    len = strlen(J2S(ciphers)) + strlen(SSL_CIPHERS_ALWAYS_DISABLED) + 1;
    buf = malloc(len * sizeof(char *));
    if (buf == NULL)
        return JNI_FALSE;
    memmove(buf, SSL_CIPHERS_ALWAYS_DISABLED, strlen(SSL_CIPHERS_ALWAYS_DISABLED));
    memmove(buf + strlen(SSL_CIPHERS_ALWAYS_DISABLED), J2S(ciphers), strlen(J2S(ciphers)));
    buf[len - 1] = '\0';
    if (!ssl_methods.SSL_CTX_set_cipher_list(c->ctx, buf)) {
#else
    if (!ssl_methods.SSL_CTX_set_cipher_list(c->ctx, J2S(ciphers))) {
#endif
        char err[2048];
        generate_openssl_stack_error(e, err, sizeof(err));
        tcn_Throw(e, "Unable to configure permitted SSL ciphers (%s)", err);
        rv = JNI_FALSE;
    }
#ifndef HAVE_EXPORT_CIPHERS
    free(buf);
#endif
    TCN_FREE_CSTRING(ciphers);
    return rv;
}

WF_OPENSSL(jboolean, setCipherSuiteTLS13)(JNIEnv *e, jobject o, jlong ctx, jstring ciphers)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
    TCN_ALLOC_CSTRING(ciphers);
    jboolean rv = JNI_TRUE;

    if (ssl_methods.SSL_CTX_set_ciphersuites == NULL) {
        TCN_FREE_CSTRING(ciphers);
        throwIllegalStateException(e, "OpenSSL version does not support method SSL_CTX_set_ciphersuites");
        return JNI_FALSE;
    }

    UNREFERENCED(o);
    TCN_ASSERT(ctx != 0);
    if (!J2S(ciphers)) {
        TCN_FREE_CSTRING(ciphers);
        return JNI_FALSE;
    }

    if (!ssl_methods.SSL_CTX_set_ciphersuites(c->ctx, J2S(ciphers))) {
        char err[2048];
        generate_openssl_stack_error(e, err, sizeof(err));
        tcn_Throw(e, "Unable to configure permitted SSL ciphers (%s)", err);
        rv = JNI_FALSE;
    }
    TCN_FREE_CSTRING(ciphers);
    return rv;
}

WF_OPENSSL(jboolean, setServerNameIndication)(JNIEnv *e, jobject o, jlong ssl,
jstring hostName)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    jboolean rv = JNI_TRUE;
    SSL *ssl_ = J2P(ssl, SSL *);
    TCN_ALLOC_CSTRING(hostName);
    const char * hostNameString;
    hostNameString = J2S(hostName);
    if (ssl_ == NULL) {
        TCN_FREE_CSTRING(hostName);
        throwIllegalStateException(e, "ssl is null");
        return JNI_FALSE;
    }

    UNREFERENCED(o);
    if (hostNameString == NULL) {
        TCN_FREE_CSTRING(hostName);
        return JNI_FALSE;
    }
    if (!ssl_methods.SSL_ctrl(ssl_, SSL_CTRL_SET_TLSEXT_HOSTNAME,
        TLSEXT_NAMETYPE_host_name, (void *)hostNameString)) {
        char err[2048];
        generate_openssl_stack_error(e, err, sizeof(err));
        throwIllegalStateException(e, err);
        rv = JNI_FALSE;
    }
    TCN_FREE_CSTRING(hostName);
    return rv;
}

WF_OPENSSL(jboolean, setCARevocation)(JNIEnv *e, jobject o, jlong ctx, jstring file, jstring path)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
    TCN_ALLOC_CSTRING(file);
    TCN_ALLOC_CSTRING(path);
    jboolean rv = JNI_FALSE;
    X509_LOOKUP *lookup;
    char err[2048];

    UNREFERENCED(o);
    TCN_ASSERT(ctx != 0);
    if (J2S(file) == NULL && J2S(path) == NULL)
        return JNI_FALSE;

    if (!c->crl) {
        if ((c->crl = crypto_methods.X509_STORE_new()) == NULL)
            goto cleanup;
    }
    if (J2S(file)) {
        lookup = crypto_methods.X509_STORE_add_lookup(c->crl, crypto_methods.X509_LOOKUP_file());
        if (lookup == NULL) {
            generate_openssl_stack_error(e, err, sizeof(err));
            crypto_methods.X509_STORE_free(c->crl);
            c->crl = NULL;
            tcn_Throw(e, "Lookup failed for file %s (%s)", J2S(file), err);
            goto cleanup;
        }
        crypto_methods.X509_LOOKUP_ctrl((lookup),X509_L_FILE_LOAD,(J2S(file)),(long)(X509_FILETYPE_PEM),NULL);
    }
    if (J2S(path)) {
        lookup = crypto_methods.X509_STORE_add_lookup(c->crl, crypto_methods.X509_LOOKUP_hash_dir());
        if (lookup == NULL) {
            generate_openssl_stack_error(e, err, sizeof(err));
            crypto_methods.X509_STORE_free(c->crl);
            c->crl = NULL;
            tcn_Throw(e, "Lookup failed for path %s (%s)", J2S(file), err);
            goto cleanup;
        }
        crypto_methods.X509_LOOKUP_ctrl((lookup),X509_L_ADD_DIR,(J2S(path)),(long)(X509_FILETYPE_PEM),NULL);
    }
    rv = JNI_TRUE;
cleanup:
    TCN_FREE_CSTRING(file);
    TCN_FREE_CSTRING(path);
    return rv;
}

WF_OPENSSL(jboolean, setCertificate)(JNIEnv *e, jobject o, jlong ctx, jbyteArray javaCert, jobjectArray intermediateCerts, jbyteArray javaKey, jint idx)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    /* we get the key contents into a byte array */
    unsigned char* cert;
    unsigned char* intCert;
    char err[2048];
    jboolean rv;
    const unsigned char *tmp;
    jsize lengthOfCert;
    jbyte* bufferPtr = (*e)->GetByteArrayElements(e, javaKey, NULL);
    jsize lengthOfKey = (*e)->GetArrayLength(e, javaKey);
    unsigned char* key = malloc(lengthOfKey);
    tcn_ssl_ctxt_t *c;
    BIO * bio;
    X509* tmpIntCert;
    int intermediateLength;
    jbyteArray byteArray;
    int i;

    memmove(key, bufferPtr, lengthOfKey);
    (*e)->ReleaseByteArrayElements(e, javaKey, bufferPtr, 0);


    bufferPtr = (*e)->GetByteArrayElements(e, javaCert, NULL);
    lengthOfCert = (*e)->GetArrayLength(e, javaCert);
    cert = malloc(lengthOfCert);
    memmove(cert, bufferPtr, lengthOfCert);
    (*e)->ReleaseByteArrayElements(e, javaCert, bufferPtr, 0);


    c = J2P(ctx, tcn_ssl_ctxt_t *);
    rv = JNI_TRUE;

    TCN_ASSERT(ctx != 0);

    if (idx < 0 || idx >= SSL_AIDX_MAX) {
        throwIllegalStateException(e, "Invalid key type");
        rv = JNI_FALSE;
        goto cleanup;
    }
    tmp = (const unsigned char *)cert;
    if ((c->certs[idx] = crypto_methods.d2i_X509(NULL, &tmp, lengthOfCert)) == NULL) {
        generate_openssl_stack_error(e, err, sizeof(err));
        throwIllegalStateException(e, err);
        rv = JNI_FALSE;
        goto cleanup;
    }

    if(c->keys[idx] != NULL) {
        free(c->keys[idx]);
    }

    bio = crypto_methods.BIO_new(crypto_methods.BIO_s_mem());
    crypto_methods.BIO_write(bio, key, lengthOfKey);

    c->keys[idx] = crypto_methods.PEM_read_bio_PrivateKey(bio, NULL, 0, NULL);
    crypto_methods.BIO_free(bio);
    if (c->keys[idx] == NULL) {
        generate_openssl_stack_error(e, err, sizeof(err));
        throwIllegalStateException(e, err);
        rv = JNI_FALSE;
        goto cleanup;
    }

    if (ssl_methods.SSL_CTX_use_certificate(c->ctx, c->certs[idx]) <= 0) {
        generate_openssl_stack_error(e, err, sizeof(err));
        tcn_Throw(e, "Error setting certificate (%s)", err);
        rv = JNI_FALSE;
        goto cleanup;
    }

    /*intermediate certs */
    intermediateLength = (*e)->GetArrayLength(e, intermediateCerts);
    for(i = 0; i < intermediateLength; ++i ) {

        byteArray = (*e)->GetObjectArrayElement(e, intermediateCerts, i);
        bufferPtr = (*e)->GetByteArrayElements(e, byteArray, NULL);
        lengthOfCert = (*e)->GetArrayLength(e, byteArray);
        intCert = malloc(lengthOfCert);
        memmove(intCert, bufferPtr, lengthOfCert);
        (*e)->ReleaseByteArrayElements(e, byteArray, bufferPtr, 0);

        tmp = (const unsigned char *)intCert;


        if ((tmpIntCert = crypto_methods.d2i_X509(NULL, &tmp, lengthOfCert)) == NULL) {
            generate_openssl_stack_error(e, err, sizeof(err));
            throwIllegalStateException(e, err);
            rv = JNI_FALSE;
            free(intCert);
            goto cleanup;
        }

        if (ssl_methods.SSL_CTX_ctrl(c->ctx,SSL_CTRL_EXTRA_CHAIN_CERT,0,(char *)tmpIntCert) <= 0) {
            generate_openssl_stack_error(e, err, sizeof(err));
            tcn_Throw(e, "Error setting certificate (%s)", err);
            rv = JNI_FALSE;
            free(intCert);
            goto cleanup;
        }
        free(intCert);
    }


    if (ssl_methods.SSL_CTX_use_PrivateKey(c->ctx, c->keys[idx]) <= 0) {
        generate_openssl_stack_error(e, err, sizeof(err));
        tcn_Throw(e, "Error setting private key (%s)", err);
        rv = JNI_FALSE;
        goto cleanup;
    }
    if (ssl_methods.SSL_CTX_check_private_key(c->ctx) <= 0) {
        generate_openssl_stack_error(e, err, sizeof(err));
        tcn_Throw(e, "Private key does not match the certificate public key (%s)",
                  err);
        rv = JNI_FALSE;
        goto cleanup;
    }


cleanup:
    free(key);
    free(cert);
    return rv;
}


static int SSL_cert_verify(X509_STORE_CTX *ctx, void *arg) {
    unsigned i;
    X509 *cert;
    int length;
    unsigned char *buf;
    JNIEnv *e;
    jbyteArray array;
    jbyteArray bArray;
    jboolean result;
    int r;
    int len;

    /* Get Apache context back through OpenSSL context */
    SSL *ssl = crypto_methods.X509_STORE_CTX_get_ex_data(ctx, ssl_methods.SSL_get_ex_data_X509_STORE_CTX_idx());
    tcn_ssl_ctxt_t *c = SSL_get_app_data2(ssl);
    tcn_ssl_conn_t *conn = SSL_get_app_data1(ssl);


    /*  Get a stack of all certs in the chain */
    STACK_OF_X509 *sk;
    if(crypto_methods.X509_STORE_CTX_get0_untrusted == NULL) {
        sk = ctx->untrusted;
    } else {
        sk = crypto_methods.X509_STORE_CTX_get0_untrusted(ctx);
    }

    len = crypto_methods.sk_num(sk);
    tcn_get_java_env(&e);

    /*  Create the byte[][] array that holds all the certs */
    array = (*e)->NewObjectArray(e, len, byteArrayClass, NULL);

    for(i = 0; i < len; i++) {

        cert = (X509*) crypto_methods.sk_value(sk, i);

        buf = NULL;
        length = crypto_methods.i2d_X509(cert, &buf);
        if (length < 0) {
            /*  In case of error just return an empty byte[][] */
            array = (*e)->NewObjectArray(e, 0, byteArrayClass, NULL);
            /*  We need to delete the local references so we not leak memory as this method is called via callback. */
            crypto_methods.CRYPTO_free(buf);
            break;
        }
        bArray = (*e)->NewByteArray(e, length);
        (*e)->SetByteArrayRegion(e, bArray, 0, length, (jbyte*) buf);
        (*e)->SetObjectArrayElement(e, array, i, bArray);

        /*  Delete the local reference as we not know how long the chain is and local references are otherwise */
        /*  only freed once jni method returns. */
        (*e)->DeleteLocalRef(e, bArray);
        crypto_methods.CRYPTO_free(buf);
    }
    result = (*e)->CallBooleanMethod(e, c->verifier, c->verifier_method, P2J(ssl), array,
            conn->server_cipher, conn->server);

    r = result == JNI_TRUE ? 1 : 0;

    /*  We need to delete the local references so we not leak memory as this method is called via callback. */
    (*e)->DeleteLocalRef(e, array);
    return r;
}


WF_OPENSSL(void, setCertVerifyCallback)(JNIEnv *e, jobject o, jlong ctx, jobject verifier)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);

    UNREFERENCED(o);
    TCN_ASSERT(ctx != 0);

    if (verifier == NULL) {
        ssl_methods.SSL_CTX_set_cert_verify_callback(c->ctx, NULL, NULL);
    } else {
        jclass verifier_class = (*e)->GetObjectClass(e, verifier);
        jmethodID method = (*e)->GetMethodID(e, verifier_class, "verify", "(J[[BIZ)Z");

        if (method == NULL) {
            return;
        }
        /*  Delete the reference to the previous specified verifier if needed. */
        if (c->verifier != NULL) {
            (*e)->DeleteLocalRef(e, c->verifier);
        }
        c->verifier = (*e)->NewGlobalRef(e, verifier);
        c->verifier_method = method;

        ssl_methods.SSL_CTX_set_cert_verify_callback(c->ctx, SSL_cert_verify, NULL);
    }
}

WF_OPENSSL(jboolean, setSessionIdContext)(JNIEnv *e, jobject o, jlong ctx, jbyteArray sidCtx)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
    int len = (*e)->GetArrayLength(e, sidCtx);
    unsigned char *buf;
    int res;

    UNREFERENCED(o);
    TCN_ASSERT(ctx != 0);

    buf = malloc(len);

    (*e)->GetByteArrayRegion(e, sidCtx, 0, len, (jbyte*) buf);

    res = ssl_methods.SSL_CTX_set_session_id_context(c->ctx, buf, len);
    free(buf);

    if (res == 1) {
        return JNI_TRUE;
    }
    return JNI_FALSE;
}


static void ssl_info_callback(SSL *ssl, int where, int ret) {
    int *handshakeCount = NULL;
    if (0 != (where & SSL_CB_HANDSHAKE_START)) {
        handshakeCount = (int*) SSL_get_app_data3(ssl);
        if (handshakeCount != NULL) {
            ++(*handshakeCount);
        }
    }
    if (0 != (where & SSL_CB_HANDSHAKE_DONE)) {
        tcn_ssl_conn_t *con = SSL_get_app_data1(ssl);
        con->handshake_done = 1;
    }
}

WF_OPENSSL(jlong, newSSL)(JNIEnv *e, jobject o, jlong ctx /* tcn_ssl_ctxt_t * */, jboolean server) {
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
    int *handshakeCount = malloc(sizeof(int));
    SSL *ssl;
    tcn_ssl_conn_t *con;

    UNREFERENCED_STDARGS;

    TCN_ASSERT(ctx != 0);
    ssl = ssl_methods.SSL_new(c->ctx);
    if (ssl == NULL) {
        throwIllegalStateException(e, "cannot create new ssl");
        return 0;
    }
    if ((con = malloc(sizeof(tcn_ssl_conn_t))) == NULL) {
        throwIllegalStateException(e, "Failed to allocate memory");
        return 0;
    }
    memset(con, 0, sizeof(*con));
    con->ctx  = c;
    con->ssl  = ssl;
    con->shutdown_type = c->shutdown_type;
    con->server = server;

    /* Store the handshakeCount in the SSL instance. */
    *handshakeCount = 0;
    SSL_set_app_data3(ssl, handshakeCount);

    if (server) {
        ssl_methods.SSL_set_accept_state(ssl);
    } else {
        ssl_methods.SSL_set_connect_state(ssl);
    }
    ssl_methods.SSL_set_read_ahead(ssl, 0);

    /* Setup verify and seed */
    if(server == JNI_FALSE) {
        ssl_methods.SSL_set_verify(ssl, SSL_VERIFY_PEER, NULL);
    }
    ssl_methods.SSL_set_verify_result(ssl, X509_V_OK);
    /* Add callback to keep track of handshakes and the handshake state */
    ssl_methods.SSL_set_info_callback(ssl, &ssl_info_callback);

    /* Store for later usage in SSL_callback_SSL_verify */
    SSL_set_app_data1(ssl, con);
    SSL_set_app_data2(ssl, c);
    return P2J(ssl);
}


/* Free the SSL * and its associated internal BIO */
WF_OPENSSL(void, freeSSL)(JNIEnv *e, jobject o, jlong ssl /* SSL * */) {
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    SSL *ssl_ = J2P(ssl, SSL *);
    tcn_ssl_conn_t *con;
    int *handshakeCount = SSL_get_app_data3(ssl_);

    if (handshakeCount != NULL) {
        free(handshakeCount);
    }

    con = SSL_get_app_data1(ssl_);
    if(con->alpn_selection_callback != NULL) {
        (*e)->DeleteGlobalRef(e, con->alpn_selection_callback);
    }
    free(con);
    ssl_methods.SSL_free(ssl_);
}


WF_OPENSSL(jlong, bufferAddress)(JNIEnv *e, jobject o, jobject bb)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    UNREFERENCED(o);
    if(bb == NULL) {
        throwIllegalArgumentException(e, "Buffer was null");
    }
    return P2J((*e)->GetDirectBufferAddress(e, bb));
}


/* Make a BIO pair (network and internal) for the provided SSL * and return the network BIO */
WF_OPENSSL(jlong, makeNetworkBIO)(JNIEnv *e, jobject o, jlong ssl /* SSL * */) {
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    SSL *ssl_ = J2P(ssl, SSL *);
    BIO *internal_bio;
    BIO *network_bio;

    UNREFERENCED(o);

    if (ssl_ == NULL) {
        throwIllegalStateException(e, "ssl is null");
        goto fail;
    }

    if (crypto_methods.BIO_new_bio_pair(&internal_bio, 0, &network_bio, 0) != 1) {
        throwIllegalStateException(e, "BIO_new_bio_pair failed");
        goto fail;
    }

    ssl_methods.SSL_set_bio(ssl_, internal_bio, internal_bio);

    return P2J(network_bio);
 fail:
    return 0;
}


WF_OPENSSL(jint, doHandshake)(JNIEnv *e, jobject o, jlong ssl /* SSL * */) {
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    SSL *ssl_ = J2P(ssl, SSL *);
    if (ssl_ == NULL) {
        throwIllegalStateException(e, "ssl is null");
        return 0;
    }

    UNREFERENCED(o);

    return ssl_methods.SSL_do_handshake(ssl_);
}


WF_OPENSSL(void, saveServerCipher)(JNIEnv *e, jobject o, jlong ssl /* SSL * */, jint server_cipher) {
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    SSL *ssl_ = J2P(ssl, SSL *);
    tcn_ssl_conn_t *con;
    if (ssl_ == NULL) {
        throwIllegalStateException(e, "ssl is null");
        return;
    }

    UNREFERENCED(o);
    con = SSL_get_app_data1(ssl_);
    con->server_cipher = server_cipher;

}


WF_OPENSSL(jint, getSSLError)(JNIEnv *e, jobject o, jlong ssl, jlong code)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    const SSL *ssl_ = J2P(ssl, SSL *);
    UNREFERENCED(o);
    return ssl_methods.SSL_get_error(ssl_, code);
}

WF_OPENSSL(jint, renegotiate)(JNIEnv *e, jobject o, jlong ssl /* SSL * */) {
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    tcn_ssl_conn_t *con;
    SSL *ssl_ = J2P(ssl, SSL *);
    if (ssl_ == NULL) {
        throwIllegalStateException(e, "ssl is null");
        return 0;
    }

    UNREFERENCED(o);
    con = SSL_get_app_data1(ssl_);
    con->handshake_done = 0;
    ssl_methods.SSL_renegotiate(ssl_);
    return ssl_methods.SSL_do_handshake(ssl_);
}


WF_OPENSSL(jint, getLastErrorNumber)(JNIEnv *e, jobject o) {
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    return crypto_methods.ERR_get_error();
}

WF_OPENSSL(jint /* nbytes */, pendingWrittenBytesInBIO)(JNIEnv *e, jobject o, jlong bio /* BIO * */) {
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    UNREFERENCED_STDARGS;

    return crypto_methods.BIO_ctrl_pending(J2P(bio, BIO *));
}

/* How much is available for reading in the given SSL struct? */
WF_OPENSSL(jint, pendingReadableBytesInSSL)(JNIEnv *e, jobject o, jlong ssl /* SSL * */) {
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    UNREFERENCED_STDARGS;

    return ssl_methods.SSL_pending(J2P(ssl, SSL *));
}

/* Write wlen bytes from wbuf into bio */
WF_OPENSSL(jint /* status */, writeToBIO)(JNIEnv *e, jobject o, jlong bio /* BIO * */, jlong wbuf /* char* */, jint wlen /* sizeof(wbuf) */) {
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    UNREFERENCED_STDARGS;

    return crypto_methods.BIO_write(J2P(bio, BIO *), J2P(wbuf, void *), wlen);

}

/* Read up to rlen bytes from bio into rbuf */
WF_OPENSSL(jint /* status */, readFromBIO)(JNIEnv *e, jobject o, jlong bio /* BIO * */, jlong rbuf /* char * */, jint rlen /* sizeof(rbuf) - 1 */) {
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    UNREFERENCED_STDARGS;

    return crypto_methods.BIO_read(J2P(bio, BIO *), J2P(rbuf, void *), rlen);
}

/* Write up to wlen bytes of application data to the ssl BIO (encrypt) */
WF_OPENSSL(jint /* status */, writeToSSL)(JNIEnv *e, jobject o, jlong ssl /* SSL * */, jlong wbuf /* char * */, jint wlen /* sizeof(wbuf) */) {
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    UNREFERENCED_STDARGS;

    return ssl_methods.SSL_write(J2P(ssl, SSL *), J2P(wbuf, void *), wlen);
}

/* Read up to rlen bytes of application data from the given SSL BIO (decrypt) */
WF_OPENSSL(jint /* status */, readFromSSL)(JNIEnv *e, jobject o, jlong ssl /* SSL * */, jlong rbuf /* char * */, jint rlen /* sizeof(rbuf) - 1 */) {
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    UNREFERENCED_STDARGS;

    return ssl_methods.SSL_read(J2P(ssl, SSL *), J2P(rbuf, void *), rlen);
}

/* Get the shutdown status of the engine */
WF_OPENSSL(jint /* status */, getShutdown)(JNIEnv *e, jobject o, jlong ssl /* SSL * */) {
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    UNREFERENCED_STDARGS;

    return ssl_methods.SSL_get_shutdown(J2P(ssl, SSL *));
}

WF_OPENSSL(jint, isInInit)(JNIEnv *e, jobject o, jlong ssl /* SSL * */) {
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    SSL *ssl_ = J2P(ssl, SSL *);

    UNREFERENCED(o);

    if (ssl_ == NULL) {
        throwIllegalStateException(e, "ssl is null");
        return 0;
    } else {
        tcn_ssl_conn_t *con = SSL_get_app_data1(ssl_);
        return con->handshake_done == 0 ? 1 : 0;
    }
}

/* Free a BIO * (typically, the network BIO) */
WF_OPENSSL(void, freeBIO)(JNIEnv *e, jobject o, jlong bio /* BIO * */) {
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    BIO *bio_;
    UNREFERENCED_STDARGS;

    bio_ = J2P(bio, BIO *);
    crypto_methods.BIO_free(bio_);
}


WF_OPENSSL(jstring, getErrorString)(JNIEnv *e, jobject o, jlong number)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    char buf[2048];
    UNREFERENCED(o);
    crypto_methods.ERR_error_string(number, buf);
    return tcn_new_string(e, buf);
}

/* Read which cipher was negotiated for the given SSL *. */
WF_OPENSSL(jstring, getCipherForSSL)(JNIEnv *e, jobject o, jlong ssl /* SSL * */)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    return AJP_TO_JSTRING(ssl_methods.SSL_CIPHER_get_name(ssl_methods.SSL_get_current_cipher(J2P(ssl, SSL*))));
}


/* Read which protocol was negotiated for the given SSL *. */
WF_OPENSSL(jstring, getVersion)(JNIEnv *e, jobject o, jlong ssl /* SSL * */)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    return AJP_TO_JSTRING(ssl_methods.SSL_get_version(J2P(ssl, SSL*)));
}


WF_OPENSSL(jobjectArray, getPeerCertChain)(JNIEnv *e, jobject o, jlong ssl /* SSL * */)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    STACK_OF_X509 *sk;
    int len;
    int i;
    X509 *cert;
    int length;
    unsigned char *buf;
    jobjectArray array;
    jbyteArray bArray;

    SSL *ssl_ = J2P(ssl, SSL *);

    if (ssl_ == NULL) {
        throwIllegalStateException(e, "ssl is null");
        return NULL;
    }

    UNREFERENCED(o);

    /*  Get a stack of all certs in the chain. */
    sk = ssl_methods.SSL_get_peer_cert_chain(ssl_);

    len = crypto_methods.sk_num(sk);
    if (len <= 0) {

        /* No peer certificate chain as no auth took place yet, or the auth was not successful. */
        return NULL;
    }
    /* Create the byte[][] array that holds all the certs */
    array = (*e)->NewObjectArray(e, len, byteArrayClass, NULL);

    for(i = 0; i < len; i++) {
        cert = (X509*) crypto_methods.sk_value(sk, i);

        buf = NULL;
        length = crypto_methods.i2d_X509(cert, &buf);
        if (length < 0) {
            crypto_methods.CRYPTO_free(buf);
            /* In case of error just return an empty byte[][] */
            return (*e)->NewObjectArray(e, 0, byteArrayClass, NULL);
        }
        bArray = (*e)->NewByteArray(e, length);
        (*e)->SetByteArrayRegion(e, bArray, 0, length, (jbyte*) buf);
        (*e)->SetObjectArrayElement(e, array, i, bArray);

        /*
         * Delete the local reference as we not know how long the chain is and local references are otherwise
         * only freed once jni method returns.
         */
        (*e)->DeleteLocalRef(e, bArray);

        crypto_methods.CRYPTO_free(buf);
    }
    return array;
}


/* Send CLOSE_NOTIFY to peer */
WF_OPENSSL(jint , shutdownSSL)(JNIEnv *e, jobject o, jlong ssl) {
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    return ssl_methods.SSL_shutdown(J2P(ssl, SSL *));
}


WF_OPENSSL(jbyteArray, getPeerCertificate)(JNIEnv *e, jobject o, jlong ssl /* SSL * */)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    X509 *cert;
    int length;
    unsigned char *buf = NULL;
    jbyteArray bArray;

    SSL *ssl_ = J2P(ssl, SSL *);

    if (ssl_ == NULL) {
        throwIllegalStateException(e, "ssl is null");
        return NULL;
    }

    UNREFERENCED(o);

    /* Get a stack of all certs in the chain */
    cert = ssl_methods.SSL_get_peer_certificate(ssl_);
    if (cert == NULL) {
        return NULL;
    }

    length = crypto_methods.i2d_X509(cert, &buf);

    bArray = (*e)->NewByteArray(e, length);
    (*e)->SetByteArrayRegion(e, bArray, 0, length, (jbyte*) buf);

    /*
     * We need to free the cert as the reference count is incremented by one and it is not destroyed when the
     * session is freed.
     * See https://www.openssl.org/docs/ssl/SSL_get_peer_certificate.html
     */
    crypto_methods.X509_free(cert);

    crypto_methods.CRYPTO_free(buf);

    return bArray;
}


WF_OPENSSL(jstring, version)(JNIEnv *e)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    char * version = ssl_methods.SSLeay_version(0);
    return (*e)->NewStringUTF(e, version);
}

WF_OPENSSL(jlong, versionNumber)(JNIEnv *e)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    return ssl_methods.SSLeay();
}

WF_OPENSSL(void, setMinProtoVersion)(JNIEnv *e, jobject o, jlong ssl, jint version)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    SSL *c = J2P(ssl, SSL *);

    UNREFERENCED_STDARGS;
    ssl_methods.SSL_ctrl(c, SSL_CTRL_SET_MIN_PROTO_VERSION, (version), NULL);
}

WF_OPENSSL(void, setMaxProtoVersion)(JNIEnv *e, jobject o, jlong ssl, jint version)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    SSL *c = J2P(ssl, SSL *);

    UNREFERENCED_STDARGS;
    ssl_methods.SSL_ctrl(c, SSL_CTRL_SET_MAX_PROTO_VERSION, (version), NULL);
}

WF_OPENSSL(jint, getMinProtoVersion)(JNIEnv *e, jobject o, jlong ssl)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    SSL *c = J2P(ssl, SSL *);

    UNREFERENCED_STDARGS;
    return ssl_methods.SSL_ctrl(c, SSL_CTRL_GET_MIN_PROTO_VERSION, 0, NULL);
}

WF_OPENSSL(jint, getMaxProtoVersion)(JNIEnv *e, jobject o, jlong ssl)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    SSL *c = J2P(ssl, SSL *);

    UNREFERENCED_STDARGS;
    return ssl_methods.SSL_ctrl(c, SSL_CTRL_GET_MAX_PROTO_VERSION, 0, NULL);
}

WF_OPENSSL(jboolean, getSSLSessionReused)(JNIEnv *e, jobject o, jlong ssl)
{
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    SSL *c = J2P(ssl, SSL *);
    int res;
    UNREFERENCED(o);

    if (ssl_methods.SSL_session_reused == NULL) {
        throwIllegalStateException(e, "OpenSSL version does not support method SSL_session_reused");
        return JNI_FALSE;
    }
    res = ssl_methods.SSL_session_reused(c);
    if (res == 1) {
        return JNI_TRUE;
    }
    return JNI_FALSE;
}


/* sets up diffie hellman params using the apps/dh2048.pem file from openssl */
void setupDH(JNIEnv *e, SSL_CTX * ctx)
{
BIO *bio;
DH *dh;
static unsigned char dhparams[]={
    0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x20, 0x44, 0x48, 0x20, 0x50, 0x41, 0x52, 0x41, 0x4D,
    0x45, 0x54, 0x45, 0x52, 0x53, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0A, 0x4D, 0x49, 0x49, 0x42, 0x43, 0x41, 0x4B, 0x43,
    0x41, 0x51, 0x45, 0x41, 0x39, 0x6B, 0x4A, 0x58, 0x74, 0x77, 0x68, 0x2F, 0x43, 0x42, 0x64, 0x79, 0x6F, 0x72, 0x72,
    0x57, 0x71, 0x55, 0x4C, 0x7A, 0x42, 0x65, 0x6A, 0x35, 0x55, 0x78, 0x45, 0x35, 0x54, 0x37, 0x62, 0x78, 0x62, 0x72,
    0x6C, 0x4C, 0x4F, 0x43, 0x44, 0x61, 0x41, 0x61, 0x64, 0x57, 0x6F, 0x78, 0x54, 0x70, 0x6A, 0x30, 0x42, 0x56, 0x0A,
    0x38, 0x39, 0x41, 0x48, 0x78, 0x73, 0x74, 0x44, 0x71, 0x5A, 0x53, 0x74, 0x39, 0x30, 0x78, 0x6B, 0x68, 0x6B, 0x6E,
    0x34, 0x44, 0x49, 0x4F, 0x39, 0x5A, 0x65, 0x6B, 0x58, 0x31, 0x4B, 0x48, 0x54, 0x55, 0x50, 0x6A, 0x31, 0x57, 0x56,
    0x2F, 0x63, 0x64, 0x6C, 0x4A, 0x50, 0x50, 0x54, 0x32, 0x4E, 0x32, 0x38, 0x36, 0x5A, 0x34, 0x56, 0x65, 0x53, 0x57,
    0x63, 0x33, 0x39, 0x75, 0x4B, 0x35, 0x30, 0x0A, 0x54, 0x38, 0x58, 0x38, 0x64, 0x72, 0x79, 0x44, 0x78, 0x55, 0x63,
    0x77, 0x59, 0x63, 0x35, 0x38, 0x79, 0x57, 0x62, 0x2F, 0x46, 0x66, 0x6D, 0x37, 0x2F, 0x5A, 0x46, 0x65, 0x78, 0x77,
    0x47, 0x71, 0x30, 0x31, 0x75, 0x65, 0x6A, 0x61, 0x43, 0x6C, 0x63, 0x6A, 0x72, 0x55, 0x47, 0x76, 0x43, 0x2F, 0x52,
    0x67, 0x42, 0x59, 0x4B, 0x2B, 0x58, 0x30, 0x69, 0x50, 0x31, 0x59, 0x54, 0x6B, 0x6E, 0x62, 0x0A, 0x7A, 0x53, 0x43,
    0x30, 0x6E, 0x65, 0x53, 0x52, 0x42, 0x7A, 0x5A, 0x72, 0x4D, 0x32, 0x77, 0x34, 0x44, 0x55, 0x55, 0x64, 0x44, 0x33,
    0x79, 0x49, 0x73, 0x78, 0x78, 0x38, 0x57, 0x79, 0x32, 0x4F, 0x39, 0x76, 0x50, 0x4A, 0x49, 0x38, 0x42, 0x44, 0x38,
    0x4B, 0x56, 0x62, 0x47, 0x49, 0x32, 0x4F, 0x75, 0x31, 0x57, 0x4D, 0x75, 0x46, 0x30, 0x34, 0x30, 0x7A, 0x54, 0x39,
    0x66, 0x42, 0x64, 0x58, 0x0A, 0x51, 0x36, 0x4D, 0x64, 0x47, 0x47, 0x7A, 0x65, 0x4D, 0x79, 0x45, 0x73, 0x74, 0x53,
    0x72, 0x2F, 0x50, 0x4F, 0x47, 0x78, 0x4B, 0x55, 0x41, 0x59, 0x45, 0x59, 0x31, 0x38, 0x68, 0x4B, 0x63, 0x4B, 0x63,
    0x74, 0x61, 0x47, 0x78, 0x41, 0x4D, 0x5A, 0x79, 0x41, 0x63, 0x70, 0x65, 0x73, 0x71, 0x56, 0x44, 0x4E, 0x6D, 0x57,
    0x6E, 0x36, 0x76, 0x51, 0x43, 0x6C, 0x43, 0x62, 0x41, 0x6B, 0x62, 0x54, 0x0A, 0x43, 0x44, 0x31, 0x6D, 0x70, 0x46,
    0x31, 0x42, 0x6E, 0x35, 0x78, 0x38, 0x76, 0x59, 0x6C, 0x4C, 0x49, 0x68, 0x6B, 0x6D, 0x75, 0x71, 0x75, 0x69, 0x58,
    0x73, 0x4E, 0x56, 0x36, 0x54, 0x49, 0x4C, 0x4F, 0x77, 0x49, 0x42, 0x41, 0x67, 0x3D, 0x3D, 0x0A, 0x2D, 0x2D, 0x2D,
    0x2D, 0x2D, 0x45, 0x4E, 0x44, 0x20, 0x44, 0x48, 0x20, 0x50, 0x41, 0x52, 0x41, 0x4D, 0x45, 0x54, 0x45, 0x52, 0x53,
    0x2D, 0x2D, 0x2D, 0x2D, 0x2D, };
    bio = crypto_methods.BIO_new(crypto_methods.BIO_s_mem());
    if (!bio) {
        char err[2048];
        generate_openssl_stack_error(e, err, sizeof(err));
        tcn_Throw(e, "Error while configuring DH %s", err);
        return;
    }
    crypto_methods.BIO_write(bio, dhparams, sizeof(dhparams));

    dh = crypto_methods.PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
    crypto_methods.BIO_free(bio);
    if (!dh) {
        char err[2048];
        generate_openssl_stack_error(e, err, sizeof(err));
        tcn_Throw(e, "Error while configuring DH: no DH parameter found (%s)", err);
        return;
    }

    if (1 != ssl_methods.SSL_CTX_ctrl(ctx,SSL_CTRL_SET_TMP_DH,0,(char *)dh)) {
        char err[2048];
        crypto_methods.DH_free(dh);
        generate_openssl_stack_error(e, err, sizeof(err));
        tcn_Throw(e, "Error while configuring DH: %s", err);
    }

    crypto_methods.DH_free(dh);
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy