Skip to content

Commit e3755a8

Browse files
committed
JCE: add OCSP response status check before native verification via wolfSSL_d2i_OCSP_RESPONSE()
1 parent 78a5270 commit e3755a8

4 files changed

Lines changed: 205 additions & 14 deletions

File tree

jni/include/com_wolfssl_wolfcrypt_WolfSSLCertManager.h

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

jni/jni_wolfssl_cert_manager.c

Lines changed: 105 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,32 @@ static void removeCallbackCtx(WOLFSSL_CERT_MANAGER* cm)
170170
}
171171
}
172172

173+
/* Extract cert DER bytes at given depth from WOLFSSL_X509_STORE_CTX into
174+
* a new jbyteArray. Returns NULL if cert not available at depth. */
175+
static jbyteArray getCertDerAtDepth(JNIEnv* jenv,
176+
WOLFSSL_X509_STORE_CTX* store, int depth)
177+
{
178+
jbyteArray der = NULL;
179+
WOLFSSL_BUFFER_INFO* certInfo = NULL;
180+
181+
if (store->certs == NULL || depth < 0 || depth >= store->totalCerts) {
182+
return NULL;
183+
}
184+
185+
certInfo = &store->certs[depth];
186+
if (certInfo->buffer == NULL || certInfo->length == 0) {
187+
return NULL;
188+
}
189+
190+
der = (*jenv)->NewByteArray(jenv, (jsize)certInfo->length);
191+
if (der != NULL) {
192+
(*jenv)->SetByteArrayRegion(jenv, der, 0, (jsize)certInfo->length,
193+
(const jbyte*)certInfo->buffer);
194+
}
195+
196+
return der;
197+
}
198+
173199
/* Native verify callback that calls into Java verify callback.
174200
*
175201
* This is registered with wolfSSL_CertManagerSetVerify() and called
@@ -185,6 +211,7 @@ static int nativeVerifyCallback(int preverify, WOLFSSL_X509_STORE_CTX* store)
185211
jint result = 0;
186212
JNIEnv* jenv = NULL;
187213
VerifyCallbackCtx* ctx = NULL;
214+
jbyteArray certDer = NULL;
188215
jclass callbackClass = NULL;
189216
jmethodID verifyMethod = NULL;
190217

@@ -236,28 +263,37 @@ static int nativeVerifyCallback(int preverify, WOLFSSL_X509_STORE_CTX* store)
236263
error = store->error;
237264
errorDepth = store->error_depth;
238265

266+
/* If available, extract cert DER bytes from store context at errorDepth */
267+
certDer = getCertDerAtDepth(jenv, store, errorDepth);
268+
239269
/* Find verify() method on callback object */
240270
callbackClass = (*jenv)->GetObjectClass(jenv, ctx->callback);
241271
if (callbackClass == NULL) {
272+
if (certDer != NULL) {
273+
(*jenv)->DeleteLocalRef(jenv, certDer);
274+
}
242275
if (needsDetach) {
243276
(*ctx->jvm)->DetachCurrentThread(ctx->jvm);
244277
}
245278
return 0;
246279
}
247280

248281
verifyMethod = (*jenv)->GetMethodID(jenv, callbackClass,
249-
"verify", "(III)I");
282+
"verify", "(III[B)I");
250283
if (verifyMethod == NULL) {
284+
if (certDer != NULL) {
285+
(*jenv)->DeleteLocalRef(jenv, certDer);
286+
}
251287
(*jenv)->DeleteLocalRef(jenv, callbackClass);
252288
if (needsDetach) {
253289
(*ctx->jvm)->DetachCurrentThread(ctx->jvm);
254290
}
255291
return 0;
256292
}
257293

258-
/* Call Java callback.verify(preverify, error, errorDepth) */
294+
/* Call Java callback.verify(preverify, error, errorDepth, certDer) */
259295
result = (*jenv)->CallIntMethod(jenv, ctx->callback, verifyMethod,
260-
(jint)preverify, (jint)error, (jint)errorDepth);
296+
(jint)preverify, (jint)error, (jint)errorDepth, certDer);
261297

262298
/* Check for Java exceptions, reject on exception */
263299
if ((*jenv)->ExceptionCheck(jenv)) {
@@ -273,6 +309,9 @@ static int nativeVerifyCallback(int preverify, WOLFSSL_X509_STORE_CTX* store)
273309
store->error = 0;
274310
}
275311

312+
if (certDer != NULL) {
313+
(*jenv)->DeleteLocalRef(jenv, certDer);
314+
}
276315
(*jenv)->DeleteLocalRef(jenv, callbackClass);
277316
if (needsDetach) {
278317
(*ctx->jvm)->DetachCurrentThread(ctx->jvm);
@@ -749,6 +788,69 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManager
749788
#endif
750789
}
751790

