1025 lines
29 KiB
C
1025 lines
29 KiB
C
/* jni_wolfssl_cert_manager.c
|
|
*
|
|
* Copyright (C) 2006-2026 wolfSSL Inc.
|
|
*
|
|
* This file is part of wolfSSL.
|
|
*
|
|
* wolfSSL is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* wolfSSL is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
|
|
#ifdef WOLFSSL_USER_SETTINGS
|
|
#include <wolfssl/wolfcrypt/settings.h>
|
|
#elif !defined(__ANDROID__)
|
|
#include <wolfssl/options.h>
|
|
#endif
|
|
|
|
#include <wolfssl/ssl.h>
|
|
#include <wolfssl/error-ssl.h>
|
|
#include <wolfssl/ocsp.h>
|
|
#include <wolfssl/wolfcrypt/asn.h>
|
|
#include <wolfssl/wolfcrypt/asn_public.h>
|
|
#include <com_wolfssl_wolfcrypt_WolfSSLCertManager.h>
|
|
#include <wolfcrypt_jni_error.h>
|
|
|
|
/* #define WOLFCRYPT_JNI_DEBUG_ON */
|
|
#include <wolfcrypt_jni_debug.h>
|
|
|
|
/* Struct holding Java callback information for verify callback to use for
|
|
* reaching back to Java. Stored in CallbackNode struct, which is is mapped to
|
|
* WOLFSSL_CERT_MANAGER pointer in CallbackNode. */
|
|
typedef struct {
|
|
JavaVM* jvm; /* Java VM pointer for thread attachment */
|
|
jobject callback; /* Global reference to Java callback object */
|
|
} VerifyCallbackCtx;
|
|
|
|
/* Linked list node struct for callback context storage */
|
|
typedef struct CallbackNode {
|
|
WOLFSSL_CERT_MANAGER* cm; /* Native WOLFSSL_CERT_MANAGER pointer */
|
|
VerifyCallbackCtx* ctx; /* Java verify callback info */
|
|
struct CallbackNode* next;
|
|
} CallbackNode;
|
|
|
|
/* Global linked list of CallbackNode structs, protected by mutex */
|
|
static CallbackNode* g_callbackList = NULL;
|
|
|
|
/* Mutex for protecting g_callbackList access */
|
|
static wolfSSL_Mutex g_callbackMutex;
|
|
|
|
/* Flag to track if g_callbackMutex has been initialized. */
|
|
static int g_mutexInitialized = 0;
|
|
|
|
/* Initialize the global g_callbackMutex. Called from JNI_OnLoad() to ensure
|
|
* thread-safe init before any CertManager operations.
|
|
*
|
|
* Returns 0 on success, negative on error. */
|
|
int wolfSSL_CertManager_init(void)
|
|
{
|
|
if (!g_mutexInitialized) {
|
|
if (wc_InitMutex(&g_callbackMutex) != 0) {
|
|
return -1;
|
|
}
|
|
g_mutexInitialized = 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Free the global g_callbackMutex. Called from JNI_OnUnload() when native
|
|
* library is unloaded. */
|
|
void wolfSSL_CertManager_cleanup(void)
|
|
{
|
|
if (g_mutexInitialized) {
|
|
wc_FreeMutex(&g_callbackMutex);
|
|
g_mutexInitialized = 0;
|
|
}
|
|
}
|
|
|
|
/* Find correct VerifyCallbackCtx in global g_callbackList by comparing
|
|
* WOLFSSL_CERT_MANAGER pointers.
|
|
*
|
|
* Caller must hold g_callbackMutex.
|
|
*
|
|
* Returns NULL if not found, or VerifyCallbackCtx pointer on success. */
|
|
static VerifyCallbackCtx* findCallbackCtx(WOLFSSL_CERT_MANAGER* cm)
|
|
{
|
|
CallbackNode* node = g_callbackList;
|
|
|
|
while (node != NULL) {
|
|
if (node->cm == cm) {
|
|
return node->ctx;
|
|
}
|
|
node = node->next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Add VerifyCallbackCtx to global g_callbackList
|
|
*
|
|
* Caller must hold g_callbackMutex.
|
|
*
|
|
* Returns 0 on success, negative on error. */
|
|
static int addCallbackCtx(WOLFSSL_CERT_MANAGER* cm, VerifyCallbackCtx* ctx)
|
|
{
|
|
CallbackNode* node = NULL;
|
|
|
|
if (cm == NULL || ctx == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
/* Allocate new node */
|
|
node = (CallbackNode*)XMALLOC(sizeof(CallbackNode), NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (node == NULL) {
|
|
return MEMORY_E;
|
|
}
|
|
|
|
/* Store WOLFSSL_CERT_MANAGER and context */
|
|
node->cm = cm;
|
|
node->ctx = ctx;
|
|
|
|
/* Add to front of global callback list */
|
|
node->next = g_callbackList;
|
|
g_callbackList = node;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Remove CallbackNode from global g_callbackList.
|
|
*
|
|
* Caller must hold g_callbackMutex. */
|
|
static void removeCallbackCtx(WOLFSSL_CERT_MANAGER* cm)
|
|
{
|
|
CallbackNode* node = g_callbackList;
|
|
CallbackNode* prev = NULL;
|
|
|
|
if (cm == NULL) {
|
|
return;
|
|
}
|
|
|
|
while (node != NULL) {
|
|
if (node->cm == cm) {
|
|
/* Found matching node, remove from list */
|
|
if (prev == NULL) {
|
|
g_callbackList = node->next;
|
|
}
|
|
else {
|
|
prev->next = node->next;
|
|
}
|
|
|
|
/* Free node (ctx is freed separately) */
|
|
XFREE(node, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
return;
|
|
}
|
|
|
|
prev = node;
|
|
node = node->next;
|
|
}
|
|
}
|
|
|
|
/* Extract cert DER bytes at given depth from WOLFSSL_X509_STORE_CTX into
|
|
* a new jbyteArray. Returns NULL if cert not available at depth. */
|
|
static jbyteArray getCertDerAtDepth(JNIEnv* jenv,
|
|
WOLFSSL_X509_STORE_CTX* store, int depth)
|
|
{
|
|
jbyteArray der = NULL;
|
|
WOLFSSL_BUFFER_INFO* certInfo = NULL;
|
|
|
|
if (store->certs == NULL || depth < 0 || depth >= store->totalCerts) {
|
|
return NULL;
|
|
}
|
|
|
|
certInfo = &store->certs[depth];
|
|
if (certInfo->buffer == NULL || certInfo->length == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
der = (*jenv)->NewByteArray(jenv, (jsize)certInfo->length);
|
|
if (der != NULL) {
|
|
(*jenv)->SetByteArrayRegion(jenv, der, 0, (jsize)certInfo->length,
|
|
(const jbyte*)certInfo->buffer);
|
|
}
|
|
|
|
return der;
|
|
}
|
|
|
|
/* Native verify callback that calls into Java verify callback.
|
|
*
|
|
* This is registered with wolfSSL_CertManagerSetVerify() and called
|
|
* during certificate verification.
|
|
*
|
|
* Return of 0 causes verification to fail, otherwise non-zero error
|
|
* indicates verification should continue. */
|
|
static int nativeVerifyCallback(int preverify, WOLFSSL_X509_STORE_CTX* store)
|
|
{
|
|
int needsDetach = 0;
|
|
int error = 0;
|
|
int errorDepth = 0;
|
|
jint result = 0;
|
|
JNIEnv* jenv = NULL;
|
|
JavaVM* jvm = NULL;
|
|
VerifyCallbackCtx* ctx = NULL;
|
|
jobject localCallback = NULL;
|
|
jbyteArray certDer = NULL;
|
|
jclass callbackClass = NULL;
|
|
jmethodID verifyMethod = NULL;
|
|
|
|
if (store == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if (wc_LockMutex(&g_callbackMutex) != 0) {
|
|
return 0;
|
|
}
|
|
|
|
/* wolfSSL sets store->userCtx to the WOLFSSL_CERT_MANAGER that
|
|
* initiated verification, use it to find the matching callback */
|
|
if (store->userCtx != NULL) {
|
|
ctx = findCallbackCtx((WOLFSSL_CERT_MANAGER*)store->userCtx);
|
|
}
|
|
|
|
/* Fall back to the cm pointer in store if the userCtx lookup missed.
|
|
* Both are exact cm matches, so neither can dispatch to the wrong CM. */
|
|
if (ctx == NULL && store->store != NULL && store->store->cm != NULL) {
|
|
ctx = findCallbackCtx(store->store->cm);
|
|
}
|
|
|
|
/* No callbacks found, verification falls back to preverify. Logging it
|
|
* here for visibility. */
|
|
if (ctx == NULL) {
|
|
LogStr("nativeVerifyCallback: no callback ctx found for "
|
|
"userCtx=%p, cm=%p, using preverify=%d\n",
|
|
store->userCtx,
|
|
(store->store != NULL) ? (void*)store->store->cm : NULL,
|
|
preverify);
|
|
}
|
|
|
|
/* No callback registered, use preverify result */
|
|
if (ctx == NULL || ctx->callback == NULL) {
|
|
wc_UnLockMutex(&g_callbackMutex);
|
|
return preverify;
|
|
}
|
|
|
|
/* Capture jvm and take our own local reference to the callback while
|
|
* still holding g_callbackMutex. A concurrent CertManagerClearVerify()
|
|
* deletes the global reference and frees ctx after releasing the mutex,
|
|
* so nothing below dereferences ctx or the global reference. */
|
|
jvm = ctx->jvm;
|
|
|
|
/* Get JNIEnv for current thread. Native callback may be called from
|
|
* different thread than original Java thread. */
|
|
if ((*jvm)->GetEnv(jvm, (void**)&jenv,
|
|
JNI_VERSION_1_6) == JNI_EDETACHED) {
|
|
#ifdef __ANDROID__
|
|
result = (*jvm)->AttachCurrentThread(jvm, &jenv, NULL);
|
|
#else
|
|
result = (*jvm)->AttachCurrentThread(jvm, (void**)&jenv, NULL);
|
|
#endif
|
|
if (result != 0) {
|
|
/* Failed to attach, reject */
|
|
wc_UnLockMutex(&g_callbackMutex);
|
|
return 0;
|
|
}
|
|
needsDetach = 1;
|
|
}
|
|
|
|
/* Local reference keeps the callback object alive independent of ctx's
|
|
* global reference, which a concurrent ClearVerify may delete. */
|
|
localCallback = (*jenv)->NewLocalRef(jenv, ctx->callback);
|
|
|
|
wc_UnLockMutex(&g_callbackMutex);
|
|
|
|
/* ctx must not be dereferenced past this point, it may have been freed
|
|
* by a concurrent CertManagerClearVerify(). */
|
|
if (localCallback == NULL) {
|
|
if (needsDetach) {
|
|
(*jvm)->DetachCurrentThread(jvm);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Get error code and depth from store */
|
|
error = store->error;
|
|
errorDepth = store->error_depth;
|
|
|
|
/* If available, extract cert DER bytes from store context at errorDepth */
|
|
certDer = getCertDerAtDepth(jenv, store, errorDepth);
|
|
|
|
/* Find verify() method on callback object */
|
|
callbackClass = (*jenv)->GetObjectClass(jenv, localCallback);
|
|
if (callbackClass == NULL) {
|
|
if (certDer != NULL) {
|
|
(*jenv)->DeleteLocalRef(jenv, certDer);
|
|
}
|
|
(*jenv)->DeleteLocalRef(jenv, localCallback);
|
|
if (needsDetach) {
|
|
(*jvm)->DetachCurrentThread(jvm);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
verifyMethod = (*jenv)->GetMethodID(jenv, callbackClass,
|
|
"verify", "(III[B)I");
|
|
if (verifyMethod == NULL) {
|
|
if (certDer != NULL) {
|
|
(*jenv)->DeleteLocalRef(jenv, certDer);
|
|
}
|
|
(*jenv)->DeleteLocalRef(jenv, callbackClass);
|
|
(*jenv)->DeleteLocalRef(jenv, localCallback);
|
|
if (needsDetach) {
|
|
(*jvm)->DetachCurrentThread(jvm);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Call Java callback.verify(preverify, error, errorDepth, certDer) */
|
|
result = (*jenv)->CallIntMethod(jenv, localCallback, verifyMethod,
|
|
(jint)preverify, (jint)error, (jint)errorDepth, certDer);
|
|
|
|
/* Check for Java exceptions, reject on exception */
|
|
if ((*jenv)->ExceptionCheck(jenv)) {
|
|
(*jenv)->ExceptionDescribe(jenv);
|
|
(*jenv)->ExceptionClear(jenv);
|
|
result = 0;
|
|
}
|
|
|
|
/* If callback accepts the certificate (returns 1), clear the error.
|
|
* This is necessary because wolfSSL checks the error code after
|
|
* calling the callback. If we override an error, we must clear it. */
|
|
if (result == 1 && store->error != 0) {
|
|
store->error = 0;
|
|
}
|
|
|
|
if (certDer != NULL) {
|
|
(*jenv)->DeleteLocalRef(jenv, certDer);
|
|
}
|
|
(*jenv)->DeleteLocalRef(jenv, callbackClass);
|
|
(*jenv)->DeleteLocalRef(jenv, localCallback);
|
|
if (needsDetach) {
|
|
(*jvm)->DetachCurrentThread(jvm);
|
|
}
|
|
|
|
return (int)result;
|
|
}
|
|
|
|
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_getWOLFSSL_1LOAD_1FLAG_1DATE_1ERR_1OKAY
|
|
(JNIEnv* env, jclass jcl)
|
|
{
|
|
(void)env;
|
|
(void)jcl;
|
|
|
|
return WOLFSSL_LOAD_FLAG_DATE_ERR_OKAY;
|
|
}
|
|
|
|
JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerNew
|
|
(JNIEnv* env, jclass jcl)
|
|
{
|
|
(void)env;
|
|
(void)jcl;
|
|
|
|
/* Mutex should be initialized in JNI_OnLoad(). If not initialized,
|
|
* try again here as fallback. */
|
|
if (!g_mutexInitialized) {
|
|
if (wolfSSL_CertManager_init() != 0) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return (jlong)(uintptr_t)wolfSSL_CertManagerNew();
|
|
}
|
|
|
|
JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerFree
|
|
(JNIEnv* env, jclass jcl, jlong cmPtr)
|
|
{
|
|
(void)env;
|
|
(void)jcl;
|
|
|
|
wolfSSL_CertManagerFree((WOLFSSL_CERT_MANAGER*)(uintptr_t)cmPtr);
|
|
}
|
|
|
|
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerLoadCA
|
|
(JNIEnv* env, jclass jcl, jlong cmPtr, jstring f, jstring d)
|
|
{
|
|
#ifndef NO_FILESYSTEM
|
|
int ret;
|
|
const char* certFile = NULL;
|
|
const char* certPath = NULL;
|
|
WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)(uintptr_t)cmPtr;
|
|
(void)jcl;
|
|
|
|
if (env == NULL || cm == NULL) {
|
|
return (jint)BAD_FUNC_ARG;
|
|
}
|
|
|
|
/* f and d are both optional, pass NULL through to native wolfSSL */
|
|
if (f != NULL) {
|
|
certFile = (*env)->GetStringUTFChars(env, f, 0);
|
|
if (certFile == NULL) {
|
|
return (jint)MEMORY_E;
|
|
}
|
|
}
|
|
if (d != NULL) {
|
|
certPath = (*env)->GetStringUTFChars(env, d, 0);
|
|
if (certPath == NULL) {
|
|
if (certFile != NULL) {
|
|
(*env)->ReleaseStringUTFChars(env, f, certFile);
|
|
}
|
|
return (jint)MEMORY_E;
|
|
}
|
|
}
|
|
|
|
ret = wolfSSL_CertManagerLoadCA(cm, certFile, certPath);
|
|
|
|
if (certFile != NULL) {
|
|
(*env)->ReleaseStringUTFChars(env, f, certFile);
|
|
}
|
|
if (certPath != NULL) {
|
|
(*env)->ReleaseStringUTFChars(env, d, certPath);
|
|
}
|
|
|
|
return (jint)ret;
|
|
#else
|
|
(void)env;
|
|
(void)jcl;
|
|
(void)cmPtr;
|
|
(void)f;
|
|
(void)d;
|
|
return NOT_COMPILED_IN;
|
|
#endif
|
|
}
|
|
|
|
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerLoadCABuffer
|
|
(JNIEnv* env, jclass jcl, jlong cmPtr, jbyteArray in, jlong sz, jint format)
|
|
{
|
|
int ret = 0;
|
|
word32 buffSz = 0;
|
|
byte* buff = NULL;
|
|
WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)(uintptr_t)cmPtr;
|
|
(void)jcl;
|
|
|
|
if (env == NULL || in == NULL || (sz < 0) ||
|
|
(sz > (jlong)(*env)->GetArrayLength(env, in))) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
buff = (byte*)(*env)->GetByteArrayElements(env, in, NULL);
|
|
buffSz = (word32)sz;
|
|
|
|
ret = wolfSSL_CertManagerLoadCABuffer(cm, buff, buffSz, format);
|
|
|
|
(*env)->ReleaseByteArrayElements(env, in, (jbyte*)buff, JNI_ABORT);
|
|
|
|
return (jint)ret;
|
|
}
|
|
|
|
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerLoadCABufferEx
|
|
(JNIEnv* env, jclass jcl, jlong cmPtr, jbyteArray in, jlong sz, jint format, jint flags)
|
|
{
|
|
int ret = 0;
|
|
word32 buffSz = 0;
|
|
byte* buff = NULL;
|
|
WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)(uintptr_t)cmPtr;
|
|
(void)jcl;
|
|
|
|
if (env == NULL || in == NULL || (sz < 0) ||
|
|
(sz > (jlong)(*env)->GetArrayLength(env, in))) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
buff = (byte*)(*env)->GetByteArrayElements(env, in, NULL);
|
|
buffSz = (word32)sz;
|
|
|
|
ret = wolfSSL_CertManagerLoadCABuffer_ex(cm, buff, buffSz, format, 0,
|
|
(word32)flags);
|
|
|
|
(*env)->ReleaseByteArrayElements(env, in, (jbyte*)buff, JNI_ABORT);
|
|
|
|
return (jint)ret;
|
|
}
|
|
|
|
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerUnloadCAs
|
|
(JNIEnv* env, jclass jcl, jlong cmPtr)
|
|
{
|
|
int ret = 0;
|
|
WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)(uintptr_t)cmPtr;
|
|
(void)jcl;
|
|
|
|
if (env == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
ret = wolfSSL_CertManagerUnloadCAs(cm);
|
|
|
|
return (jint)ret;
|
|
}
|
|
|
|
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerVerifyBuffer
|
|
(JNIEnv* env, jclass jcl, jlong cmPtr, jbyteArray in, jlong sz, jint format)
|
|
{
|
|
int ret = 0;
|
|
word32 buffSz = 0;
|
|
byte* buff = NULL;
|
|
WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)(uintptr_t)cmPtr;
|
|
(void)jcl;
|
|
|
|
if (env == NULL || in == NULL || (sz < 0) ||
|
|
(sz > (jlong)(*env)->GetArrayLength(env, in))) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
buff = (byte*)(*env)->GetByteArrayElements(env, in, NULL);
|
|
buffSz = (word32)sz;
|
|
|
|
ret = wolfSSL_CertManagerVerifyBuffer(cm, buff, buffSz, format);
|
|
|
|
(*env)->ReleaseByteArrayElements(env, in, (jbyte*)buff, JNI_ABORT);
|
|
|
|
return (jint)ret;
|
|
}
|
|
|
|
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerEnableCRL
|
|
(JNIEnv* env, jclass jcl, jlong cmPtr, jint options)
|
|
{
|
|
#ifdef HAVE_CRL
|
|
WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)(uintptr_t)cmPtr;
|
|
(void)jcl;
|
|
|
|
if (env == NULL || cm == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
return wolfSSL_CertManagerEnableCRL(cm, (int)options);
|
|
|
|
#else
|
|
(void)env;
|
|
(void)jcl;
|
|
(void)cmPtr;
|
|
(void)options;
|
|
return NOT_COMPILED_IN;
|
|
#endif
|
|
}
|
|
|
|
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerDisableCRL
|
|
(JNIEnv* env, jclass jcl, jlong cmPtr)
|
|
{
|
|
#ifdef HAVE_CRL
|
|
WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)(uintptr_t)cmPtr;
|
|
(void)jcl;
|
|
|
|
if (env == NULL || cm == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
return wolfSSL_CertManagerDisableCRL(cm);
|
|
|
|
#else
|
|
(void)env;
|
|
(void)jcl;
|
|
(void)cmPtr;
|
|
return NOT_COMPILED_IN;
|
|
#endif
|
|
}
|
|
|
|
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerLoadCRLBuffer
|
|
(JNIEnv* env, jclass jcl, jlong cmPtr, jbyteArray in, jlong sz, jint type)
|
|
{
|
|
#ifdef HAVE_CRL
|
|
int ret = 0;
|
|
word32 buffSz = 0;
|
|
byte* buff = NULL;
|
|
WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)(uintptr_t)cmPtr;
|
|
(void)jcl;
|
|
|
|
if (env == NULL || in == NULL || (sz < 0) ||
|
|
(sz > (jlong)(*env)->GetArrayLength(env, in))) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
buff = (byte*)(*env)->GetByteArrayElements(env, in, NULL);
|
|
buffSz = (word32)sz;
|
|
|
|
ret = wolfSSL_CertManagerLoadCRLBuffer(cm, buff, buffSz, type);
|
|
|
|
(*env)->ReleaseByteArrayElements(env, in, (jbyte*)buff, JNI_ABORT);
|
|
|
|
return (jint)ret;
|
|
#else
|
|
(void)env;
|
|
(void)jcl;
|
|
(void)cmPtr;
|
|
(void)in;
|
|
(void)sz;
|
|
(void)type;
|
|
return NOT_COMPILED_IN;
|
|
#endif
|
|
}
|
|
|
|
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerEnableOCSP
|
|
(JNIEnv* env, jclass jcl, jlong cmPtr, jint options)
|
|
{
|
|
#ifdef HAVE_OCSP
|
|
WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)(uintptr_t)cmPtr;
|
|
(void)jcl;
|
|
|
|
if (env == NULL || cm == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
return wolfSSL_CertManagerEnableOCSP(cm, (int)options);
|
|
|
|
#else
|
|
(void)env;
|
|
(void)jcl;
|
|
(void)cmPtr;
|
|
(void)options;
|
|
return NOT_COMPILED_IN;
|
|
#endif
|
|
}
|
|
|
|
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerDisableOCSP
|
|
(JNIEnv* env, jclass jcl, jlong cmPtr)
|
|
{
|
|
#ifdef HAVE_OCSP
|
|
WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)(uintptr_t)cmPtr;
|
|
(void)jcl;
|
|
|
|
if (env == NULL || cm == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
return wolfSSL_CertManagerDisableOCSP(cm);
|
|
|
|
#else
|
|
(void)env;
|
|
(void)jcl;
|
|
(void)cmPtr;
|
|
return NOT_COMPILED_IN;
|
|
#endif
|
|
}
|
|
|
|
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerSetOCSPOverrideURL
|
|
(JNIEnv* env, jclass jcl, jlong cmPtr, jstring url)
|
|
{
|
|
#ifdef HAVE_OCSP
|
|
int ret = 0;
|
|
const char* urlStr = NULL;
|
|
WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)(uintptr_t)cmPtr;
|
|
(void)jcl;
|
|
|
|
if (env == NULL || cm == NULL || url == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
urlStr = (*env)->GetStringUTFChars(env, url, NULL);
|
|
if (urlStr == NULL) {
|
|
return MEMORY_E;
|
|
}
|
|
|
|
ret = wolfSSL_CertManagerSetOCSPOverrideURL(cm, urlStr);
|
|
|
|
(*env)->ReleaseStringUTFChars(env, url, urlStr);
|
|
|
|
return (jint)ret;
|
|
|
|
#else
|
|
(void)env;
|
|
(void)jcl;
|
|
(void)cmPtr;
|
|
(void)url;
|
|
return NOT_COMPILED_IN;
|
|
#endif
|
|
}
|
|
|
|
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerCheckOCSP
|
|
(JNIEnv* env, jclass jcl, jlong cmPtr, jbyteArray cert, jint sz)
|
|
{
|
|
#ifdef HAVE_OCSP
|
|
int ret = 0;
|
|
word32 certSz = 0;
|
|
byte* certBuf = NULL;
|
|
WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)(uintptr_t)cmPtr;
|
|
(void)jcl;
|
|
|
|
if (env == NULL || cm == NULL || cert == NULL || (sz < 0) ||
|
|
(sz > (*env)->GetArrayLength(env, cert))) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
certBuf = (byte*)(*env)->GetByteArrayElements(env, cert, NULL);
|
|
if (certBuf == NULL) {
|
|
return MEMORY_E;
|
|
}
|
|
certSz = (word32)sz;
|
|
|
|
ret = wolfSSL_CertManagerCheckOCSP(cm, certBuf, certSz);
|
|
|
|
(*env)->ReleaseByteArrayElements(env, cert, (jbyte*)certBuf, JNI_ABORT);
|
|
|
|
LogStr("wolfSSL_CertManagerCheckOCSP(cm=%p, certSz=%d) = %d\n",
|
|
cm, certSz, ret);
|
|
|
|
return (jint)ret;
|
|
|
|
#else
|
|
(void)env;
|
|
(void)jcl;
|
|
(void)cmPtr;
|
|
(void)cert;
|
|
(void)sz;
|
|
return NOT_COMPILED_IN;
|
|
#endif
|
|
}
|
|
|
|
/* Verify OCSP response for a certificate.
|
|
*
|
|
* Returns 0 (WOLFSSL_SUCCESS) on valid OCSP response, negative on error:
|
|
* BAD_FUNC_ARG (-173) - Invalid arguments (NULL pointers, negative sizes)
|
|
* MEMORY_E (-125) - Memory allocation failure
|
|
* ASN_PARSE_E (-140) - Failed to parse OCSP response or certificate
|
|
* OCSP_LOOKUP_FAIL - OCSP response status not successful
|
|
* OCSP_CERT_REVOKED - Certificate has been revoked
|
|
* OCSP_CERT_UNKNOWN - Certificate status unknown
|
|
*/
|
|
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerCheckOCSPResponse
|
|
(JNIEnv* env, jclass jcl, jlong cmPtr, jbyteArray response,
|
|
jint responseSz, jbyteArray cert, jint certSz)
|
|
{
|
|
#ifdef HAVE_OCSP
|
|
int ret = 0;
|
|
word32 respBufSz = 0;
|
|
word32 certBufSz = 0;
|
|
byte* respBuf = NULL;
|
|
byte* certBuf = NULL;
|
|
WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)(uintptr_t)cmPtr;
|
|
OcspEntry* entry = NULL;
|
|
CertStatus* status = NULL;
|
|
OcspRequest* request = NULL;
|
|
DecodedCert* cert_decoded = NULL;
|
|
(void)jcl;
|
|
|
|
if (env == NULL || cm == NULL || response == NULL || cert == NULL ||
|
|
(responseSz < 0) || (certSz < 0) ||
|
|
(responseSz > (*env)->GetArrayLength(env, response)) ||
|
|
(certSz > (*env)->GetArrayLength(env, cert))) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
/* Get response buffer and size */
|
|
respBuf = (byte*)(*env)->GetByteArrayElements(env, response, NULL);
|
|
if (respBuf == NULL) {
|
|
return MEMORY_E;
|
|
}
|
|
respBufSz = (word32)responseSz;
|
|
|
|
/* Get cert buffer and size */
|
|
certBuf = (byte*)(*env)->GetByteArrayElements(env, cert, NULL);
|
|
if (certBuf == NULL) {
|
|
(*env)->ReleaseByteArrayElements(env, response,
|
|
(jbyte*)respBuf, JNI_ABORT);
|
|
return MEMORY_E;
|
|
}
|
|
certBufSz = (word32)certSz;
|
|
|
|
/* Allocate request and decoded cert structures */
|
|
request = wolfSSL_OCSP_REQUEST_new();
|
|
if (request != NULL) {
|
|
cert_decoded = (DecodedCert*)XMALLOC(sizeof(DecodedCert), NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
}
|
|
|
|
if (request != NULL && cert_decoded != NULL) {
|
|
/* Decode the certificate to extract OCSP request info */
|
|
InitDecodedCert(cert_decoded, certBuf, certBufSz, NULL);
|
|
ret = ParseCert(cert_decoded, CERT_TYPE, NO_VERIFY, NULL);
|
|
if (ret == 0) {
|
|
/* Populate OcspRequest from decoded certificate.
|
|
* Need serial number, issuer hash, and issuer key hash. */
|
|
if (cert_decoded->serialSz > 0) {
|
|
request->serialSz = cert_decoded->serialSz;
|
|
request->serial = (byte*)XMALLOC(cert_decoded->serialSz,
|
|
NULL, DYNAMIC_TYPE_OCSP_REQUEST);
|
|
if (request->serial != NULL) {
|
|
XMEMCPY(request->serial, cert_decoded->serial,
|
|
cert_decoded->serialSz);
|
|
XMEMCPY(request->issuerHash, cert_decoded->issuerHash,
|
|
KEYID_SIZE);
|
|
XMEMCPY(request->issuerKeyHash,
|
|
cert_decoded->issuerKeyHash, KEYID_SIZE);
|
|
|
|
/* CertManager operations are internally thread-safe
|
|
* wolfSSL uses mutex locking on the OCSP cache. */
|
|
ret = wolfSSL_CertManagerCheckOCSPResponse(cm, respBuf,
|
|
respBufSz, NULL, status, entry, request);
|
|
}
|
|
else {
|
|
ret = MEMORY_E;
|
|
}
|
|
}
|
|
else {
|
|
ret = ASN_PARSE_E;
|
|
}
|
|
}
|
|
FreeDecodedCert(cert_decoded);
|
|
}
|
|
else {
|
|
ret = MEMORY_E;
|
|
}
|
|
|
|
/* Release JNI byte array elements */
|
|
(*env)->ReleaseByteArrayElements(env, response,
|
|
(jbyte*)respBuf, JNI_ABORT);
|
|
(*env)->ReleaseByteArrayElements(env, cert,
|
|
(jbyte*)certBuf, JNI_ABORT);
|
|
|
|
/* Free allocated native structures */
|
|
if (request != NULL) {
|
|
wolfSSL_OCSP_REQUEST_free(request);
|
|
}
|
|
if (cert_decoded != NULL) {
|
|
XFREE(cert_decoded, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
}
|
|
|
|
return (jint)ret;
|
|
|
|
#else
|
|
(void)env;
|
|
(void)jcl;
|
|
(void)cmPtr;
|
|
(void)response;
|
|
(void)responseSz;
|
|
(void)cert;
|
|
(void)certSz;
|
|
return NOT_COMPILED_IN;
|
|
#endif
|
|
}
|
|
|
|
/* Get OCSPResponseStatus from raw OCSP response bytes.
|
|
*
|
|
* Uses wolfSSL_d2i_OCSP_RESPONSE() to parse response and
|
|
* wolfSSL_OCSP_response_status() to extract the status.
|
|
*
|
|
* RFC 6960:
|
|
* OCSPResponse ::= SEQUENCE {
|
|
* responseStatus OCSPResponseStatus,
|
|
* responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
|
|
* OCSPResponseStatus ::= ENUMERATED {
|
|
* successful (0), malformedRequest (1), internalError (2),
|
|
* tryLater (3), sigRequired (5), unauthorized (6) }
|
|
*
|
|
* Returns OCSPResponseStatus ENUMERATED value (0-6) on success, or negative
|
|
* error code on failure (BAD_FUNC_ARG, MEMORY_E, NOT_COMPILED_IN, or -1 on
|
|
* parse failure).
|
|
*/
|
|
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_OcspResponseStatus
|
|
(JNIEnv* env, jclass jcl, jbyteArray response, jint responseSz)
|
|
{
|
|
#if defined(HAVE_OCSP) && defined(OPENSSL_EXTRA)
|
|
int ret = -1;
|
|
jint arrLen = 0;
|
|
byte* respBuf = NULL;
|
|
const unsigned char* p = NULL;
|
|
OcspResponse* ocspResp = NULL;
|
|
(void)jcl;
|
|
|
|
if (env == NULL || response == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
arrLen = (*env)->GetArrayLength(env, response);
|
|
if (arrLen <= 0 || responseSz <= 0 || responseSz > arrLen) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
respBuf = (byte*)(*env)->GetByteArrayElements(env, response, NULL);
|
|
if (respBuf == NULL) {
|
|
return MEMORY_E;
|
|
}
|
|
|
|
p = (const unsigned char*)respBuf;
|
|
ocspResp = wolfSSL_d2i_OCSP_RESPONSE(NULL, &p, (int)responseSz);
|
|
if (ocspResp != NULL) {
|
|
ret = wolfSSL_OCSP_response_status(ocspResp);
|
|
wolfSSL_OCSP_RESPONSE_free(ocspResp);
|
|
}
|
|
|
|
(*env)->ReleaseByteArrayElements(env, response, (jbyte*)respBuf, JNI_ABORT);
|
|
|
|
return (jint)ret;
|
|
|
|
#else
|
|
(void)env;
|
|
(void)jcl;
|
|
(void)response;
|
|
(void)responseSz;
|
|
return NOT_COMPILED_IN;
|
|
#endif /* HAVE_OCSP && OPENSSL_EXTRA */
|
|
}
|
|
|
|
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerSetVerify
|
|
(JNIEnv* env, jclass jcl, jlong cmPtr, jobject callback)
|
|
{
|
|
#ifndef NO_WOLFSSL_CM_VERIFY
|
|
int ret = 0;
|
|
WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)(uintptr_t)cmPtr;
|
|
VerifyCallbackCtx* ctx = NULL;
|
|
JavaVM* jvm = NULL;
|
|
(void)jcl;
|
|
|
|
if (env == NULL || cm == NULL || callback == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
/* Get JavaVM pointer for thread attachment in callback */
|
|
if ((*env)->GetJavaVM(env, &jvm) != 0) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
/* Allocate context structure to store callback info */
|
|
ctx = (VerifyCallbackCtx*)XMALLOC(sizeof(VerifyCallbackCtx), NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (ctx == NULL) {
|
|
return MEMORY_E;
|
|
}
|
|
|
|
/* Create global reference to callback object so it persists across
|
|
* JNI calls and thread boundaries */
|
|
ctx->callback = (*env)->NewGlobalRef(env, callback);
|
|
if (ctx->callback == NULL) {
|
|
XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
return MEMORY_E;
|
|
}
|
|
ctx->jvm = jvm;
|
|
|
|
/* Add context to global list */
|
|
if (wc_LockMutex(&g_callbackMutex) != 0) {
|
|
(*env)->DeleteGlobalRef(env, ctx->callback);
|
|
XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
return BAD_MUTEX_E;
|
|
}
|
|
|
|
ret = addCallbackCtx(cm, ctx);
|
|
|
|
wc_UnLockMutex(&g_callbackMutex);
|
|
|
|
if (ret != 0) {
|
|
(*env)->DeleteGlobalRef(env, ctx->callback);
|
|
XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
return ret;
|
|
}
|
|
|
|
/* Register native callback with wolfSSL */
|
|
wolfSSL_CertManagerSetVerify(cm, nativeVerifyCallback);
|
|
|
|
return WOLFSSL_SUCCESS;
|
|
#else
|
|
(void)env;
|
|
(void)jcl;
|
|
(void)cmPtr;
|
|
(void)callback;
|
|
return NOT_COMPILED_IN;
|
|
#endif
|
|
}
|
|
|
|
JNIEXPORT jint JNICALL
|
|
Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerClearVerify
|
|
(JNIEnv* env, jclass jcl, jlong cmPtr)
|
|
{
|
|
#ifndef NO_WOLFSSL_CM_VERIFY
|
|
WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)(uintptr_t)cmPtr;
|
|
VerifyCallbackCtx* ctx = NULL;
|
|
(void)jcl;
|
|
|
|
if (env == NULL || cm == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
/* Clear callback in wolfSSL first */
|
|
wolfSSL_CertManagerSetVerify(cm, NULL);
|
|
|
|
/* Lock mutex and find/remove callback context */
|
|
if (wc_LockMutex(&g_callbackMutex) != 0) {
|
|
return BAD_MUTEX_E;
|
|
}
|
|
|
|
ctx = findCallbackCtx(cm);
|
|
if (ctx != NULL) {
|
|
/* Remove from global list */
|
|
removeCallbackCtx(cm);
|
|
}
|
|
|
|
wc_UnLockMutex(&g_callbackMutex);
|
|
|
|
/* Free context if it existed */
|
|
if (ctx != NULL) {
|
|
/* Delete global reference to callback object */
|
|
if (ctx->callback != NULL) {
|
|
(*env)->DeleteGlobalRef(env, ctx->callback);
|
|
}
|
|
|
|
/* Free context structure */
|
|
XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
}
|
|
|
|
return WOLFSSL_SUCCESS;
|
|
#else
|
|
(void)env;
|
|
(void)jcl;
|
|
(void)cmPtr;
|
|
return NOT_COMPILED_IN;
|
|
#endif
|
|
}
|
|
|