Skip to content

Commit d64e206

Browse files
committed
JNI/JCE: add getExtendedKeyUsage() to X509Certificate (WolfSSLX509), wrap wolfSSL_X509_get_extended_key_usage() in WolfSSLCertificate.getExtendedKeyUsage()
1 parent 5e4dd5d commit d64e206

6 files changed

Lines changed: 286 additions & 1 deletion

File tree

native/com_wolfssl_WolfSSLCertificate.c

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1565,6 +1565,107 @@ JNIEXPORT jbooleanArray JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1get_1k
15651565
return ret;
15661566
}
15671567

1568+
/* Helper function to add an Extended Key Usage (EKU) OID string to the
1569+
* jobjectArray array if the flag is set. Returns updated index in array. */
1570+
static int addEkuOid(JNIEnv* jenv, jobjectArray ret, int idx,
1571+
unsigned int ekuBits, unsigned int flag, int nid)
1572+
{
1573+
WOLFSSL_ASN1_OBJECT* obj = NULL;
1574+
jstring ekuStr = NULL;
1575+
char oidBuf[128];
1576+
int oidLen = 0;
1577+
1578+
if (ekuBits & flag) {
1579+
1580+
/* Convert NID to WOLFSSL_ASN1_OBJECT */
1581+
obj = wolfSSL_OBJ_nid2obj(nid);
1582+
if (obj != NULL) {
1583+
1584+
/* Convert WOLFSSL_ASN1_OBJECT to OID string */
1585+
oidLen = wolfSSL_OBJ_obj2txt(oidBuf, sizeof(oidBuf), obj, 1);
1586+
if (oidLen > 0) {
1587+
1588+
/* Create Java String and add to array */
1589+
ekuStr = (*jenv)->NewStringUTF(jenv, oidBuf);
1590+
if (ekuStr != NULL) {
1591+
(*jenv)->SetObjectArrayElement(jenv, ret, idx, ekuStr);
1592+
(*jenv)->DeleteLocalRef(jenv, ekuStr);
1593+
idx++;
1594+
}
1595+
}
1596+
1597+
/* Free WOLFSSL_ASN1_OBJECT */
1598+
wolfSSL_ASN1_OBJECT_free(obj);
1599+
}
1600+
}
1601+
1602+
return idx;
1603+
}
1604+
1605+
JNIEXPORT jobjectArray JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1get_1extended_1key_1usage
1606+
(JNIEnv* jenv, jclass jcl, jlong x509Ptr)
1607+
{
1608+
jobjectArray ret = NULL;
1609+
jclass stringClass = NULL;
1610+
unsigned int ekuBits = 0;
1611+
int ekuCount = 0;
1612+
int idx = 0;
1613+
WOLFSSL_X509* x509 = (WOLFSSL_X509*)(uintptr_t)x509Ptr;
1614+
(void)jcl;
1615+
1616+
if (jenv == NULL || x509 == NULL) {
1617+
return NULL;
1618+
}
1619+
1620+
/* Get extended key usage bitmask from wolfSSL */
1621+
ekuBits = wolfSSL_X509_get_extended_key_usage(x509);
1622+
if (ekuBits == 0) {
1623+
return NULL;
1624+
}
1625+
1626+
/* Count how many EKU bits are set */
1627+
if (ekuBits & XKU_SSL_SERVER) ekuCount++;
1628+
if (ekuBits & XKU_SSL_CLIENT) ekuCount++;
1629+
if (ekuBits & XKU_CODE_SIGN) ekuCount++;
1630+
if (ekuBits & XKU_SMIME) ekuCount++;
1631+
if (ekuBits & XKU_TIMESTAMP) ekuCount++;
1632+
if (ekuBits & XKU_OCSP_SIGN) ekuCount++;
1633+
1634+
if (ekuCount == 0) {
1635+
return NULL;
1636+
}
1637+
1638+
/* Create String[] array to return */
1639+
stringClass = (*jenv)->FindClass(jenv, "java/lang/String");
1640+
if (stringClass == NULL) {
1641+
return NULL;
1642+
}
1643+
1644+
ret = (*jenv)->NewObjectArray(jenv, ekuCount, stringClass, NULL);
1645+
if (ret == NULL) {
1646+
(*jenv)->DeleteLocalRef(jenv, stringClass);
1647+
return NULL;
1648+
}
1649+
1650+
/* Add each EKU OID string to array */
1651+
idx = addEkuOid(jenv, ret, idx, ekuBits, XKU_SSL_SERVER,
1652+
EKU_SERVER_AUTH_OID);
1653+
idx = addEkuOid(jenv, ret, idx, ekuBits, XKU_SSL_CLIENT,
1654+
EKU_CLIENT_AUTH_OID);
1655+
idx = addEkuOid(jenv, ret, idx, ekuBits, XKU_CODE_SIGN,
1656+
EKU_CODESIGNING_OID);
1657+
idx = addEkuOid(jenv, ret, idx, ekuBits, XKU_SMIME,
1658+
EKU_EMAILPROTECT_OID);
1659+
idx = addEkuOid(jenv, ret, idx, ekuBits, XKU_TIMESTAMP,
1660+
EKU_TIMESTAMP_OID);
1661+
idx = addEkuOid(jenv, ret, idx, ekuBits, XKU_OCSP_SIGN,
1662+
EKU_OCSP_SIGN_OID);
1663+
1664+
(*jenv)->DeleteLocalRef(jenv, stringClass);
1665+
1666+
return ret;
1667+
}
1668+
15681669
JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1get_1extension
15691670
(JNIEnv* jenv, jclass jcl, jlong x509Ptr, jstring oidIn)
15701671
{

native/com_wolfssl_WolfSSLCertificate.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.

src/java/com/wolfssl/WolfSSLCertificate.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ public class WolfSSLCertificate implements Serializable {
9898
static native int X509_get_pathLength(long x509);
9999
static native int X509_verify(long x509, byte[] pubKey, int pubKeySz);
100100
static native boolean[] X509_get_key_usage(long x509);
101+
static native String[] X509_get_extended_key_usage(long x509);
101102
static native byte[] X509_get_extension(long x509, String oid);
102103
static native int X509_is_extension_set(long x509, String oid);
103104
static native String X509_get_next_altname(long x509);
@@ -1550,6 +1551,34 @@ public boolean[] getKeyUsage() throws IllegalStateException {
15501551
}
15511552
}
15521553

1554+
/**
1555+
* Get extended key usage OIDs from X.509 certificate.
1556+
*
1557+
* Returns an array of OID strings representing the extended key usage
1558+
* values present in the certificate. Common OIDs include:
1559+
* - 1.3.6.1.5.5.7.3.1 (TLS Web Server Authentication / serverAuth)
1560+
* - 1.3.6.1.5.5.7.3.2 (TLS Web Client Authentication / clientAuth)
1561+
* - 1.3.6.1.5.5.7.3.3 (Code Signing / codeSigning)
1562+
* - 1.3.6.1.5.5.7.3.4 (Email Protection / emailProtection)
1563+
*
1564+
* @return Array of OID strings, or null if Extended Key Usage extension
1565+
* is not present in certificate
1566+
*
1567+
* @throws IllegalStateException if WolfSSLCertificate has been freed
1568+
*/
1569+
public String[] getExtendedKeyUsage() throws IllegalStateException {
1570+
1571+
confirmObjectIsActive();
1572+
1573+
synchronized (x509Lock) {
1574+
WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI,
1575+
WolfSSLDebug.INFO, this.x509Ptr,
1576+
() -> "entering getExtendedKeyUsage()");
1577+
1578+
return X509_get_extended_key_usage(this.x509Ptr);
1579+
}
1580+
}
1581+
15531582
/**
15541583
* Get DER encoded extension value from a specified OID
15551584
*

src/java/com/wolfssl/provider/jsse/WolfSSLX509.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import java.security.cert.CertificateParsingException;
4141
import java.security.cert.X509Certificate;
4242
import java.util.ArrayList;
43+
import java.util.Collections;
4344
import java.util.Date;
4445
import java.util.Set;
4546
import java.util.TreeSet;
@@ -319,6 +320,34 @@ public boolean[] getKeyUsage() {
319320
return this.cert.getKeyUsage();
320321
}
321322

323+
@Override
324+
public List<String> getExtendedKeyUsage()
325+
throws CertificateParsingException {
326+
327+
String[] ekuArray;
328+
329+
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
330+
() -> "entered getExtendedKeyUsage()");
331+
332+
if (this.cert == null) {
333+
return null;
334+
}
335+
336+
ekuArray = this.cert.getExtendedKeyUsage();
337+
if (ekuArray == null) {
338+
return null;
339+
}
340+
341+
/* Convert String[] to List<String> as required by
342+
* X509Certificate.getExtendedKeyUsage() API */
343+
List<String> ekuList = new ArrayList<String>();
344+
for (String oid : ekuArray) {
345+
ekuList.add(oid);
346+
}
347+
348+
return Collections.unmodifiableList(ekuList);
349+
}
350+
322351
@Override
323352
public int getBasicConstraints() {
324353

src/test/com/wolfssl/provider/jsse/test/WolfSSLX509Test.java

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ public static void testProviderInstallationAtRuntime()
9494
try {
9595
tf = new WolfSSLTestFactory();
9696
} catch (WolfSSLException e) {
97-
// TODO Auto-generated catch block
9897
e.printStackTrace();
9998
}
10099
}
@@ -804,6 +803,70 @@ public void testWolfSSLPrincipalInterfaceImplementation() {
804803
pass("\t... passed");
805804
}
806805

