From 1efe944fa75cb65dff76fac8536cc3584e52d363 Mon Sep 17 00:00:00 2001 From: Paul Adelsbach Date: Wed, 18 Feb 2026 11:23:34 -0800 Subject: [PATCH] Add cert/CRL capabilities: skid, akid, dist point, netscape --- examples/certs/test/crl-dp-cert.pem | 21 ++ examples/certs/update-certs.sh | 32 ++- native/com_wolfssl_WolfSSL.c | 52 ++++ native/com_wolfssl_WolfSSL.h | 46 +++ native/com_wolfssl_WolfSSLCRL.c | 10 +- native/com_wolfssl_WolfSSLCertificate.c | 259 +++++++++++++++++ native/com_wolfssl_WolfSSLCertificate.h | 56 ++++ src/java/com/wolfssl/WolfSSL.java | 32 +++ src/java/com/wolfssl/WolfSSLCertificate.java | 272 ++++++++++++++++++ .../wolfssl/test/WolfSSLCertificateTest.java | 114 ++++++++ 10 files changed, 890 insertions(+), 4 deletions(-) create mode 100644 examples/certs/test/crl-dp-cert.pem diff --git a/examples/certs/test/crl-dp-cert.pem b/examples/certs/test/crl-dp-cert.pem new file mode 100644 index 000000000..979060c06 --- /dev/null +++ b/examples/certs/test/crl-dp-cert.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIUZqjaWzuAIDjJjqQW/x9Lyn5H2McwDQYJKoZIhvcNAQEL +BQAwOjEUMBIGA1UEAwwLVGVzdCBDUkwgRFAxFTATBgNVBAoMDHdvbGZTU0wgVGVz +dDELMAkGA1UEBhMCVVMwHhcNMjYwMjA5MTgxMTQzWhcNMjcwMjA5MTgxMTQzWjA6 +MRQwEgYDVQQDDAtUZXN0IENSTCBEUDEVMBMGA1UECgwMd29sZlNTTCBUZXN0MQsw +CQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALP/1lo5 +T10/LJAck3ImKvrinzS1oubA/YP/w2NTJLzlZQtbvNPW4WhY2LcuUWOSv/VmMSpq +J/mEqEn8P9CfIgtRo0z39+HJJ3aE3ClioH6fTpj284nHZnJdYQFy/9+T4DTLcuiJ +VILqRotqH06JRU4mhR2hqiw7YHI76BlPJAB9pVwGbit6BKWbF5vJRy440AYNCWjs +t/NEhrKnCJugaPqvyhH9ByWI8/wPeyFNXUpuEiZVg+rSYwPr0w4kVBRUVWnDxEam +WKEEPSM1CdY2LJGDT6Qjm6WyVQbWppu1mz6Dg+nvw+h125PyW4Cyim6HAFj3IJcI +6YcDC2lGep7PNmECAwEAAaNrMGkwCQYDVR0TBAIwADALBgNVHQ8EBAMCB4AwMAYD +VR0fBCkwJzAloCOgIYYfaHR0cDovL2NybC5leGFtcGxlLmNvbS90ZXN0LmNybDAd +BgNVHQ4EFgQUXEABbBfseiUjqacQWYMRluxQV+kwDQYJKoZIhvcNAQELBQADggEB +AF21pa2SQXeqmDtYLvhwNWpwpt814nRfejAzlLBLpJB8nf1NE89a53U7ELbZMPNj +tQC/ADNoNGFQmSaPNytXtHNslPM17kSWN+6/JFhKGcWHXgPPM4E5VOZ94H1BK4fh +PMCfMMh+826Y+RK/nsi4NnlmeJy5/QdRgbDfGY4ZZECssHSIbKPP7pgxH/YzDUd/ +HIzf5vXeiUG7PXXJhzA38k1HRhuyxOYnsrLMYw/FsDOl/knhH9dF8f+XFVHuFfQv +GH9cm+btX0gM1EaBi1huQcYYNRp2BSa2qSjIeDRg5Bs4i5BENh7wVtZDheGD0SpE +3jhznnX5L4CwmLzlfQkARuU= +-----END CERTIFICATE----- diff --git a/examples/certs/update-certs.sh b/examples/certs/update-certs.sh index bfde2b003..53d3b3320 100755 --- a/examples/certs/update-certs.sh +++ b/examples/certs/update-certs.sh @@ -98,6 +98,37 @@ if [ $? -ne 0 ]; then fi printf "Generated ca-keyPkcs8.der\n" +# Generate CRL Distribution Points test cert +printf "Generating test/crl-dp-cert.pem\n" +mkdir -p test +TMP_DIR="$(mktemp -d)" +cat > "${TMP_DIR}/openssl.cnf" </dev/null 2>&1 +if [ $? -ne 0 ]; then + printf "Failed to generate test/crl-dp-cert.pem\n" + rm -rf "${TMP_DIR}" + exit 1 +fi +rm -rf "${TMP_DIR}" + # Remove text info from intermediate certs, causes issues on Android (WRONG TAG) printf "Removing text info from intermediate certs\n" sed -i.bak -n '/-----BEGIN CERTIFICATE-----/,$p' ca-cert.pem @@ -131,4 +162,3 @@ else fi printf "\nFinished successfully\n" - diff --git a/native/com_wolfssl_WolfSSL.c b/native/com_wolfssl_WolfSSL.c index 727dd671f..a272de51b 100644 --- a/native/com_wolfssl_WolfSSL.c +++ b/native/com_wolfssl_WolfSSL.c @@ -545,6 +545,58 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getNID_1dnQualifier return NID_dnQualifier; } +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getNID_1subject_1key_1identifier + (JNIEnv* jenv, jclass jcl) +{ + (void)jenv; + (void)jcl; + +#ifdef WOLFSSL_CERT_EXT + return NID_subject_key_identifier; +#else + return 0; +#endif +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getNID_1authority_1key_1identifier + (JNIEnv* jenv, jclass jcl) +{ + (void)jenv; + (void)jcl; + +#ifdef WOLFSSL_CERT_EXT + return NID_authority_key_identifier; +#else + return 0; +#endif +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getNID_1crl_1distribution_1points + (JNIEnv* jenv, jclass jcl) +{ + (void)jenv; + (void)jcl; + +#ifdef WOLFSSL_CERT_EXT + return NID_crl_distribution_points; +#else + return 0; +#endif +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getNID_1netscape_1cert_1type + (JNIEnv* jenv, jclass jcl) +{ + (void)jenv; + (void)jcl; + +#ifndef IGNORE_NETSCAPE_CERT_TYPE + return NID_netscape_cert_type; +#else + return 0; +#endif +} + /* functions to return BulkCipherAlgorithm enum values from ./wolfssl/ssl.h */ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getBulkCipherAlgorithmEnumNULL (JNIEnv* jenv, jclass jcl) diff --git a/native/com_wolfssl_WolfSSL.h b/native/com_wolfssl_WolfSSL.h index dedf9b0cb..58c97725b 100644 --- a/native/com_wolfssl_WolfSSL.h +++ b/native/com_wolfssl_WolfSSL.h @@ -221,6 +221,20 @@ extern "C" { #define com_wolfssl_WolfSSL_ASN_URI_TYPE 6L #undef com_wolfssl_WolfSSL_ASN_IP_TYPE #define com_wolfssl_WolfSSL_ASN_IP_TYPE 7L +#undef com_wolfssl_WolfSSL_NS_CERT_TYPE_SSL_CLIENT +#define com_wolfssl_WolfSSL_NS_CERT_TYPE_SSL_CLIENT 128L +#undef com_wolfssl_WolfSSL_NS_CERT_TYPE_SSL_SERVER +#define com_wolfssl_WolfSSL_NS_CERT_TYPE_SSL_SERVER 64L +#undef com_wolfssl_WolfSSL_NS_CERT_TYPE_EMAIL +#define com_wolfssl_WolfSSL_NS_CERT_TYPE_EMAIL 32L +#undef com_wolfssl_WolfSSL_NS_CERT_TYPE_OBJECT_SIGNING +#define com_wolfssl_WolfSSL_NS_CERT_TYPE_OBJECT_SIGNING 16L +#undef com_wolfssl_WolfSSL_NS_CERT_TYPE_SSL_CA +#define com_wolfssl_WolfSSL_NS_CERT_TYPE_SSL_CA 4L +#undef com_wolfssl_WolfSSL_NS_CERT_TYPE_EMAIL_CA +#define com_wolfssl_WolfSSL_NS_CERT_TYPE_EMAIL_CA 2L +#undef com_wolfssl_WolfSSL_NS_CERT_TYPE_OBJECT_CA +#define com_wolfssl_WolfSSL_NS_CERT_TYPE_OBJECT_CA 1L #undef com_wolfssl_WolfSSL_WOLFSSL_NAMED_GROUP_INVALID #define com_wolfssl_WolfSSL_WOLFSSL_NAMED_GROUP_INVALID 0L #undef com_wolfssl_WolfSSL_WOLFSSL_ECC_SECT163K1 @@ -409,6 +423,38 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getNID_1ext_1key_1usage JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getNID_1dnQualifier (JNIEnv *, jclass); +/* + * Class: com_wolfssl_WolfSSL + * Method: getNID_subject_key_identifier + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getNID_1subject_1key_1identifier + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_WolfSSL + * Method: getNID_authority_key_identifier + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getNID_1authority_1key_1identifier + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_WolfSSL + * Method: getNID_crl_distribution_points + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getNID_1crl_1distribution_1points + (JNIEnv *, jclass); + +/* + * Class: com_wolfssl_WolfSSL + * Method: getNID_netscape_cert_type + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getNID_1netscape_1cert_1type + (JNIEnv *, jclass); + /* * Class: com_wolfssl_WolfSSL * Method: getBulkCipherAlgorithmEnumNULL diff --git a/native/com_wolfssl_WolfSSLCRL.c b/native/com_wolfssl_WolfSSLCRL.c index e444230ec..670fad487 100644 --- a/native/com_wolfssl_WolfSSLCRL.c +++ b/native/com_wolfssl_WolfSSLCRL.c @@ -156,7 +156,7 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCRL_X509_1CRL_1set_1lastUpdate } else { /* Extract length from bytes 32-35 (assuming native byte order) */ - timeLen = *((int*)(timeBuf + CTC_DATE_SIZE)); + XMEMCPY(&timeLen, timeBuf + CTC_DATE_SIZE, sizeof(timeLen)); if (timeLen <= 0 || timeLen > CTC_DATE_SIZE) { ret = 0; } @@ -212,7 +212,7 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCRL_X509_1CRL_1set_1nextUpdate } else { /* Extract length from bytes 32-35 (assuming native byte order) */ - timeLen = *((int*)(timeBuf + CTC_DATE_SIZE)); + XMEMCPY(&timeLen, timeBuf + CTC_DATE_SIZE, sizeof(timeLen)); if (timeLen <= 0 || timeLen > CTC_DATE_SIZE) { ret = 0; } @@ -254,6 +254,8 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCRL_X509_1CRL_1add_1revoked 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; @@ -284,7 +286,9 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCRL_X509_1CRL_1add_1revoked (*jenv)->ReleaseByteArrayElements(jenv, serial, (jbyte*)serialBuf, JNI_ABORT); - wolfSSL_ASN1_INTEGER_free(serialInt); + if (serialInt != NULL) { + wolfSSL_ASN1_INTEGER_free(serialInt); + } return ret; #else diff --git a/native/com_wolfssl_WolfSSLCertificate.c b/native/com_wolfssl_WolfSSLCertificate.c index 2188ed1e9..5f846ec7a 100644 --- a/native/com_wolfssl_WolfSSLCertificate.c +++ b/native/com_wolfssl_WolfSSLCertificate.c @@ -570,6 +570,265 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1set_1serialNumb #endif } +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1set_1subject_1key_1id + (JNIEnv* jenv, jclass jcl, jlong x509Ptr, jbyteArray skid) +{ + /* Subject/Authority Key ID support added after wolfSSL 5.8.4 in PR 9713. + * Version check must be greater than 5.8.4 or patch from PR 9713 must be + * applied and WOLFSSL_PR9713_PATCH_APPLIED defined when compiling this + * wrapper. */ +#if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS) && \ + defined(WOLFSSL_CERT_EXT) && \ + (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \ + ((LIBWOLFSSL_VERSION_HEX > 0x05008004) || \ + defined(WOLFSSL_PR9713_PATCH_APPLIED)) + WOLFSSL_X509* x509 = (WOLFSSL_X509*)(uintptr_t)x509Ptr; + byte* skidBuf = NULL; + int skidSz = 0; + int ret = WOLFSSL_FAILURE; + (void)jcl; + + if (jenv == NULL || x509 == NULL || skid == NULL) { + return ret; + } + + skidBuf = (byte*)(*jenv)->GetByteArrayElements(jenv, skid, NULL); + skidSz = (*jenv)->GetArrayLength(jenv, skid); + + if (skidBuf != NULL && skidSz > 0) { + ret = wolfSSL_X509_set_subject_key_id(x509, skidBuf, skidSz); + } + + (*jenv)->ReleaseByteArrayElements(jenv, skid, (jbyte*)skidBuf, JNI_ABORT); + + return (jint)ret; +#else + (void)jenv; + (void)jcl; + (void)x509Ptr; + (void)skid; + return (jint)NOT_COMPILED_IN; +#endif +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1set_1subject_1key_1id_1ex + (JNIEnv* jenv, jclass jcl, jlong x509Ptr) +{ + /* Subject/Authority Key ID support added after wolfSSL 5.8.4 in PR 9713. + * Version check must be greater than 5.8.4 or patch from PR 9713 must be + * applied and WOLFSSL_PR9713_PATCH_APPLIED defined when compiling this + * wrapper. */ +#if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS) && \ + defined(WOLFSSL_CERT_EXT) && !defined(NO_SHA) && \ + (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \ + ((LIBWOLFSSL_VERSION_HEX > 0x05008004) || \ + defined(WOLFSSL_PR9713_PATCH_APPLIED)) + WOLFSSL_X509* x509 = (WOLFSSL_X509*)(uintptr_t)x509Ptr; + int ret = WOLFSSL_FAILURE; + (void)jcl; + + if (jenv == NULL || x509 == NULL) { + return ret; + } + + ret = wolfSSL_X509_set_subject_key_id_ex(x509); + + return (jint)ret; +#else + (void)jenv; + (void)jcl; + (void)x509Ptr; + return (jint)NOT_COMPILED_IN; +#endif +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1set_1authority_1key_1id + (JNIEnv* jenv, jclass jcl, jlong x509Ptr, jbyteArray akid) +{ + /* Subject/Authority Key ID support added after wolfSSL 5.8.4 in PR 9713. + * Version check must be greater than 5.8.4 or patch from PR 9713 must be + * applied and WOLFSSL_PR9713_PATCH_APPLIED defined when compiling this + * wrapper. */ +#if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS) && \ + defined(WOLFSSL_CERT_EXT) && \ + (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \ + ((LIBWOLFSSL_VERSION_HEX > 0x05008004) || \ + defined(WOLFSSL_PR9713_PATCH_APPLIED)) + WOLFSSL_X509* x509 = (WOLFSSL_X509*)(uintptr_t)x509Ptr; + byte* akidBuf = NULL; + int akidSz = 0; + int ret = WOLFSSL_FAILURE; + (void)jcl; + + if (jenv == NULL || x509 == NULL || akid == NULL) { + return ret; + } + + akidBuf = (byte*)(*jenv)->GetByteArrayElements(jenv, akid, NULL); + akidSz = (*jenv)->GetArrayLength(jenv, akid); + + if (akidBuf != NULL && akidSz > 0) { + ret = wolfSSL_X509_set_authority_key_id(x509, akidBuf, akidSz); + } + + (*jenv)->ReleaseByteArrayElements(jenv, akid, (jbyte*)akidBuf, JNI_ABORT); + + return (jint)ret; +#else + (void)jenv; + (void)jcl; + (void)x509Ptr; + (void)akid; + return (jint)NOT_COMPILED_IN; +#endif +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1set_1authority_1key_1id_1ex + (JNIEnv* jenv, jclass jcl, jlong x509Ptr, jlong issuerPtr) +{ + /* Subject/Authority Key ID support added after wolfSSL 5.8.4 in PR 9713. + * Version check must be greater than 5.8.4 or patch from PR 9713 must be + * applied and WOLFSSL_PR9713_PATCH_APPLIED defined when compiling this + * wrapper. */ +#if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS) && \ + defined(WOLFSSL_CERT_EXT) && !defined(NO_SHA) && \ + (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \ + ((LIBWOLFSSL_VERSION_HEX > 0x05008004) || \ + defined(WOLFSSL_PR9713_PATCH_APPLIED)) + WOLFSSL_X509* x509 = (WOLFSSL_X509*)(uintptr_t)x509Ptr; + WOLFSSL_X509* issuer = (WOLFSSL_X509*)(uintptr_t)issuerPtr; + int ret = WOLFSSL_FAILURE; + (void)jcl; + + if (jenv == NULL || x509 == NULL || issuer == NULL) { + return ret; + } + + ret = wolfSSL_X509_set_authority_key_id_ex(x509, issuer); + + return (jint)ret; +#else + (void)jenv; + (void)jcl; + (void)x509Ptr; + (void)issuerPtr; + return (jint)NOT_COMPILED_IN; +#endif +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1CRL_1set_1dist_1points + (JNIEnv* jenv, jclass jcl, jlong x509Ptr, jbyteArray der) +{ + /* CRL Distribution Points support added after wolfSSL 5.8.4 in PR 9713. + * Version check must be greater than 5.8.4 or patch from PR 9713 must be + * applied and WOLFSSL_PR9713_PATCH_APPLIED defined when compiling this + * wrapper. */ +#if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS) && \ + defined(WOLFSSL_CERT_EXT) && \ + (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \ + ((LIBWOLFSSL_VERSION_HEX > 0x05008004) || \ + defined(WOLFSSL_PR9713_PATCH_APPLIED)) + WOLFSSL_X509* x509 = (WOLFSSL_X509*)(uintptr_t)x509Ptr; + byte* derBuf = NULL; + int derSz = 0; + int ret = WOLFSSL_FAILURE; + (void)jcl; + + if (jenv == NULL || x509 == NULL || der == NULL) { + return ret; + } + + derBuf = (byte*)(*jenv)->GetByteArrayElements(jenv, der, NULL); + derSz = (*jenv)->GetArrayLength(jenv, der); + + if (derBuf != NULL && derSz > 0) { + ret = wolfSSL_X509_CRL_set_dist_points(x509, derBuf, derSz); + } + + (*jenv)->ReleaseByteArrayElements(jenv, der, (jbyte*)derBuf, JNI_ABORT); + + return (jint)ret; +#else + (void)jenv; + (void)jcl; + (void)x509Ptr; + (void)der; + return (jint)NOT_COMPILED_IN; +#endif +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1CRL_1add_1dist_1point + (JNIEnv* jenv, jclass jcl, jlong x509Ptr, jstring uri, jboolean critical) +{ + /* CRL Distribution Points support added after wolfSSL 5.8.4 in PR 9713. + * Version check must be greater than 5.8.4 or patch from PR 9713 must be + * applied and WOLFSSL_PR9713_PATCH_APPLIED defined when compiling this + * wrapper. */ +#if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS) && \ + defined(WOLFSSL_CERT_EXT) && \ + (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \ + ((LIBWOLFSSL_VERSION_HEX > 0x05008004) || \ + defined(WOLFSSL_PR9713_PATCH_APPLIED)) + WOLFSSL_X509* x509 = (WOLFSSL_X509*)(uintptr_t)x509Ptr; + const char* uriStr = NULL; + int ret = WOLFSSL_FAILURE; + (void)jcl; + + if (jenv == NULL || x509 == NULL || uri == NULL) { + return ret; + } + + uriStr = (*jenv)->GetStringUTFChars(jenv, uri, 0); + if (uriStr != NULL) { + ret = wolfSSL_X509_CRL_add_dist_point(x509, uriStr, + (critical == JNI_TRUE) ? 1 : 0); + } + + (*jenv)->ReleaseStringUTFChars(jenv, uri, uriStr); + + return (jint)ret; +#else + (void)jenv; + (void)jcl; + (void)x509Ptr; + (void)uri; + (void)critical; + return (jint)NOT_COMPILED_IN; +#endif +} + +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1set_1ns_1cert_1type + (JNIEnv* jenv, jclass jcl, jlong x509Ptr, jint nsCertType) +{ + /* Netscape Certificate Type support added after wolfSSL 5.8.4 in PR 9713. + * Version check must be greater than 5.8.4 or patch from PR 9713 must be + * applied and WOLFSSL_PR9713_PATCH_APPLIED defined when compiling this + * wrapper. */ +#if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS) && \ + !defined(IGNORE_NETSCAPE_CERT_TYPE) && \ + (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \ + ((LIBWOLFSSL_VERSION_HEX > 0x05008004) || \ + defined(WOLFSSL_PR9713_PATCH_APPLIED)) + WOLFSSL_X509* x509 = (WOLFSSL_X509*)(uintptr_t)x509Ptr; + int ret = WOLFSSL_FAILURE; + (void)jcl; + + if (jenv == NULL || x509 == NULL) { + return ret; + } + + ret = wolfSSL_X509_set_ns_cert_type(x509, (int)nsCertType); + + return (jint)ret; +#else + (void)jenv; + (void)jcl; + (void)x509Ptr; + (void)nsCertType; + return (jint)NOT_COMPILED_IN; +#endif +} + JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1sign (JNIEnv* jenv, jclass jcl, jlong x509Ptr, jint keyType, jbyteArray fileBytes, jint fileFormat, jstring digestAlg) { diff --git a/native/com_wolfssl_WolfSSLCertificate.h b/native/com_wolfssl_WolfSSLCertificate.h index 1034c1280..2d4c49835 100644 --- a/native/com_wolfssl_WolfSSLCertificate.h +++ b/native/com_wolfssl_WolfSSLCertificate.h @@ -381,6 +381,62 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1set_1notAfter JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1set_1serialNumber (JNIEnv *, jclass, jlong, jbyteArray); +/* + * Class: com_wolfssl_WolfSSLCertificate + * Method: X509_set_subject_key_id + * Signature: (J[B)I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1set_1subject_1key_1id + (JNIEnv *, jclass, jlong, jbyteArray); + +/* + * Class: com_wolfssl_WolfSSLCertificate + * Method: X509_set_subject_key_id_ex + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1set_1subject_1key_1id_1ex + (JNIEnv *, jclass, jlong); + +/* + * Class: com_wolfssl_WolfSSLCertificate + * Method: X509_set_authority_key_id + * Signature: (J[B)I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1set_1authority_1key_1id + (JNIEnv *, jclass, jlong, jbyteArray); + +/* + * Class: com_wolfssl_WolfSSLCertificate + * Method: X509_set_authority_key_id_ex + * Signature: (JJ)I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1set_1authority_1key_1id_1ex + (JNIEnv *, jclass, jlong, jlong); + +/* + * Class: com_wolfssl_WolfSSLCertificate + * Method: X509_CRL_set_dist_points + * Signature: (J[B)I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1CRL_1set_1dist_1points + (JNIEnv *, jclass, jlong, jbyteArray); + +/* + * Class: com_wolfssl_WolfSSLCertificate + * Method: X509_CRL_add_dist_point + * Signature: (JLjava/lang/String;Z)I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1CRL_1add_1dist_1point + (JNIEnv *, jclass, jlong, jstring, jboolean); + +/* + * Class: com_wolfssl_WolfSSLCertificate + * Method: X509_set_ns_cert_type + * Signature: (JI)I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1set_1ns_1cert_1type + (JNIEnv *, jclass, jlong, jint); + /* * Class: com_wolfssl_WolfSSLCertificate * Method: X509_sign diff --git a/src/java/com/wolfssl/WolfSSL.java b/src/java/com/wolfssl/WolfSSL.java index ef9a7be89..9e5915490 100644 --- a/src/java/com/wolfssl/WolfSSL.java +++ b/src/java/com/wolfssl/WolfSSL.java @@ -470,6 +470,30 @@ public enum TLS_VERSION { public static int NID_ext_key_usage; /** Domain name qualifier NID */ public static int NID_dnQualifier; + /** Subject Key Identifier NID */ + public static int NID_subject_key_identifier; + /** Authority Key Identifier NID */ + public static int NID_authority_key_identifier; + /** CRL Distribution Points NID */ + public static int NID_crl_distribution_points; + /** Netscape Certificate Type NID */ + public static int NID_netscape_cert_type; + + /* Netscape Certificate Type bit flags */ + /** Netscape Cert Type: SSL Client */ + public static final int NS_CERT_TYPE_SSL_CLIENT = 0x80; + /** Netscape Cert Type: SSL Server */ + public static final int NS_CERT_TYPE_SSL_SERVER = 0x40; + /** Netscape Cert Type: S/MIME */ + public static final int NS_CERT_TYPE_EMAIL = 0x20; + /** Netscape Cert Type: Object Signing */ + public static final int NS_CERT_TYPE_OBJECT_SIGNING = 0x10; + /** Netscape Cert Type: SSL CA */ + public static final int NS_CERT_TYPE_SSL_CA = 0x04; + /** Netscape Cert Type: S/MIME CA */ + public static final int NS_CERT_TYPE_EMAIL_CA = 0x02; + /** Netscape Cert Type: Object Signing CA */ + public static final int NS_CERT_TYPE_OBJECT_CA = 0x01; /* -------------- Named Groups (from enum in ssl.h) ----------------- */ /** Invalid named group */ @@ -610,6 +634,10 @@ public WolfSSL() throws WolfSSLException { NID_basic_constraints = getNID_basic_constraints(); NID_ext_key_usage = getNID_ext_key_usage(); NID_dnQualifier = getNID_dnQualifier(); + NID_subject_key_identifier = getNID_subject_key_identifier(); + NID_authority_key_identifier = getNID_authority_key_identifier(); + NID_crl_distribution_points = getNID_crl_distribution_points(); + NID_netscape_cert_type = getNID_netscape_cert_type(); /* initialize cipher enum values */ wolfssl_aes = getBulkCipherAlgorithmEnumAES(); @@ -679,6 +707,10 @@ public WolfSSL() throws WolfSSLException { static native int getNID_basic_constraints(); static native int getNID_ext_key_usage(); static native int getNID_dnQualifier(); + static native int getNID_subject_key_identifier(); + static native int getNID_authority_key_identifier(); + static native int getNID_crl_distribution_points(); + static native int getNID_netscape_cert_type(); static native int getBulkCipherAlgorithmEnumNULL(); static native int getBulkCipherAlgorithmEnumRC4(); diff --git a/src/java/com/wolfssl/WolfSSLCertificate.java b/src/java/com/wolfssl/WolfSSLCertificate.java index d5641c8b5..1933c8bc9 100644 --- a/src/java/com/wolfssl/WolfSSLCertificate.java +++ b/src/java/com/wolfssl/WolfSSLCertificate.java @@ -131,6 +131,15 @@ static native int X509_add_ext_via_set_object_boolean(long x509Ptr, static native int X509_set_notBefore(long x509Ptr, long timeSecs); static native int X509_set_notAfter(long x509Ptr, long timeSecs); static native int X509_set_serialNumber(long x509Ptr, byte[] serialBytes); + static native int X509_set_subject_key_id(long x509Ptr, byte[] skid); + static native int X509_set_subject_key_id_ex(long x509Ptr); + static native int X509_set_authority_key_id(long x509Ptr, byte[] akid); + static native int X509_set_authority_key_id_ex(long x509Ptr, + long issuerPtr); + static native int X509_CRL_set_dist_points(long x509Ptr, byte[] der); + static native int X509_CRL_add_dist_point(long x509Ptr, String uri, + boolean critical); + static native int X509_set_ns_cert_type(long x509Ptr, int nsCertType); static native int X509_sign(long x509Ptr, int evpKeyType, byte[] keyBytes, int format, String digestAlg); @@ -738,6 +747,247 @@ public void setSerialNumber(BigInteger serial) } } + /** + * Sets the Subject Key Identifier extension for this WolfSSLCertificate, + * used when generating X509v3 certificates. + * + * @param skid Byte array containing Subject Key Identifier. + * + * @throws IllegalStateException if WolfSSLCertificate has been freed. + * @throws WolfSSLException if invalid arguments or native JNI error occurs. + */ + public void setSubjectKeyId(byte[] skid) + throws IllegalStateException, WolfSSLException { + + int ret = 0; + + confirmObjectIsActive(); + + if (skid == null || skid.length == 0) { + throw new WolfSSLException("Subject Key Identifier is null/empty"); + } + + synchronized (x509Lock) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI, + WolfSSLDebug.INFO, this.x509Ptr, + () -> "entering setSubjectKeyId(byte[])"); + + ret = X509_set_subject_key_id(this.x509Ptr, skid); + } + + if (ret != WolfSSL.SSL_SUCCESS) { + throw new WolfSSLException( + "Error setting subject key id into native WOLFSSL_X509 " + + "(ret: " + ret + ")"); + } + } + + /** + * Sets the Subject Key Identifier extension for this WolfSSLCertificate + * using the public key currently set in the certificate. + * + * @throws IllegalStateException if WolfSSLCertificate has been freed. + * @throws WolfSSLException if native JNI error occurs. + */ + public void setSubjectKeyIdEx() + throws IllegalStateException, WolfSSLException { + + int ret = 0; + + confirmObjectIsActive(); + + synchronized (x509Lock) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI, + WolfSSLDebug.INFO, this.x509Ptr, + () -> "entering setSubjectKeyIdEx()"); + + ret = X509_set_subject_key_id_ex(this.x509Ptr); + } + + if (ret != WolfSSL.SSL_SUCCESS) { + throw new WolfSSLException( + "Error setting subject key id (ex) into native WOLFSSL_X509 " + + "(ret: " + ret + ")"); + } + } + + /** + * Sets the Authority Key Identifier extension for this WolfSSLCertificate, + * used when generating X509v3 certificates. + * + * @param akid Byte array containing Authority Key Identifier. + * + * @throws IllegalStateException if WolfSSLCertificate has been freed. + * @throws WolfSSLException if invalid arguments or native JNI error occurs. + */ + public void setAuthorityKeyId(byte[] akid) + throws IllegalStateException, WolfSSLException { + + int ret = 0; + + confirmObjectIsActive(); + + if (akid == null || akid.length == 0) { + throw new WolfSSLException( + "Authority Key Identifier is null/empty"); + } + + synchronized (x509Lock) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI, + WolfSSLDebug.INFO, this.x509Ptr, + () -> "entering setAuthorityKeyId(byte[])"); + + ret = X509_set_authority_key_id(this.x509Ptr, akid); + } + + if (ret != WolfSSL.SSL_SUCCESS) { + throw new WolfSSLException( + "Error setting authority key id into native WOLFSSL_X509 " + + "(ret: " + ret + ")"); + } + } + + /** + * Sets the Authority Key Identifier extension for this WolfSSLCertificate + * using the issuer certificate. + * + * @param issuer Issuer certificate used to derive the Authority Key ID. + * + * @throws IllegalStateException if WolfSSLCertificate has been freed. + * @throws WolfSSLException if invalid arguments or native JNI error occurs. + */ + public void setAuthorityKeyIdEx(WolfSSLCertificate issuer) + throws IllegalStateException, WolfSSLException { + + int ret = 0; + long issuerX509Ptr; + + confirmObjectIsActive(); + + if (issuer == null) { + throw new WolfSSLException("Issuer certificate is null"); + } + + issuerX509Ptr = issuer.getX509Ptr(); + + synchronized (x509Lock) { + + WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI, + WolfSSLDebug.INFO, this.x509Ptr, + () -> "entering setAuthorityKeyIdEx(issuerPtr=" + + issuerX509Ptr + ")"); + + ret = X509_set_authority_key_id_ex(this.x509Ptr, + issuerX509Ptr); + } + + if (ret != WolfSSL.SSL_SUCCESS) { + throw new WolfSSLException( + "Error setting authority key id (ex) into native " + + "WOLFSSL_X509 (ret: " + ret + ")"); + } + } + + /** + * Sets the CRL Distribution Points extension from DER data. + * + * @param der DER-encoded CRL Distribution Points extension. + * + * @throws IllegalStateException if WolfSSLCertificate has been freed. + * @throws WolfSSLException if invalid arguments or native JNI error occurs. + */ + public void setCrlDistPoints(byte[] der) + throws IllegalStateException, WolfSSLException { + + int ret = 0; + + confirmObjectIsActive(); + + if (der == null || der.length == 0) { + throw new WolfSSLException("CRL dist points DER is null/empty"); + } + + synchronized (x509Lock) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI, + WolfSSLDebug.INFO, this.x509Ptr, + () -> "entering setCrlDistPoints(byte[])"); + + ret = X509_CRL_set_dist_points(this.x509Ptr, der); + } + + if (ret != WolfSSL.SSL_SUCCESS) { + throw new WolfSSLException( + "Error setting CRL dist points into native WOLFSSL_X509 " + + "(ret: " + ret + ")"); + } + } + + /** + * Adds a CRL Distribution Point URI. + * + * @param uri URI string of the distribution point. + * @param critical Whether to mark the extension critical. + * + * @throws IllegalStateException if WolfSSLCertificate has been freed. + * @throws WolfSSLException if invalid arguments or native JNI error occurs. + */ + public void addCrlDistPoint(String uri, boolean critical) + throws IllegalStateException, WolfSSLException { + + int ret = 0; + + confirmObjectIsActive(); + + if (uri == null || uri.isEmpty()) { + throw new WolfSSLException("CRL dist point URI is null/empty"); + } + + synchronized (x509Lock) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI, + WolfSSLDebug.INFO, this.x509Ptr, + () -> "entering addCrlDistPoint(" + uri + ", critical: " + + critical + ")"); + + ret = X509_CRL_add_dist_point(this.x509Ptr, uri, critical); + } + + if (ret != WolfSSL.SSL_SUCCESS) { + throw new WolfSSLException( + "Error adding CRL dist point into native WOLFSSL_X509 " + + "(ret: " + ret + ")"); + } + } + + /** + * Sets the Netscape Certificate Type extension. + * + * @param nsCertType Netscape cert type bitmask. + * + * @throws IllegalStateException if WolfSSLCertificate has been freed. + * @throws WolfSSLException if native JNI error occurs. + */ + public void setNsCertType(int nsCertType) + throws IllegalStateException, WolfSSLException { + + int ret = 0; + + confirmObjectIsActive(); + + synchronized (x509Lock) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI, + WolfSSLDebug.INFO, this.x509Ptr, + () -> "entering setNsCertType(" + nsCertType + ")"); + + ret = X509_set_ns_cert_type(this.x509Ptr, nsCertType); + } + + if (ret != WolfSSL.SSL_SUCCESS) { + throw new WolfSSLException( + "Error setting ns cert type into native WOLFSSL_X509 " + + "(ret: " + ret + ")"); + } + } + /** * Sets the notBefore date for this WolfSSLCertificate, used when * generating X509v3 certificates. @@ -973,6 +1223,28 @@ public void addExtension(int nid, boolean value, boolean isCritical) } } + /** + * Add IP address Subject Alternative Name. + * + * Convenience method for adding an IP address to the Subject Alternative + * Name extension. + * + * @param ipAddress IP address string (e.g., "192.168.1.1" or "::1") + * + * @throws IllegalStateException if WolfSSLCertificate has been freed. + * @throws WolfSSLException if invalid arguments or native JNI error occurs. + */ + public void addAltNameIP(String ipAddress) + throws IllegalStateException, WolfSSLException { + + if (ipAddress == null || ipAddress.isEmpty()) { + throw new WolfSSLException( + "IP address must not be null or empty"); + } + + addAltName(ipAddress, WolfSSL.ASN_IP_TYPE); + } + /** * Sign certificate with private key from file. * diff --git a/src/test/com/wolfssl/test/WolfSSLCertificateTest.java b/src/test/com/wolfssl/test/WolfSSLCertificateTest.java index 6fb195e68..bef5a42f8 100644 --- a/src/test/com/wolfssl/test/WolfSSLCertificateTest.java +++ b/src/test/com/wolfssl/test/WolfSSLCertificateTest.java @@ -78,6 +78,7 @@ public class WolfSSLCertificateTest { public static String serverCertPem = "examples/certs/server-cert.pem"; public static String external = "examples/certs/ca-google-root.der"; public static String sanTestDir = "examples/certs/san-test"; + public static String crlDpCertPem = "examples/certs/test/crl-dp-cert.pem"; public static String sanTestUpnCert = null; public static String sanTestAllTypesCert = null; public static String sanTestAllTypesDer = null; @@ -93,6 +94,34 @@ public class WolfSSLCertificateTest { public static String bogusFile = "/dev/null"; private WolfSSLCertificate cert; + private interface ThrowingRunnable { + void run() throws WolfSSLException, WolfSSLJNIException, IOException; + } + + private boolean isNotCompiledIn(WolfSSLException e) { + String msg = e.getMessage(); + if (msg == null) { + return false; + } + return msg.contains(Integer.toString(WolfSSL.NOT_COMPILED_IN)) || + msg.contains("NOT_COMPILED_IN"); + } + + private void runOrAllowNotCompiled(ThrowingRunnable r, String label) + throws WolfSSLException, WolfSSLJNIException, IOException { + + try { + r.run(); + } catch (WolfSSLException e) { + if (isNotCompiledIn(e)) { + System.out.println("\t\t" + label + + " ... NOT_COMPILED_IN (skipping)"); + return; + } + throw e; + } + } + @BeforeClass public static void setCertPaths() throws WolfSSLException { @@ -112,6 +141,7 @@ public static void setCertPaths() throws WolfSSLException { serverCertPem = WolfSSLTestCommon.getPath(serverCertPem); external = WolfSSLTestCommon.getPath(external); sanTestDir = WolfSSLTestCommon.getPath(sanTestDir); + crlDpCertPem = WolfSSLTestCommon.getPath(crlDpCertPem); sanTestUpnCert = sanTestDir + "/san-test-othername-upn.pem"; sanTestAllTypesCert = sanTestDir + "/san-test-all-types.pem"; sanTestAllTypesDer = sanTestDir + "/san-test-all-types.der"; @@ -870,6 +900,90 @@ public void testWolfSSLCertificateGeneration() } } + @Test + public void testWolfSSLCertificateExtensionSetters() + throws WolfSSLException, WolfSSLJNIException, IOException, + CertificateException { + + System.out.println("WolfSSLCertificate extension setters"); + + if (WolfSSL.FileSystemEnabled() == false) { + System.out.println("\tfilesystem disabled, skipping"); + return; + } + + WolfSSLCertificate x509 = new WolfSSLCertificate(); + assertNotNull(x509); + + WolfSSLX509Name subjectName = GenerateTestSubjectName(); + assertNotNull(subjectName); + + WolfSSLCertificate issuer = + new WolfSSLCertificate(caCertPem, WolfSSL.SSL_FILETYPE_PEM); + assertNotNull(issuer); + + /* Minimal required fields for extensions that depend on */ + /* pubkey/issuer */ + Instant now = Instant.now(); + x509.setNotBefore(Date.from(now)); + x509.setNotAfter(Date.from(now.plus(Duration.ofDays(365)))); + x509.setSerialNumber(BigInteger.valueOf(67890)); + x509.setSubjectName(subjectName); + x509.setIssuerName(issuer); + x509.setPublicKey(cliKeyPubDer, WolfSSL.RSAk, + WolfSSL.SSL_FILETYPE_ASN1); + + /* Arbitrary 20-byte test vectors for SKID/AKID content. */ + final byte[] skid = new byte[] { + 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0A, + 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14 + }; + final byte[] akid = new byte[] { + 0x10, 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x19, + 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, + 0x1F, 0x20, 0x21, 0x22, 0x23 + }; + + runOrAllowNotCompiled( + () -> x509.setSubjectKeyId(skid), + "setSubjectKeyId"); + runOrAllowNotCompiled( + () -> x509.setSubjectKeyIdEx(), + "setSubjectKeyIdEx"); + runOrAllowNotCompiled( + () -> x509.setAuthorityKeyId(akid), + "setAuthorityKeyId"); + runOrAllowNotCompiled( + () -> x509.setAuthorityKeyIdEx(issuer), + "setAuthorityKeyIdEx"); + + runOrAllowNotCompiled( + () -> x509.addCrlDistPoint("http://crl.example.com/ca.crl", false), + "addCrlDistPoint"); + + byte[] crlDpDer = issuer.getExtension("2.5.29.31"); + if (crlDpDer != null && crlDpDer.length > 0) { + runOrAllowNotCompiled( + () -> x509.setCrlDistPoints(crlDpDer), + "setCrlDistPoints"); + } else { + System.out.println("\t\tsetCrlDistPoints ... no DER available"); + } + + runOrAllowNotCompiled( + () -> x509.setNsCertType(0x80), + "setNsCertType"); + + subjectName.free(); + issuer.free(); + x509.free(); + + System.out.println("\t\t... passed"); + } + /* Quick sanity check on certificate bytes. Loads cert into new * WolfSSLCertificate object, tries to get various elements and * simply verify if not null / etc. */