791+
/* Get OCSPResponseStatus from raw OCSP response bytes.
792+
*
793+
* Uses wolfSSL_d2i_OCSP_RESPONSE() to parse response and
794+
* wolfSSL_OCSP_response_status() to extract the status.
795+
*
796+
* RFC 6960:
797+
* OCSPResponse ::= SEQUENCE {
798+
* responseStatus OCSPResponseStatus,
799+
* responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
800+
* OCSPResponseStatus ::= ENUMERATED {
801+
* successful (0), malformedRequest (1), internalError (2),
802+
* tryLater (3), sigRequired (5), unauthorized (6) }
803+
*
804+
* Returns OCSPResponseStatus ENUMERATED value (0-6) on success, or negative
805+
* error code on failure (BAD_FUNC_ARG, MEMORY_E, NOT_COMPILED_IN, or -1 on
806+
* parse failure).
807+
*/
808+
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_OcspResponseStatus
809+
(JNIEnv* env, jclass jcl, jbyteArray response, jint responseSz)
810+
{
811+
#if defined(HAVE_OCSP) && defined(OPENSSL_EXTRA)
812+
int ret = -1;
813+
jint arrLen = 0;
814+
byte* respBuf = NULL;
815+
const unsigned char* p = NULL;
816+
OcspResponse* ocspResp = NULL;
817+
(void)jcl;
818+
(void)responseSz;
819+
820+
if (env == NULL || response == NULL) {
821+
return BAD_FUNC_ARG;
822+
}
823+
824+
arrLen = (*env)->GetArrayLength(env, response);
825+
if (arrLen <= 0) {
826+
return BAD_FUNC_ARG;
827+
}
828+
829+
respBuf = (byte*)(*env)->GetByteArrayElements(env, response, NULL);
830+
if (respBuf == NULL) {
831+
return MEMORY_E;
832+
}
833+
834+
p = (const unsigned char*)respBuf;
835+
ocspResp = wolfSSL_d2i_OCSP_RESPONSE(NULL, &p, (int)arrLen);
836+
if (ocspResp != NULL) {
837+
ret = wolfSSL_OCSP_response_status(ocspResp);
838+
wolfSSL_OCSP_RESPONSE_free(ocspResp);
839+
}
840+
841+
(*env)->ReleaseByteArrayElements(env, response, (jbyte*)respBuf, JNI_ABORT);
842+
843+
return (jint)ret;
844+
845+
#else
846+
(void)env;
847+
(void)jcl;
848+
(void)response;
849+
(void)responseSz;
850+
return NOT_COMPILED_IN;
851+
#endif /* HAVE_OCSP && OPENSSL_EXTRA */
852+
}
853+
752854
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerSetVerify
753855
(JNIEnv* env, jclass jcl, jlong cmPtr, jobject callback)
754856
{

src/main/java/com/wolfssl/provider/jce/WolfCryptPKIXRevocationChecker.java

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,36 @@ else if (certIndex >= 0 && trustAnchors != null) {
406406
}
407407
}
408408

409+
/**
410+
* Get human readable name for OCSPResponseStatus value.
411+
*
412+
* Per RFC 6960, OCSPResponseStatus ::= ENUMERATED {
413+
* successful (0), malformedRequest (1), internalError (2),
414+
* tryLater (3), sigRequired (5), unauthorized (6) }
415+
*
416+
* @param status OCSPResponseStatus value
417+
* @return status name string
418+
*/
419+
private String getOcspResponseStatusName(int status) {
420+
421+
switch (status) {
422+
case 0:
423+
return "SUCCESSFUL";
424+
case 1:
425+
return "MALFORMED_REQUEST";
426+
case 2:
427+
return "INTERNAL_ERROR";
428+
case 3:
429+
return "TRY_LATER";
430+
case 5:
431+
return "SIG_REQUIRED";
432+
case 6:
433+
return "UNAUTHORIZED";
434+
default:
435+
return "UNKNOWN(" + status + ")";
436+
}
437+
}
438+
409439
/**
410440
* Check pre-loaded OCSP response.
411441
*
@@ -420,6 +450,7 @@ else if (certIndex >= 0 && trustAnchors != null) {
420450
private void checkPreloadedOcspResponse(X509Certificate cert)
421451
throws CertPathValidatorException {
422452

453+
int ocspStatus;
423454
byte[] response;
424455
byte[] certDer;
425456

@@ -435,14 +466,29 @@ private void checkPreloadedOcspResponse(X509Certificate cert)
435466
"CertManager not available for OCSP response checking");
436467
}
437468

469+
/* Check OCSP response status before native verification.
470+
* Non-successful OCSP responses (e.g. UNAUTHORIZED, TRY_LATER) should
471+
* be reported as errors per RFC 6960. Use native wolfSSL to parse the
472+
* response status from raw DER bytes. */
473+
ocspStatus = WolfSSLCertManager.getOcspResponseStatus(response,
474+
response.length);
475+
if (ocspStatus > 0) {
476+
throw new CertPathValidatorException("OCSP response error: " +
477+
getOcspResponseStatusName(ocspStatus));
478+
}
479+
else if (ocspStatus < 0) {
480+
throw new CertPathValidatorException(
481+
"Failed to parse OCSP response status: " + ocspStatus);
482+
}
483+
438484
/* Load issuer cert so OCSP response signature can be verified */
439485
loadIssuerForOcspVerification(cert);
440486

