diff --git a/native/com_wolfssl_WolfSSL.h b/native/com_wolfssl_WolfSSL.h index c6d6741f..27a33d67 100644 --- a/native/com_wolfssl_WolfSSL.h +++ b/native/com_wolfssl_WolfSSL.h @@ -187,6 +187,8 @@ extern "C" { #define com_wolfssl_WolfSSL_CACHE_MATCH_ERROR -280L #undef com_wolfssl_WolfSSL_MAX_RECORD_SIZE #define com_wolfssl_WolfSSL_MAX_RECORD_SIZE 16384L +#undef com_wolfssl_WolfSSL_EXTERNAL_SERIAL_SIZE +#define com_wolfssl_WolfSSL_EXTERNAL_SERIAL_SIZE 32L #undef com_wolfssl_WolfSSL_TLS_RECORD_HEADER_LEN #define com_wolfssl_WolfSSL_TLS_RECORD_HEADER_LEN 5L #undef com_wolfssl_WolfSSL_TLS_RECORD_LEN_HI_OFF diff --git a/native/com_wolfssl_WolfSSLCRL.c b/native/com_wolfssl_WolfSSLCRL.c index f5780225..986ed5e5 100644 --- a/native/com_wolfssl_WolfSSLCRL.c +++ b/native/com_wolfssl_WolfSSLCRL.c @@ -44,6 +44,13 @@ #define WOLFSSL_JNI_CRL_GEN_ENABLED #endif +/* WOLFSSL_X509_REVOKED.revocationDate was added in 5.9.0 */ +#if defined(WOLFSSL_JNI_CRL_GEN_ENABLED) && \ + ((LIBWOLFSSL_VERSION_HEX > 0x05008004) || \ + defined(WOLFSSL_PR9839_PATCH_APPLIED)) +#define WOLFSSL_JNI_CRL_REVDATE_ENABLED +#endif + JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSLCRL_X509_1CRL_1new (JNIEnv* jenv, jclass jcl) { @@ -260,15 +267,16 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCRL_X509_1CRL_1add_1revoked WOLFSSL_X509_CRL* crl = (WOLFSSL_X509_CRL*)(uintptr_t)crlPtr; WOLFSSL_X509_REVOKED revoked; WOLFSSL_ASN1_INTEGER* serialInt = NULL; +#ifdef WOLFSSL_JNI_CRL_REVDATE_ENABLED + WOLFSSL_ASN1_TIME revTime; + byte* revDateBuf = NULL; + int revDateSz = 0; +#endif byte* serialBuf = NULL; int serialSz = 0; int ret = WOLFSSL_SUCCESS; (void)jcl; - /* Note: date is not currently used until WOLFSSL_X509_REVOKED adds it. */ - (void)revDate; - (void)dateFmt; - if (jenv == NULL || crl == NULL || serial == NULL) { return WOLFSSL_FAILURE; } @@ -291,10 +299,46 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCRL_X509_1CRL_1add_1revoked serialInt->isDynamic = 0; serialInt->type = 0; revoked.serialNumber = serialInt; - ret = wolfSSL_X509_CRL_add_revoked(crl, &revoked); + + #ifdef WOLFSSL_JNI_CRL_REVDATE_ENABLED + /* Set caller-supplied revocation date if present, otherwise + * wolfSSL fills in the current time. revDate holds the ASN.1 + * time value bytes, dateFmt is ASN_UTC_TIME or + * ASN_GENERALIZED_TIME tag. */ + if (revDate != NULL) { + revDateBuf = (byte*)(*jenv)->GetByteArrayElements(jenv, + revDate, NULL); + revDateSz = (*jenv)->GetArrayLength(jenv, revDate); + if (revDateBuf == NULL || revDateSz <= 0 || + (size_t)revDateSz > sizeof(revTime.data)) { + ret = WOLFSSL_FAILURE; + } + else { + XMEMSET(&revTime, 0, sizeof(revTime)); + XMEMCPY(revTime.data, revDateBuf, (size_t)revDateSz); + revTime.length = revDateSz; + revTime.type = (int)dateFmt; + revoked.revocationDate = &revTime; + } + } + #else + /* No revocationDate support, wolfSSL records the current time. */ + (void)revDate; + (void)dateFmt; + #endif + + if (ret == WOLFSSL_SUCCESS) { + ret = wolfSSL_X509_CRL_add_revoked(crl, &revoked); + } } } +#ifdef WOLFSSL_JNI_CRL_REVDATE_ENABLED + if (revDateBuf != NULL) { + (*jenv)->ReleaseByteArrayElements(jenv, revDate, + (jbyte*)revDateBuf, JNI_ABORT); + } +#endif if (serialBuf != NULL) { (*jenv)->ReleaseByteArrayElements(jenv, serial, (jbyte*)serialBuf, JNI_ABORT); @@ -325,13 +369,20 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCRL_X509_1CRL_1add_1revoked_1cert int certSz = 0; int ret = WOLFSSL_SUCCESS; (void)jcl; - (void)revDate; (void)dateFmt; if (jenv == NULL || crl == NULL || certDer == NULL) { return WOLFSSL_FAILURE; } + /* Reject non-null date rather than ignoring it, since + * wolfSSL_X509_CRL_add_revoked_cert() always records the current time. + * Caller should use addRevoked() with an explicit serial number to set a + * specific revocation date. */ + if (revDate != NULL) { + return BAD_FUNC_ARG; + } + certBuf = (byte*)(*jenv)->GetByteArrayElements(jenv, certDer, NULL); certSz = (*jenv)->GetArrayLength(jenv, certDer); if (certBuf == NULL || certSz == 0) { diff --git a/native/com_wolfssl_WolfSSLContext.c b/native/com_wolfssl_WolfSSLContext.c index b0a3faf4..75f5f7f7 100644 --- a/native/com_wolfssl_WolfSSLContext.c +++ b/native/com_wolfssl_WolfSSLContext.c @@ -804,7 +804,7 @@ int NativeVerifyCallback(int preverify_ok, WOLFSSL_X509_STORE_CTX* store) { JNIEnv* jenv; jint vmret = 0; - jint retval = -1; + jint retval = 0; int needsDetach = 0; jclass excClass = NULL; jclass verifyClass = NULL; @@ -828,11 +828,11 @@ int NativeVerifyCallback(int preverify_ok, WOLFSSL_X509_STORE_CTX* store) vmret = (*g_vm)->AttachCurrentThread(g_vm, (void**) &jenv, NULL); #endif if (vmret) { - return -101; /* failed to attach JNIEnv to thread */ + return 0; /* failed to attach JNIEnv to thread */ } needsDetach = 1; } else if (vmret != JNI_OK) { - return -102; /* unable to get JNIEnv from JavaVM */ + return 0; /* unable to get JNIEnv from JavaVM */ } /* find exception class */ @@ -843,7 +843,7 @@ int NativeVerifyCallback(int preverify_ok, WOLFSSL_X509_STORE_CTX* store) freeNativeVerifyCbLocalRefs(jenv, excClass, verifyClass, verifyCbObj); if (needsDetach) (*g_vm)->DetachCurrentThread(g_vm); - return -103; + return 0; } /* Locate the per-context verify callback jobject via @@ -896,7 +896,7 @@ int NativeVerifyCallback(int preverify_ok, WOLFSSL_X509_STORE_CTX* store) verifyCbObj); if (needsDetach) (*g_vm)->DetachCurrentThread(g_vm); - return -104; + return 0; } verifyMethod = (*jenv)->GetMethodID(jenv, verifyClass, @@ -913,7 +913,7 @@ int NativeVerifyCallback(int preverify_ok, WOLFSSL_X509_STORE_CTX* store) verifyCbObj); if (needsDetach) (*g_vm)->DetachCurrentThread(g_vm); - return -105; + return 0; } retval = (*jenv)->CallIntMethod(jenv, verifyCbObj, @@ -927,7 +927,7 @@ int NativeVerifyCallback(int preverify_ok, WOLFSSL_X509_STORE_CTX* store) verifyCbObj); if (needsDetach) (*g_vm)->DetachCurrentThread(g_vm); - return -106; + return 0; } } else { @@ -941,14 +941,19 @@ int NativeVerifyCallback(int preverify_ok, WOLFSSL_X509_STORE_CTX* store) freeNativeVerifyCbLocalRefs(jenv, excClass, verifyClass, verifyCbObj); if (needsDetach) (*g_vm)->DetachCurrentThread(g_vm); - return -1; + return 0; } freeNativeVerifyCbLocalRefs(jenv, excClass, verifyClass, verifyCbObj); if (needsDetach) (*g_vm)->DetachCurrentThread(g_vm); - return retval; + /* Accept only on an explicit callback success (1), reject otherwise. */ + if (retval == 1) { + return 1; + } + + return 0; } JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSLContext_setOptions diff --git a/native/com_wolfssl_WolfSSLSession.c b/native/com_wolfssl_WolfSSLSession.c index d17383c3..322723ef 100644 --- a/native/com_wolfssl_WolfSSLSession.c +++ b/native/com_wolfssl_WolfSSLSession.c @@ -107,7 +107,7 @@ int NativeSSLVerifyCallback(int preverify_ok, WOLFSSL_X509_STORE_CTX* store) { JNIEnv* jenv; jint vmret = 0; - jint retval = -1; + jint retval = 0; int needsDetach = 0; jobjectRefType refcheck; SSLAppData* appData; /* WOLFSSL app data, stored verify cb obj */ @@ -128,11 +128,11 @@ int NativeSSLVerifyCallback(int preverify_ok, WOLFSSL_X509_STORE_CTX* store) vmret = (*g_vm)->AttachCurrentThread(g_vm, (void**) &jenv, NULL); #endif if (vmret) { - return -101; /* failed to attach JNIEnv to thread */ + return 0; /* failed to attach JNIEnv to thread */ } needsDetach = 1; } else if (vmret != JNI_OK) { - return -102; /* unable to get JNIEnv from JavaVM */ + return 0; /* unable to get JNIEnv from JavaVM */ } /* get app data to retrieve stored Java jobject callback object */ @@ -143,7 +143,7 @@ int NativeSSLVerifyCallback(int preverify_ok, WOLFSSL_X509_STORE_CTX* store) if (needsDetach) { (*g_vm)->DetachCurrentThread(g_vm); } - return -105; + return 0; } /* Promote stored global callback ref to a local ref under g_verifyCbMutex @@ -161,7 +161,7 @@ int NativeSSLVerifyCallback(int preverify_ok, WOLFSSL_X509_STORE_CTX* store) if (needsDetach) { (*g_vm)->DetachCurrentThread(g_vm); } - return -106; + return 0; } /* valid ref check: non-zero type covers local/global/weak, and verifyCbObj @@ -180,7 +180,7 @@ int NativeSSLVerifyCallback(int preverify_ok, WOLFSSL_X509_STORE_CTX* store) if (needsDetach) { (*g_vm)->DetachCurrentThread(g_vm); } - return -107; + return 0; } retval = (*jenv)->CallIntMethod(jenv, verifyCbObj, @@ -194,7 +194,7 @@ int NativeSSLVerifyCallback(int preverify_ok, WOLFSSL_X509_STORE_CTX* store) (*jenv)->DeleteLocalRef(jenv, verifyCbObj); if (needsDetach) (*g_vm)->DetachCurrentThread(g_vm); - return -109; + return 0; } } else { @@ -208,7 +208,7 @@ int NativeSSLVerifyCallback(int preverify_ok, WOLFSSL_X509_STORE_CTX* store) (*jenv)->DeleteLocalRef(jenv, verifyCbObj); if (needsDetach) (*g_vm)->DetachCurrentThread(g_vm); - return -1; + return 0; } (*jenv)->DeleteLocalRef(jenv, verifyCbObj); @@ -216,7 +216,12 @@ int NativeSSLVerifyCallback(int preverify_ok, WOLFSSL_X509_STORE_CTX* store) if (needsDetach) (*g_vm)->DetachCurrentThread(g_vm); - return retval; + /* Accept only on an explicit callback success (1), reject otherwise. */ + if (retval == 1) { + return 1; + } + + return 0; } #ifndef USE_WINDOWS_API @@ -4957,13 +4962,19 @@ JNIEXPORT jstring JNICALL Java_com_wolfssl_WolfSSLSession_getPskIdentityHint { #ifndef NO_PSK WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr; + const char* hint = NULL; (void)obj; if (jenv == NULL || ssl == NULL) { return NULL; } - return (*jenv)->NewStringUTF(jenv, wolfSSL_get_psk_identity_hint(ssl)); + hint = wolfSSL_get_psk_identity_hint(ssl); + if (hint == NULL) { + return NULL; + } + + return (*jenv)->NewStringUTF(jenv, hint); #else (void)jenv; (void)obj; @@ -4977,13 +4988,19 @@ JNIEXPORT jstring JNICALL Java_com_wolfssl_WolfSSLSession_getPskIdentity { #ifndef NO_PSK WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr; + const char* identity = NULL; (void)obj; if (jenv == NULL || ssl == NULL) { return NULL; } - return (*jenv)->NewStringUTF(jenv, wolfSSL_get_psk_identity(ssl)); + identity = wolfSSL_get_psk_identity(ssl); + if (identity == NULL) { + return NULL; + } + + return (*jenv)->NewStringUTF(jenv, identity); #else (void)jenv; (void)obj; diff --git a/src/java/com/wolfssl/WolfSSL.java b/src/java/com/wolfssl/WolfSSL.java index 050230f3..4e8e126a 100644 --- a/src/java/com/wolfssl/WolfSSL.java +++ b/src/java/com/wolfssl/WolfSSL.java @@ -357,6 +357,10 @@ public enum TLS_VERSION { /** Maximum SSL record size (16KB) as defined by the protocol. */ public static final int MAX_RECORD_SIZE = 16384; + /** Maximum X.509 serial number size in bytes. Matches + * EXTERNAL_SERIAL_SIZE in wolfssl/wolfcrypt/asn.h. */ + public static final int EXTERNAL_SERIAL_SIZE = 32; + /** TLS record header is: type(1) + version(2) + length(2) */ public static final int TLS_RECORD_HEADER_LEN = 5; /** TLS record header length high byte offset */ diff --git a/src/java/com/wolfssl/WolfSSLCRL.java b/src/java/com/wolfssl/WolfSSLCRL.java index 66f40739..a3c19401 100644 --- a/src/java/com/wolfssl/WolfSSLCRL.java +++ b/src/java/com/wolfssl/WolfSSLCRL.java @@ -418,7 +418,7 @@ public int addRevoked(byte[] serialNumber, Date revocationDate) { int dateFmt = 0; if (revocationDate != null) { Asn1TimeData timeData = buildAsn1TimeData(revocationDate); - revDateBytes = timeData.paddedData; + revDateBytes = Arrays.copyOf(timeData.paddedData, timeData.length); dateFmt = timeData.type; } @@ -436,14 +436,20 @@ public int addRevoked(byte[] serialNumber, Date revocationDate) { * Add revoked certificate entry to CRL from DER-encoded certificate. * * @param certDer DER-encoded certificate to add as revoked - * @param revocationDate Date when certificate was revoked, or null + * @param revocationDate Date when certificate was revoked, or null to + * record the current time * * @return native wolfSSL return code * - * @throws IllegalStateException if WolfSSLCRL has been freed. - * @throws IllegalArgumentException if certDer is null or empty. + * @throws IllegalStateException if WolfSSLCRL has been freed, or if + * the certificate serial number cannot be read. + * @throws IllegalArgumentException if certDer is null or empty, or if + * revocationDate is non-null and certDer cannot be parsed. */ public int addRevokedCert(byte[] certDer, Date revocationDate) { + + WolfSSLCertificate cert = null; + if (certDer == null || certDer.length == 0) { throw new IllegalArgumentException( "Certificate DER is null or empty"); @@ -451,21 +457,27 @@ public int addRevokedCert(byte[] certDer, Date revocationDate) { confirmObjectIsActive(); - byte[] revDateBytes = null; - int dateFmt = 0; if (revocationDate != null) { - Asn1TimeData timeData = buildAsn1TimeData(revocationDate); - revDateBytes = timeData.paddedData; - dateFmt = timeData.type; + try { + cert = new WolfSSLCertificate(certDer); + return addRevokedBySerial(cert, revocationDate); + } + catch (WolfSSLException ex) { + throw new IllegalArgumentException( + "Failed to parse certificate DER", ex); + } + finally { + if (cert != null) { + cert.free(); + } + } } synchronized (crlLock) { WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI, WolfSSLDebug.INFO, this.crlPtr, - () -> "entered addRevokedCert(der: " + certDer.length + - ", revocationDate: " + revocationDate + ")"); - return X509_CRL_add_revoked_cert(this.crlPtr, certDer, - revDateBytes, dateFmt); + () -> "entered addRevokedCert(der: " + certDer.length + ")"); + return X509_CRL_add_revoked_cert(this.crlPtr, certDer, null, 0); } } @@ -473,15 +485,18 @@ public int addRevokedCert(byte[] certDer, Date revocationDate) { * Add revoked certificate entry to CRL from WolfSSLCertificate object. * * @param cert WolfSSLCertificate object to add as revoked - * @param revocationDate Date when certificate was revoked, or null + * @param revocationDate Date when certificate was revoked, or null to + * record the current time * * @return native wolfSSL return code * - * @throws IllegalStateException if WolfSSLCRL has been freed or if - * certificate DER encoding fails. + * @throws IllegalStateException if WolfSSLCRL has been freed, if + * certificate DER encoding fails, or if the certificate serial + * number cannot be read. * @throws IllegalArgumentException if cert is null. */ public int addRevokedCert(WolfSSLCertificate cert, Date revocationDate) { + if (cert == null) { throw new IllegalArgumentException("Certificate is null"); } @@ -495,6 +510,10 @@ public int addRevokedCert(WolfSSLCertificate cert, Date revocationDate) { revocationDate + ")"); } + if (revocationDate != null) { + return addRevokedBySerial(cert, revocationDate); + } + byte[] certDer = null; try { certDer = cert.getDer(); @@ -508,7 +527,22 @@ public int addRevokedCert(WolfSSLCertificate cert, Date revocationDate) { throw new IllegalStateException("Certificate DER is empty"); } - return addRevokedCert(certDer, revocationDate); + return addRevokedCert(certDer, null); + } + + /** + * Revoke by a certificate's serial number so an explicit date is honored. + * Backs the addRevokedCert() date paths. */ + private int addRevokedBySerial(WolfSSLCertificate cert, + Date revocationDate) { + + byte[] serial = cert.getSerialBytes(); + if (serial == null || serial.length == 0) { + throw new IllegalStateException( + "Failed to read certificate serial number"); + } + + return addRevoked(serial, revocationDate); } /** diff --git a/src/java/com/wolfssl/WolfSSLCertificate.java b/src/java/com/wolfssl/WolfSSLCertificate.java index da2d9863..53186961 100644 --- a/src/java/com/wolfssl/WolfSSLCertificate.java +++ b/src/java/com/wolfssl/WolfSSLCertificate.java @@ -1577,7 +1577,7 @@ public byte[] getTbs() throws IllegalStateException { */ public BigInteger getSerial() throws IllegalStateException { - byte[] out = new byte[32]; + byte[] out = new byte[WolfSSL.EXTERNAL_SERIAL_SIZE]; int sz; confirmObjectIsActive(); @@ -1597,6 +1597,35 @@ public BigInteger getSerial() throws IllegalStateException { } } + /** + * Return an X.509 serial number as the raw bytes. + * + * @return serial number bytes, or null if not available + * + * @throws IllegalStateException if WolfSSLCertificate has been freed. + */ + byte[] getSerialBytes() throws IllegalStateException { + + int sz; + byte[] out = new byte[WolfSSL.EXTERNAL_SERIAL_SIZE]; + + confirmObjectIsActive(); + + synchronized (x509Lock) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI, + WolfSSLDebug.INFO, this.x509Ptr, + () -> "entering getSerialBytes()"); + + sz = X509_get_serial_number(this.x509Ptr, out); + } + + if (sz <= 0) { + return null; + } + + return Arrays.copyOf(out, sz); + } + /** * Get X.509 validity notBefore date * diff --git a/src/java/com/wolfssl/WolfSSLVerifyCallback.java b/src/java/com/wolfssl/WolfSSLVerifyCallback.java index fd247b27..2c00f7e5 100644 --- a/src/java/com/wolfssl/WolfSSLVerifyCallback.java +++ b/src/java/com/wolfssl/WolfSSLVerifyCallback.java @@ -27,7 +27,7 @@ * callback class to be used by wolfSSL during the handshake process. *
* After implementing this interface, it should be passed as a parameter
- * to the {@link WolfSSLContext#setVerify(long, int, WolfSSLVerifyCallback)
+ * to the {@link WolfSSLContext#setVerify(int, WolfSSLVerifyCallback)
* WolfSSLContext.setVerify()} method to be registered with the native wolfSSL
* library.
*
@@ -45,10 +45,9 @@ public interface WolfSSLVerifyCallback {
* already passed. 0 if failed, 1 if passed.
* @param x509StorePtr pointer to the context used for certificate
* chain verification.
- * @return 0 if the verification process should
- * stop immediately with an error. 1 if
- * the verification process should continue with the
- * rest of the handshake.
+ * @return 1 to accept and continue the handshake.
+ * Any other return value or exception thrown is
+ * treated as verification failure and aborts handshake.
*/
public int verifyCallback(int preverify_ok, long x509StorePtr);
diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLX509.java b/src/java/com/wolfssl/provider/jsse/WolfSSLX509.java
index c375af7c..52d62d1d 100644
--- a/src/java/com/wolfssl/provider/jsse/WolfSSLX509.java
+++ b/src/java/com/wolfssl/provider/jsse/WolfSSLX509.java
@@ -698,7 +698,7 @@ private static class WolfSSLPrincipal implements Principal {
private String[] DNs = { "/emailAddress=", "/CN=", "/OU=",
"/O=", "/L=", "/ST=", "/C="};
- /* replace the wolfSSL version of the tag. Returns replacement
+ /* Replace the wolfSSL version of the tag. Returns replacement
* on success. */
private String getReplace(String in) {
if (in.equals("/emailAddress=")) {
@@ -725,53 +725,51 @@ private String getReplace(String in) {
return null;
}
- /* check if the string starts with an expected tag.
- * returns index into DNs of tag when found */
- private int containsDN(String in) {
- int i;
- for (i = 0; i < DNs.length; i++) {
- if (in.startsWith(DNs[i]))
- return i;
+ /* Map "TAG=value" component to its JSSE spelling, leaving
+ * unrecognized tags unchanged. */
+ private String mapComponent(String component) {
+ for (int i = 0; i < DNs.length; i++) {
+ /* DNs entries carry a leading '/', drop it to compare */
+ String tag = DNs[i].substring(1);
+ if (component.startsWith(tag)) {
+ String replace = getReplace(DNs[i]);
+ if (replace != null) {
+ return replace.concat(
+ component.substring(tag.length()));
+ }
+ }
}
- return -1;
+ return component;
}
- /* convert name from having "/DN=" format to "DN= ," format
+ /* Convert name from having "/DN=" format to "DN= ," format
* returns the new reformatted string on success */
private String reformatList(String in) {
- String[] ret;
- int i, j;
- String tmp = in;
- ArrayList