806+
@Test
807+
public void testGetExtendedKeyUsage() {
808+
X509Certificate x509;
809+
List<String> eku;
810+
811+
System.out.print("\tTesting getExtendedKeyUsage");
812+
813+
/* skip if wolfSSL compiled with NO_FILESYSTEM */
814+
if (WolfSSL.FileSystemEnabled() == false) {
815+
pass("\t... skipped");
816+
return;
817+
}
818+
819+
try {
820+
/* Test with server certificate which has Extended Key Usage
821+
* extension with serverAuth and clientAuth */
822+
byte[] der = tf.getCert("server");
823+
x509 = new WolfSSLX509(der);
824+
eku = x509.getExtendedKeyUsage();
825+
826+
/* Server cert should have EKU extension */
827+
if (eku == null) {
828+
error("\t... failed");
829+
fail("getExtendedKeyUsage() returned null for server cert");
830+
}
831+
832+
/* Server cert should have serverAuth (1.3.6.1.5.5.7.3.1) and
833+
* clientAuth (1.3.6.1.5.5.7.3.2) */
834+
if (eku.size() != 2) {
835+
error("\t... failed");
836+
fail("Expected 2 EKU OIDs, got: " + eku.size());
837+
}
838+
839+
if (!eku.contains("1.3.6.1.5.5.7.3.1")) {
840+
error("\t... failed");
841+
fail("Missing serverAuth OID 1.3.6.1.5.5.7.3.1");
842+
}
843+
844+
if (!eku.contains("1.3.6.1.5.5.7.3.2")) {
845+
error("\t... failed");
846+
fail("Missing clientAuth OID 1.3.6.1.5.5.7.3.2");
847+
}
848+
849+
/* Verify all OIDs are properly formatted */
850+
for (String oid : eku) {
851+
if (oid == null || oid.isEmpty()) {
852+
error("\t... failed");
853+
fail("EKU OID is null or empty");
854+
}
855+
if (!oid.matches("^[0-9]+(\\.[0-9]+)*$")) {
856+
error("\t... failed");
857+
fail("Invalid OID format: " + oid);
858+
}
859+
}
860+
861+
} catch (Exception ex) {
862+
error("\t... failed");
863+
ex.printStackTrace();
864+
fail("unexpected exception: " + ex.getMessage());
865+
}
866+
867+
pass("\t... passed");
868+
}
869+
807870
@Test
808871
public void testWolfSSLPrincipalImplies() {
809872
Subject emptySubject, subjectWithPrincipals;

src/test/com/wolfssl/test/WolfSSLCertificateTest.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ public void test_runCertTestsAfterConstructor() {
140140
/* Key Usage and Extended Key Usage only work with wolfSSL
141141
* later than 5.6.3 */
142142
test_getKeyUsage();
143+
test_getExtendedKeyUsage();
143144
}
144145
test_getExtensionSet();
145146
test_toString();
@@ -600,6 +601,60 @@ public void test_getKeyUsage() {
600601
System.out.println("\t\t... passed");
601602
}
602603

604+
public void test_getExtendedKeyUsage() {
605+
int i;
606+
String[] eku;
607+
String[] expected = {
608+
"1.3.6.1.5.5.7.3.1", /* TLS Web Server Authentication */
609+
"1.3.6.1.5.5.7.3.2" /* TLS Web Client Authentication */
610+
};
611+
612+
System.out.print("\t\tgetExtendedKeyUsage");
613+
614+
/* Client cert has Extended Key Usage extension with serverAuth
615+
* and clientAuth */
616+
eku = this.cert.getExtendedKeyUsage();
617+
if (eku == null) {
618+
System.out.println("\t... failed");
619+
fail("getExtendedKeyUsage() returned null for client cert");
620+
}
621+
622+
if (eku.length != expected.length) {
623+
System.out.println("\t... failed");
624+
fail("Expected " + expected.length + " EKU OIDs, got: " +
625+
eku.length);
626+
}
627+
628+
/* Verify expected OIDs are present */
629+
for (i = 0; i < expected.length; i++) {
630+
boolean found = false;
631+
for (String oid : eku) {
632+
if (oid.equals(expected[i])) {
633+
found = true;
634+
break;
635+
}
636+
}
637+
if (!found) {
638+
System.out.println("\t... failed");
639+
fail("Missing expected OID: " + expected[i]);
640+
}
641+
}
642+
643+
/* Verify all OIDs are properly formatted */
644+
for (i = 0; i < eku.length; i++) {
645+
if (eku[i] == null || eku[i].isEmpty()) {
646+
System.out.println("\t... failed");
647+
fail("Extended key usage OID is null or empty");
648+
}
649+
if (!eku[i].matches("^[0-9]+(\\.[0-9]+)*$")) {
650+
System.out.println("\t... failed");
651+
fail("Invalid OID format: " + eku[i]);
652+
}
653+
}
654+
655+
System.out.println("\t... passed");
656+
}
657+
603658
public void test_getExtensionSet() {
604659
System.out.print("\t\tgetExtensionSet");
605660

0 commit comments

Comments
 (0)