441487
try {
442488
certDer = cert.getEncoded();
443489

444-
certManager.CertManagerCheckOCSPResponse(
445-
response, response.length, certDer, certDer.length);
490+
certManager.CertManagerCheckOCSPResponse(response, response.length,
491+
certDer, certDer.length);
446492

447493
} catch (CertificateEncodingException e) {
448494
throw new CertPathValidatorException(

src/main/java/com/wolfssl/wolfcrypt/WolfSSLCertManager.java

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ static native int CertManagerLoadCRLBuffer(
8484
static native int CertManagerCheckOCSP(long cm, byte[] cert, int sz);
8585
static native int CertManagerCheckOCSPResponse(
8686
long cm, byte[] response, int responseSz, byte[] cert, int certSz);
87+
static native int OcspResponseStatus(byte[] response, int responseSz);
8788
static native int CertManagerSetVerify(long cm, Object callback);
8889
static native int CertManagerClearVerify(long cm);
8990

@@ -188,8 +189,9 @@ public synchronized void CertManagerLoadCA(X509Certificate cert)
188189
synchronized (cmLock) {
189190
try {
190191
/* Throws WolfCryptException on native error */
191-
CertManagerLoadCABuffer(cert.getEncoded(),
192-
cert.getEncoded().length, WolfCrypt.SSL_FILETYPE_ASN1);
192+
byte[] encoded = cert.getEncoded();
193+
CertManagerLoadCABuffer(encoded, encoded.length,
194+
WolfCrypt.SSL_FILETYPE_ASN1);
193195
} catch (CertificateEncodingException e) {
194196
throw new WolfCryptException(e);
195197
}
@@ -247,9 +249,9 @@ public synchronized void CertManagerLoadCA(X509Certificate cert, int flags)
247249

248250
synchronized (cmLock) {
249251
try {
250-
CertManagerLoadCABufferEx(cert.getEncoded(),
251-
cert.getEncoded().length, WolfCrypt.SSL_FILETYPE_ASN1,
252-
flags);
252+
byte[] encoded = cert.getEncoded();
253+
CertManagerLoadCABufferEx(encoded, encoded.length,
254+
WolfCrypt.SSL_FILETYPE_ASN1, flags);
253255
} catch (CertificateEncodingException e) {
254256
throw new WolfCryptException(e);
255257
}
@@ -292,8 +294,8 @@ public synchronized void CertManagerLoadCAKeyStore(KeyStore ks)
292294

293295
if (cert != null && cert.getBasicConstraints() >= 0) {
294296
/* Will throw WolfCryptException on error */
295-
CertManagerLoadCABuffer(cert.getEncoded(),
296-
cert.getEncoded().length,
297+
byte[] encoded = cert.getEncoded();
298+
CertManagerLoadCABuffer(encoded, encoded.length,
297299
WolfCrypt.SSL_FILETYPE_ASN1);
298300
loadedCerts++;
299301
}
@@ -383,8 +385,9 @@ public synchronized void CertManagerVerify(
383385
synchronized (cmLock) {
384386
try {
385387
/* Throws WolfCryptException on native error */
386-
CertManagerVerifyBuffer(cert.getEncoded(),
387-
cert.getEncoded().length, WolfCrypt.SSL_FILETYPE_ASN1);
388+
byte[] encoded = cert.getEncoded();
389+
CertManagerVerifyBuffer(encoded, encoded.length,
390+
WolfCrypt.SSL_FILETYPE_ASN1);
388391
} catch (CertificateEncodingException e) {
389392
throw new WolfCryptException(e);
390393
}
@@ -691,6 +694,38 @@ public synchronized void CertManagerCheckOCSPResponse(
691694
}
692695
}
693696

697+
/**
698+
* Get OCSPResponseStatus from raw OCSP response bytes.
699+
*
700+
* Uses native wolfSSL_d2i_OCSP_RESPONSE() to parse the response and
701+
* wolfSSL_OCSP_response_status() to extract the status value per RFC 6960.
702+
*
703+
* This is a static method that does not require a WolfSSLCertManager
704+
* instance.
705+
*
706+
* @param response raw OCSP response bytes (DER encoded)
707+
* @param responseSz size of OCSP response
708+
*
709+
* @return OCSPResponseStatus value:
710+
* 0 = successful,
711+
* 1 = malformedRequest,
712+
* 2 = internalError,
713+
* 3 = tryLater,
714+
* 5 = sigRequired,
715+
* 6 = unauthorized,
716+
* negative on error (NOT_COMPILED_IN, BAD_FUNC_ARG,
717+
* MEMORY_E, or -1 if response parsing failed)
718+
*/
719+
public static int getOcspResponseStatus(byte[] response, int responseSz) {
720+
721+
if (response == null || responseSz < 0 ||
722+
responseSz > response.length) {
723+
return WolfCryptError.BAD_FUNC_ARG.getCode();
724+
}
725+
726+
return OcspResponseStatus(response, responseSz);
727+
}
728+
694729
/**
695730
* Set verification callback for this CertManager.
696731
*

0 commit comments

Comments
 (0)