diff --git a/README b/README index a20a97c..3c8cba5 100644 --- a/README +++ b/README @@ -24,6 +24,10 @@ wolfSSL JNI Release X.X.X (TBD) Release X.X.X has bug fixes and new features including: - Support for custom TrustManager checkClientTrusted(), checkServerTrusted() +- wolfJSSE TrustManager registered as PKIX provider +- Improved support for auto-loading system CA certificates +- Improved Android TrustManager support +- Support for X509Certificate.getSubjectAlternativeNames() The wolfSSL JNI Manual is available at: http://www.wolfssl.com/documentation/wolfSSL-JNI-Manual.pdf. For build diff --git a/examples/certs/example-com.der b/examples/certs/example-com.der new file mode 100644 index 0000000..39a5213 Binary files /dev/null and b/examples/certs/example-com.der differ diff --git a/native/com_wolfssl_WolfSSLCertificate.c b/native/com_wolfssl_WolfSSLCertificate.c index 05035b0..545911f 100644 --- a/native/com_wolfssl_WolfSSLCertificate.c +++ b/native/com_wolfssl_WolfSSLCertificate.c @@ -64,6 +64,84 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSLCertificate_d2i_1X509 return (jlong)(intptr_t)wolfSSL_d2i_X509(NULL, &pt, sz); } +JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_WolfSSLCertificate_certPemToDer + (JNIEnv* jenv, jclass jcl, jbyteArray pem, jint pemSz) +{ + unsigned char* der = NULL; + unsigned char* tmpPem = NULL; + int derSz = 0; + jbyteArray ret; + + if (!jenv || !pem || (pemSz < 0)) + return NULL; + + /* find exception class */ + jclass excClass = (*jenv)->FindClass(jenv, + "com/wolfssl/WolfSSLJNIException"); + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + return NULL; + } + + /* allocate space for DER, using PEM sz. DER will be smaller than PEM */ + derSz = pemSz; + der = (unsigned char*)XMALLOC(derSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (der == NULL) { + return NULL; + } + XMEMSET(der, 0, derSz); + + /* allocate space for converted jbyteArray */ + tmpPem = (unsigned char*)XMALLOC(pemSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (tmpPem == NULL) { + XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return NULL; + } + + (*jenv)->GetByteArrayRegion(jenv, pem, 0, pemSz, (jbyte*)tmpPem); + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + XFREE(tmpPem, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER); + (*jenv)->ThrowNew(jenv, excClass, + "Failed to get byte region in native certPemToDer"); + return NULL; + } + + derSz = wc_CertPemToDer(tmpPem, pemSz, der, derSz, CERT_TYPE); + if (derSz < 0) { + XFREE(tmpPem, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER); + (*jenv)->ThrowNew(jenv, excClass, + "wc_CertPemToDer() failed in native certPemToDer"); + return NULL; + } + XFREE(tmpPem, NULL, DYNAMIC_TYPE_TMP_BUFFER); + + ret = (*jenv)->NewByteArray(jenv, derSz); + if (!ret) { + XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER); + (*jenv)->ThrowNew(jenv, jcl, + "Failed to create Java byte array in native certPemToDer"); + return NULL; + } + + (*jenv)->SetByteArrayRegion(jenv, ret, 0, derSz, (jbyte*)der); + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER); + (*jenv)->ThrowNew(jenv, excClass, + "Failed to set byte region in native certPemToDer"); + return NULL; + } + XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER); + + return ret; +} + JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1get_1der (JNIEnv* jenv, jclass jcl, jlong x509) { @@ -688,3 +766,29 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1is_1extension_1 return 0; } +JNIEXPORT jstring JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1get_1next_1altname + (JNIEnv* jenv, jclass jcl, jlong x509) +{ +#if defined(KEEP_PEER_CERT) || defined(SESSION_CERTS) + char* altname; + jstring retString; + (void)jcl; + + if (!jenv || !x509) + return NULL; + + altname = wolfSSL_X509_get_next_altname((WOLFSSL_X509*)(intptr_t)x509); + if (altname == NULL) { + return NULL; + } + retString = (*jenv)->NewStringUTF(jenv, altname); + return retString; + +#else + (void)jenv; + (void)jcl; + (void)x509; + return NULL; +#endif +} + diff --git a/native/com_wolfssl_WolfSSLCertificate.h b/native/com_wolfssl_WolfSSLCertificate.h index 6160999..9cf2511 100644 --- a/native/com_wolfssl_WolfSSLCertificate.h +++ b/native/com_wolfssl_WolfSSLCertificate.h @@ -15,6 +15,14 @@ extern "C" { JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSLCertificate_d2i_1X509 (JNIEnv *, jclass, jbyteArray, jint); +/* + * Class: com_wolfssl_WolfSSLCertificate + * Method: certPemToDer + * Signature: ([BI)[B + */ +JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_WolfSSLCertificate_certPemToDer + (JNIEnv *, jclass, jbyteArray, jint); + /* * Class: com_wolfssl_WolfSSLCertificate * Method: X509_get_der @@ -183,6 +191,14 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1get_1exte JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1is_1extension_1set (JNIEnv *, jclass, jlong, jstring); +/* + * Class: com_wolfssl_WolfSSLCertificate + * Method: X509_get_next_altname + * Signature: (J)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1get_1next_1altname + (JNIEnv *, jclass, jlong); + #ifdef __cplusplus } #endif diff --git a/src/java/com/wolfssl/WolfSSLCertificate.java b/src/java/com/wolfssl/WolfSSLCertificate.java index 042d141..badcbbd 100644 --- a/src/java/com/wolfssl/WolfSSLCertificate.java +++ b/src/java/com/wolfssl/WolfSSLCertificate.java @@ -30,6 +30,10 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; +import java.util.List; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.logging.Level; import java.util.logging.Logger; @@ -42,7 +46,11 @@ public class WolfSSLCertificate { private boolean active = false; private long x509Ptr = 0; + /* cache alt names once retrieved once */ + private Collection> altNames = null; + static native long d2i_X509(byte[] der, int len); + static native byte[] certPemToDer(byte[] pem, int len); static native byte[] X509_get_der(long x509); static native byte[] X509_get_tbs(long x509); static native void X509_free(long x509); @@ -64,11 +72,43 @@ public class WolfSSLCertificate { static native boolean[] X509_get_key_usage(long x509); static native byte[] X509_get_extension(long x509, String oid); static native int X509_is_extension_set(long x509, String oid); + static native String X509_get_next_altname(long x509); public WolfSSLCertificate(byte[] der) throws WolfSSLException { x509Ptr = d2i_X509(der, der.length); if (x509Ptr == 0) { - throw new WolfSSLException("Failed to create SSL Context"); + throw new WolfSSLException("Failed to create WolfSSLCertificate"); + } + this.active = true; + } + + public WolfSSLCertificate(byte[] in, int format) throws WolfSSLException { + byte[] input = in; + + if (in == null || in.length == 0) { + throw new WolfSSLException( + "Input array must not be null or zero length"); + } + + if (format != WolfSSL.SSL_FILETYPE_ASN1 && + format != WolfSSL.SSL_FILETYPE_PEM) { + throw new WolfSSLException( + "Input format must be WolfSSL.SSL_FILETYPE_ASN1 or " + + "WolfSSL.SSL_FILETYPE_PEM"); + } + + if (format == WolfSSL.SSL_FILETYPE_PEM) { + /* convert PEM to DER */ + input = certPemToDer(in, in.length); + if (input == null) { + throw new WolfSSLException("Failed to convert PEM to DER"); + } + } + + /* create from DER array */ + x509Ptr = d2i_X509(input, input.length); + if (x509Ptr == 0) { + throw new WolfSSLException("Failed to create WolfSSLCertificate"); } this.active = true; } @@ -88,6 +128,51 @@ public class WolfSSLCertificate { "Failed to create WolfSSLCertificate", ex); } + x509Ptr = d2i_X509(der, der.length); + if (x509Ptr == 0) { + throw new WolfSSLException( + "Failed to create WolfSSLCertificate, d2i_X509() returned 0"); + } + this.active = true; + } + + public WolfSSLCertificate(String fileName, int format) + throws WolfSSLException { + InputStream stream = null; + byte[] bytes = null; + byte[] der = null; + + if (fileName == null) { + throw new WolfSSLException("Input file must not be null"); + } + + if (format != WolfSSL.SSL_FILETYPE_ASN1 && + format != WolfSSL.SSL_FILETYPE_PEM) { + throw new WolfSSLException( + "Input format must be WolfSSL.SSL_FILETYPE_ASN1 or " + + "WolfSSL.SSL_FILETYPE_PEM"); + } + + File f = new File(fileName); + try { + bytes = new byte[(int) f.length()]; + stream = new FileInputStream(f); + stream.read(bytes, 0, bytes.length); + stream.close(); + } catch (IOException ex) { + throw new WolfSSLException( + "Failed to create WolfSSLCertificate", ex); + } + + if (format == WolfSSL.SSL_FILETYPE_PEM) { + /* convert PEM to DER */ + der = certPemToDer(bytes, bytes.length); + if (der == null) { + throw new WolfSSLException("Failed to convert PEM to DER"); + } + } else { + der = bytes; + } x509Ptr = d2i_X509(der, der.length); if (x509Ptr == 0) { @@ -225,6 +310,49 @@ public class WolfSSLCertificate { return X509_is_extension_set(this.x509Ptr, oid); } + /** + * Returns an immutable Collection of subject alternative names from this + * certificate's SubjectAltName extension. + * + * Each collection item is a List containing two objects: + * [0] = Integer representing type of name, 0-8 (ex: 2 == dNSName) + * [1] = String representing altname entry. + * + * Note: this currently returns all altNames as dNSName types, with the + * second list element being a String. + * + * @return immutable Collection of subject alternative names, or null + */ + public Collection> getSubjectAltNames() { + + if (this.active == false) { + throw new IllegalStateException("Object has been freed"); + } + + if (this.altNames != null) { + /* already gathered, return cached version */ + return this.altNames; + } + + Collection> names = new ArrayList>(); + + String nextAltName = X509_get_next_altname(this.x509Ptr); + while (nextAltName != null) { + Object[] entry = new Object[2]; + entry[0] = 2; // Only return dNSName type for now + entry[1] = nextAltName; + List entryList = Arrays.asList(entry); + + names.add(Collections.unmodifiableList(entryList)); + nextAltName = X509_get_next_altname(this.x509Ptr); + } + + /* cache altNames collection for later use */ + this.altNames = Collections.unmodifiableCollection(names); + + return this.altNames; + } + /** * Returns X509Certificate object based on this certificate. * diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLContext.java b/src/java/com/wolfssl/provider/jsse/WolfSSLContext.java index 01519ca..c0254ed 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLContext.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLContext.java @@ -141,6 +141,9 @@ public class WolfSSLContext extends SSLContextSpi { return; } + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Using X509TrustManager: " + tm.toString()); + X509Certificate[] caList = tm.getAcceptedIssuers(); if (caList == null) { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLProvider.java b/src/java/com/wolfssl/provider/jsse/WolfSSLProvider.java index a7db754..0ba96cc 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLProvider.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLProvider.java @@ -77,6 +77,8 @@ public final class WolfSSLProvider extends Provider { "com.wolfssl.provider.jsse.WolfSSLContext$DEFAULT_Context"); /* Trust Factory */ + put("TrustManagerFactory.PKIX", + "com.wolfssl.provider.jsse.WolfSSLTrustManager"); put("TrustManagerFactory.X509", "com.wolfssl.provider.jsse.WolfSSLTrustManager"); put("TrustManagerFactory.SunX509", diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLTrustManager.java b/src/java/com/wolfssl/provider/jsse/WolfSSLTrustManager.java index fcd77e1..f60e383 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLTrustManager.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLTrustManager.java @@ -26,16 +26,22 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.io.ByteArrayInputStream; import java.security.InvalidAlgorithmParameterException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; import java.util.logging.Level; import java.util.logging.Logger; import javax.net.ssl.ManagerFactoryParameters; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactorySpi; +import com.wolfssl.WolfSSL; +import com.wolfssl.WolfSSLCertificate; +import com.wolfssl.WolfSSLException; /** * wolfSSL implemenation of TrustManagerFactorySpi @@ -51,47 +57,177 @@ public class WolfSSLTrustManager extends TrustManagerFactorySpi { if (in == null) { String pass = System.getProperty("javax.net.ssl.trustStorePassword"); String file = System.getProperty("javax.net.ssl.trustStore"); + String type = System.getProperty("javax.net.ssl.trustStoreType"); + String vmVendor = System.getProperty("java.vm.vendor"); char passAr[] = null; InputStream stream = null; + boolean systemCertsFound = false; + int aliasCnt = 0; + String[] cafiles = null; try { if (pass != null) { passAr = pass.toCharArray(); } - certs = KeyStore.getInstance("JKS"); + + /* default to JKS KeyStore type if not set at system level */ + try { + if (type != null && type != "") { + certs = KeyStore.getInstance(type); + } else { + if (vmVendor.equals("The Android Project")) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Detected Android VM, using BKS KeyStore type"); + certs = KeyStore.getInstance("BKS"); + } else { + certs = KeyStore.getInstance("JKS"); + } + } + } catch (KeyStoreException kse) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.ERROR, + "Unsupported KeyStore type: " + type); + throw kse; + } + + try { + /* initialize KeyStore, loading certs below will + * overwrite if needed, otherwise Android needs + * this to be initialized here */ + certs.load(null, null); + + } catch (Exception e) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.ERROR, + "Error initializing KeyStore with load(null, null)"); + throw e; + } + if (file == null) { + /* try to load trusted system certs if possible */ String home = System.getenv("JAVA_HOME"); if (home != null) { + if (!home.endsWith("/") && !home.endsWith("\\")) { + /* add trailing slash if not there already */ + home = home.concat("/"); + } + + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "$JAVA_HOME = " + home); + + /* trying: "lib/security/jssecacerts" */ File f = new File(home.concat( - "lib/security/jssecacerts")); + "jre/lib/security/jssecacerts")); if (f.exists()) { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "Loading certs from " + home.concat("lib/security/jssecacerts")); stream = new FileInputStream(f); certs.load(stream, passAr); + stream.close(); + systemCertsFound = true; } - else { - f = new File(home.concat("lib/security/cacerts")); - if (f.exists()) { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "Loading certs from " + - home.concat("lib/security/cacerts")); - stream = new FileInputStream(f); - certs.load(stream, passAr); - } - else { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "Using Anonymous cipher suite"); - } + + /* trying: "lib/security/cacerts" */ + f = new File(home.concat("jre/lib/security/cacerts")); + if (f.exists()) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Loading certs from " + + home.concat("lib/security/cacerts")); + stream = new FileInputStream(f); + certs.load(stream, passAr); + stream.close(); + systemCertsFound = true; } } + + /* ANDROID, detect based on ANDROID_ROOT */ + home = System.getenv("ANDROID_ROOT"); + if (home != null) { + /* try: "/system/security/cacerts/*" + * this is a directory of individual PEM files */ + + if (!home.endsWith("/") && !home.endsWith("\\")) { + /* add trailing slash if not there already */ + home = home.concat("/"); + } + + String caStoreDir = home.concat("etc/security/cacerts"); + File cadir = new File(caStoreDir); + try { + cafiles = cadir.list(); + } catch (Exception e) { + /* denied access reading cacerts directory */ + WolfSSLDebug.log(getClass(), WolfSSLDebug.ERROR, + "Permission error when trying to read " + + "system CA certificates"); + throw e; + } + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Found " + cafiles.length + " CA files to load " + + "into KeyStore"); + + /* get factory for cert creation */ + CertificateFactory cfactory = + CertificateFactory.getInstance("X.509"); + + /* loop over all PEM certs */ + for (String cafile : cafiles) { + + WolfSSLCertificate certPem; + String fullCertPath = caStoreDir.concat("/"); + fullCertPath = fullCertPath.concat(cafile); + + try { + certPem = new WolfSSLCertificate( + fullCertPath, WolfSSL.SSL_FILETYPE_PEM); + } catch (WolfSSLException we) { + /* skip, error parsing PEM */ + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Skipped loading cert: " + fullCertPath); + continue; + } + + byte[] derArray = certPem.getDer(); + ByteArrayInputStream bis = + new ByteArrayInputStream(derArray); + Certificate tmpCert = null; + + try { + tmpCert = cfactory.generateCertificate(bis); + } catch (CertificateException ce) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.ERROR, + "Error generating certificate from " + + "ByteArrayInputStream"); + throw ce; + } + + String aliasString = "alias" + aliasCnt; + try { + certs.setCertificateEntry(aliasString, tmpCert); + } catch (KeyStoreException kse) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.ERROR, + "Error setting certificate entry in " + + "KeyStore, skipping loading cert"); + continue; + } + + /* increment alias counter for unique aliases */ + aliasCnt++; + } + systemCertsFound = true; + } + + if (systemCertsFound == false) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "No trusted system certs found, " + + "using Anonymous cipher suite"); + } } else { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "Loading certs from " + file); stream = new FileInputStream(file); certs.load(stream, passAr); + stream.close(); } } catch (FileNotFoundException ex) { Logger.getLogger( @@ -110,14 +246,6 @@ public class WolfSSLTrustManager extends TrustManagerFactorySpi { WolfSSLTrustManager.class.getName()).log( Level.SEVERE, null, ex); } - - if (stream != null) { - try { - stream.close(); - } catch (IOException e) { - throw new KeyStoreException("Unable to close stream"); - } - } } this.store = certs; } diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLTrustX509.java b/src/java/com/wolfssl/provider/jsse/WolfSSLTrustX509.java index 08e7636..7edd84e 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLTrustX509.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLTrustX509.java @@ -129,8 +129,13 @@ public class WolfSSLTrustX509 implements X509TrustManager { @Override public X509Certificate[] getAcceptedIssuers() { - if (CAs != null) + if (CAs != null) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "accepted issuer array size = " + CAs.size()); return CAs.toArray(new X509Certificate[CAs.size()]); + } + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "accepted issuer array is null"); return null; } } diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLX509.java b/src/java/com/wolfssl/provider/jsse/WolfSSLX509.java index 527f8df..9152cb9 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLX509.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLX509.java @@ -34,11 +34,14 @@ import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateExpiredException; import java.security.cert.CertificateNotYetValidException; +import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Date; import java.util.Set; import java.util.TreeSet; +import java.util.List; +import java.util.Collection; import com.wolfssl.WolfSSLCertificate; import com.wolfssl.WolfSSLException; @@ -187,6 +190,12 @@ public class WolfSSLX509 extends X509Certificate { return ret; } + @Override + public Collection> getSubjectAlternativeNames() + throws CertificateParsingException { + return this.cert.getSubjectAltNames(); + } + @Override public void verify(PublicKey key) throws CertificateException, NoSuchAlgorithmException, diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLTestFactory.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLTestFactory.java index 8453fec..066858f 100644 --- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLTestFactory.java +++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLTestFactory.java @@ -65,6 +65,7 @@ class WolfSSLTestFactory { protected String caJKS; protected String rsaJKS; protected String googleCACert; + protected String exampleComCert; protected final static char[] jksPass = "wolfSSL test".toCharArray(); protected String keyStoreType = "JKS"; private boolean extraDebug = false; @@ -77,6 +78,7 @@ class WolfSSLTestFactory { caJKS = "examples/provider/cacerts.jks"; rsaJKS = "examples/provider/rsa.jks"; googleCACert = "examples/certs/ca-google-root.der"; + exampleComCert = "examples/certs/example-com.der"; /* test if running from IDE directory */ File f = new File(serverJKS); @@ -104,6 +106,7 @@ class WolfSSLTestFactory { caJKS = in.concat(caJKS); rsaJKS = in.concat(rsaJKS); googleCACert = in.concat(googleCACert); + exampleComCert = in.concat(exampleComCert); } private boolean isIDEFile() { diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLX509Test.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLX509Test.java index 0dd8ac2..4d5792d 100644 --- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLX509Test.java +++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLX509Test.java @@ -43,6 +43,9 @@ import java.security.cert.CertificateNotYetValidException; import java.security.cert.X509Certificate; import java.util.Date; import java.util.Set; +import java.util.List; +import java.util.ArrayList; +import java.util.Collection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; @@ -545,6 +548,79 @@ public class WolfSSLX509Test { pass("\t\t... passed"); } + @Test + public void testSubjectAlternativeNames() { + + X509Certificate x509; + int ALT_DNS_NAME = 2; /* dNSName type */ + + System.out.print("\tTesting getting alt names"); + + /* populate known alt name list for example.com cert, for comparison */ + List expected = new ArrayList<>(); + expected.add("www.example.net"); + expected.add("www.example.edu"); + expected.add("www.example.com"); + expected.add("example.org"); + expected.add("example.net"); + expected.add("example.edu"); + expected.add("example.com"); + expected.add("www.example.org"); + + /* list to hold found altNames */ + List found = new ArrayList<>(); + + try { + x509 = new WolfSSLX509(tf.exampleComCert); + + Collection subjectAltNames = x509.getSubjectAlternativeNames(); + if (subjectAltNames == null) { + error("\t... failed"); + fail("subjectAltNames Collection was null"); + } + + for (Object subjectAltName : subjectAltNames) { + List entry = (List)subjectAltName; + if (entry == null || entry.size() < 2) { + error("\t... failed"); + fail("subjectAltName List null or length < 2"); + } + Integer altNameType = (Integer)entry.get(0); + if (altNameType == null) { + error("\t... failed"); + fail("subjectAltName List[0] was null, should be Integer"); + } + if (altNameType != ALT_DNS_NAME) { + error("\t... failed"); + fail("subjectAltName type is not ALT_DNS_NAME (2)"); + } + String altName = (String)entry.get(1); + if (altName == null) { + error("\t... failed"); + fail("Individual altName was null, should not be"); + } + found.add(altName); + } + + if (found.size() != expected.size()) { + error("\r... failed"); + fail("altName list size differs from expected size"); + } + + for (int i = 0; i < found.size(); i++) { + if (!found.get(i).equals(expected.get(i))) { + error("\r... failed"); + fail("altName entry does not match expected: found: " + + found.get(i) + ", expected: " + expected.get(i)); + } + } + + } catch (Exception ex) { + error("\t... failed"); + fail("unexpected exception found"); + } + pass("\t... passed"); + } private void pass(String msg) { WolfSSLTestFactory.pass(msg); diff --git a/src/test/com/wolfssl/test/WolfSSLCertificateTest.java b/src/test/com/wolfssl/test/WolfSSLCertificateTest.java index e34511f..e997b07 100644 --- a/src/test/com/wolfssl/test/WolfSSLCertificateTest.java +++ b/src/test/com/wolfssl/test/WolfSSLCertificateTest.java @@ -33,6 +33,7 @@ import java.util.logging.Logger; import static org.junit.Assert.fail; import org.junit.Test; +import com.wolfssl.WolfSSL; import com.wolfssl.WolfSSLCertificate; import com.wolfssl.WolfSSLException; @@ -44,7 +45,8 @@ public class WolfSSLCertificateTest { public final static int TEST_FAIL = -1; public final static int TEST_SUCCESS = 0; - public static String cliCert = "examples/certs/client-cert.der"; + public static String cliCertDer = "examples/certs/client-cert.der"; + public static String cliCertPem = "examples/certs/client-cert.pem"; public static String external = "examples/certs/ca-google-root.der"; public static String bogusFile = "/dev/null"; private WolfSSLCertificate cert; @@ -54,10 +56,29 @@ public class WolfSSLCertificateTest { System.out.println("WolfSSLCertificate Class"); - cliCert = WolfSSLTestCommon.getPath(cliCert); - external = WolfSSLTestCommon.getPath(external); - - test_WolfSSLCertificate_new(); + cliCertDer = WolfSSLTestCommon.getPath(cliCertDer); + cliCertPem = WolfSSLTestCommon.getPath(cliCertPem); + external = WolfSSLTestCommon.getPath(external); + + /* WolfSSLCertificate(byte[] der) */ + test_WolfSSLCertificate_new_derArray(); + test_runCertTestsAfterConstructor(); + + /* WolfSSLCertificate(String der) */ + test_WolfSSLCertificate_new_pemArray(); + test_runCertTestsAfterConstructor(); + + /* WolfSSLCertificate(byte[] pem) */ + test_WolfSSLCertificate_new_derFile(); + test_runCertTestsAfterConstructor(); + + /* WolfSSLCertificate(String pem) */ + test_WolfSSLCertificate_new_pemFile(); + test_runCertTestsAfterConstructor(); + } + + + public void test_runCertTestsAfterConstructor() { test_getSerial(); test_notBefore(); test_notAfter(); @@ -77,89 +98,154 @@ public class WolfSSLCertificateTest { test_toString(); test_free(); } - - - public void test_WolfSSLCertificate_new() { - File f = new File(cliCert); + + + public void test_WolfSSLCertificate_new_derArray() { + File f = new File(cliCertDer); byte[] der = null; - System.out.print("\tWolfSSLCertificate_new"); + System.out.print("\tnew(byte[] der)"); + try { InputStream stream = new FileInputStream(f); der = new byte[(int) f.length()]; stream.read(der, 0, der.length); stream.close(); } catch (IOException ex) { - System.out.println("\t\t... failed"); - fail("Unable to read file " + cliCert); - Logger.getLogger(WolfSSLCertificateTest.class.getName()).log(Level.SEVERE, null, ex); + System.out.println("\t\t\t... failed"); + fail("Unable to read file " + cliCertDer); + Logger.getLogger(WolfSSLCertificateTest.class.getName()).log( + Level.SEVERE, null, ex); } - try { this.cert = new WolfSSLCertificate(der); } catch (WolfSSLException ex) { - System.out.println("\t\t... failed"); + System.out.println("\t\t\t... failed"); fail("Unable to initialize class"); - Logger.getLogger(WolfSSLCertificateTest.class.getName()).log(Level.SEVERE, null, ex); + Logger.getLogger(WolfSSLCertificateTest.class.getName()).log( + Level.SEVERE, null, ex); } - System.out.println("\t\t... passed"); + System.out.println("\t\t\t... passed"); } - + + + public void test_WolfSSLCertificate_new_derFile() { + System.out.print("\tnew(String der, int format)"); + + try { + this.cert = new WolfSSLCertificate(cliCertDer, + WolfSSL.SSL_FILETYPE_ASN1); + } catch (WolfSSLException ex) { + System.out.println("\t... failed"); + fail("Unable to initialize class"); + Logger.getLogger(WolfSSLCertificateTest.class.getName()).log( + Level.SEVERE, null, ex); + } + System.out.println("\t... passed"); + } + + + public void test_WolfSSLCertificate_new_pemArray() { + File f = new File(cliCertPem); + byte[] pem = null; + + System.out.print("\tnew(byte[] in, int format)"); + + try { + InputStream stream = new FileInputStream(f); + pem = new byte[(int) f.length()]; + stream.read(pem, 0, pem.length); + stream.close(); + } catch (IOException ex) { + System.out.println("\t... failed"); + fail("Unable to read file " + cliCertPem); + Logger.getLogger(WolfSSLCertificateTest.class.getName()).log( + Level.SEVERE, null, ex); + } + + try { + this.cert = new WolfSSLCertificate(pem, WolfSSL.SSL_FILETYPE_PEM); + } catch (WolfSSLException ex) { + System.out.println("\t... failed"); + fail("Unable to initialize class"); + Logger.getLogger(WolfSSLCertificateTest.class.getName()).log( + Level.SEVERE, null, ex); + } + System.out.println("\t... passed"); + } + + + public void test_WolfSSLCertificate_new_pemFile() { + System.out.print("\tnew(String pem, int format)"); + + try { + this.cert = new WolfSSLCertificate(cliCertPem, + WolfSSL.SSL_FILETYPE_PEM); + } catch (WolfSSLException ex) { + System.out.println("\t... failed"); + fail("Unable to initialize class"); + Logger.getLogger(WolfSSLCertificateTest.class.getName()).log( + Level.SEVERE, null, ex); + } + System.out.println("\t... passed"); + } + + public void test_getSerial() { byte[] expected = new byte[]{(byte)0xaa, (byte)0xc4, (byte)0xbf, (byte)0x4c, (byte)0x50, (byte)0xbd, (byte)0x55, (byte)0x77}; byte[] serial; int i; BigInteger bigi = cert.getSerial(); - - System.out.print("\tgetSerial"); + + System.out.print("\t\tgetSerial"); serial = bigi.toByteArray(); for (i = 0; i < serial.length && i < expected.length; i++) { if (serial[i] != expected[i]) { - System.out.println("\t\t\t... failed"); + System.out.println("\t\t... failed"); fail("Unexpected serial number"); } } - System.out.println("\t\t\t... passed"); + System.out.println("\t\t... passed"); } - + @SuppressWarnings("deprecation") public void test_notBefore() { Date date = cert.notBefore(); Date expected = new Date("Fri Apr 13 09:23:09 MDT 2018"); - System.out.print("\tnotBefore"); + System.out.print("\t\tnotBefore"); if (date.compareTo(expected) != 0) { - System.out.println("\t\t\t... failed"); + System.out.println("\t\t... failed"); fail("Unexpected not before date"); } - System.out.println("\t\t\t... passed"); + System.out.println("\t\t... passed"); } - - + + @SuppressWarnings("deprecation") public void test_notAfter() { Date date = cert.notAfter(); Date expected = new Date("Thu Jan 07 08:23:09 MST 2021"); - System.out.print("\tnotAfter"); + System.out.print("\t\tnotAfter"); if (date.compareTo(expected) != 0) { - System.out.println("\t\t\t... failed"); + System.out.println("\t\t... failed"); fail("Unexpected not after date"); } - System.out.println("\t\t\t... passed"); + System.out.println("\t\t... passed"); } - + public void test_getVersion() { int version = cert.getVersion(); - - System.out.print("\tgetVersion"); + + System.out.print("\t\tgetVersion"); if (version != 3) { - System.out.println("\t\t\t... failed"); + System.out.println("\t\t... failed"); fail("Unexpected version number"); } - System.out.println("\t\t\t... passed"); + System.out.println("\t\t... passed"); } - + public void test_getSignature() { byte[] sig = cert.getSignature(); byte[] expected = new byte[] { @@ -217,47 +303,47 @@ public class WolfSSLCertificateTest { (byte)0xB4 }; int i; - System.out.print("\tgetSignature"); + System.out.print("\t\tgetSignature"); for (i = 0; i < sig.length && i < expected.length; i++) { if (sig[i] != expected[i]) { - System.out.println("\t\t\t... failed"); - fail("Unexpected signature"); + System.out.println("\t\t... failed"); + fail("Unexpected signature"); } } + System.out.println("\t\t... passed"); + } + + public void test_isCA() { + System.out.print("\t\tisCA"); + if (this.cert.isCA() != 1) { + System.out.println("\t\t\t... failed"); + fail("Expected isCA to be set"); + } System.out.println("\t\t\t... passed"); } - - public void test_isCA() { - System.out.print("\tisCA"); - if (this.cert.isCA() != 1) { - System.out.println("\t\t\t\t... failed"); - fail("Expected isCA to be set"); - } - System.out.println("\t\t\t\t... passed"); - } - + public void test_getSubject() { String expected = "/C=US/ST=Montana/L=Bozeman/O=wolfSSL_2048/OU=Programming-2048/CN=www.wolfssl.com/emailAddress=info@wolfssl.com"; - - System.out.print("\tgetSubject"); + + System.out.print("\t\tgetSubject"); if (!cert.getSubject().equals(expected)) { - System.out.println("\t\t\t... failed"); - fail("Unexpected subject"); + System.out.println("\t\t... failed"); + fail("Unexpected subject"); } - System.out.println("\t\t\t... passed"); + System.out.println("\t\t... passed"); } - + public void test_getIssuer() { String expected = "/C=US/ST=Montana/L=Bozeman/O=wolfSSL_2048/OU=Programming-2048/CN=www.wolfssl.com/emailAddress=info@wolfssl.com"; - - System.out.print("\tgetIssuer"); + + System.out.print("\t\tgetIssuer"); if (!cert.getIssuer().equals(expected)) { - System.out.println("\t\t\t... failed"); - fail("Unexpected issuer"); + System.out.println("\t\t... failed"); + fail("Unexpected issuer"); } - System.out.println("\t\t\t... passed"); + System.out.println("\t\t... passed"); } - + public void test_getPubkey() { byte[] expected = new byte[] { (byte)0x30, (byte)0x82, (byte)0x01, (byte)0x22, @@ -331,147 +417,147 @@ public class WolfSSLCertificateTest { }; int i; byte[] pub; - - System.out.print("\tgetPubkey"); + + System.out.print("\t\tgetPubkey"); pub = cert.getPubkey(); for (i = 0; i < pub.length && i < expected.length; i++) { if (pub[i] != expected[i]) { - System.out.println("\t\t\t... failed"); + System.out.println("\t\t... failed"); fail("Unexpected public key value"); } } - System.out.println("\t\t\t... passed"); + System.out.println("\t\t... passed"); } - + public void test_getPubkeyType() { String expected = "RSA"; - System.out.print("\tgetPubkeyType"); + System.out.print("\t\tgetPubkeyType"); if (!expected.equals(this.cert.getPubkeyType())) { - System.out.println("\t\t\t... failed"); - fail("Unexpected public key type value"); - } - System.out.println("\t\t\t... passed"); - } - - public void test_getPathLen() { - int expected = -1; - System.out.print("\tgetPathLen"); - if (this.cert.getPathLen() != expected) { - System.out.println("\t\t\t... failed"); - fail("Unexpected path length value"); - } - System.out.println("\t\t\t... passed"); - } - - public void test_getSignatureType() { - String expected = "SHA256withRSA"; - System.out.print("\tgetSignatureType"); - if (!expected.equals(this.cert.getSignatureType())) { System.out.println("\t\t... failed"); - fail("Unexpected signature type"); + fail("Unexpected public key type value"); } System.out.println("\t\t... passed"); } - + + public void test_getPathLen() { + int expected = -1; + System.out.print("\t\tgetPathLen"); + if (this.cert.getPathLen() != expected) { + System.out.println("\t\t... failed"); + fail("Unexpected path length value"); + } + System.out.println("\t\t... passed"); + } + + public void test_getSignatureType() { + String expected = "SHA256withRSA"; + System.out.print("\t\tgetSignatureType"); + if (!expected.equals(this.cert.getSignatureType())) { + System.out.println("\t... failed"); + fail("Unexpected signature type"); + } + System.out.println("\t... passed"); + } + public void test_verify() { byte[] pubkey; - - System.out.print("\tverify"); + + System.out.print("\t\tverify"); pubkey = this.cert.getPubkey(); if (pubkey == null) { - System.out.println("\t\t\t\t... failed"); + System.out.println("\t\t\t... failed"); fail("Could not get public key"); return; } - + if (this.cert.verify(pubkey, pubkey.length) != true) { - System.out.println("\t\t\t\t... failed"); - fail("Verify signature failed"); + System.out.println("\t\t\t... failed"); + fail("Verify signature failed"); } - System.out.println("\t\t\t\t... passed"); + System.out.println("\t\t\t... passed"); } - + public void test_getSignatureOID() { - System.out.print("\tgetSignatureOID"); - + System.out.print("\t\tgetSignatureOID"); + /* make sure is sha256WithRSAEncryption OID */ if (!this.cert.getSignatureOID().equals("1.2.840.113549.1.1.11")) { - System.out.println("\t\t\t... failed"); - fail("Could not get public key"); + System.out.println("\t\t... failed"); + fail("Could not get public key"); } - System.out.println("\t\t\t... passed"); + System.out.println("\t\t... passed"); } - + public void test_getKeyUsage() { WolfSSLCertificate ext; boolean[] expected = { false, false, false, false, false, true, true, false, false }; - - System.out.print("\tgetKeyUsage"); + + System.out.print("\t\tgetKeyUsage"); if (this.cert.getKeyUsage() != null) { - System.out.println("\t\t\t... failed"); - fail("Found key usage extension when not expecting any"); + System.out.println("\t\t... failed"); + fail("Found key usage extension when not expecting any"); } - + /* test with certificate that has key usage extension */ try { int i; boolean[] kuse; - + ext = new WolfSSLCertificate(this.external); kuse = ext.getKeyUsage(); if (kuse == null) { - System.out.println("\t\t\t... failed"); + System.out.println("\t\t... failed"); fail("Did not find key usage extension"); return; } - + for (i = 0; i < kuse.length; i++) { if (kuse[i] != expected[i]) { - System.out.println("\t\t\t... failed"); - fail("Found wrong key usage extension"); + System.out.println("\t\t... failed"); + fail("Found wrong key usage extension"); } } ext.free(); } catch (WolfSSLException ex) { Logger.getLogger(WolfSSLCertificateTest.class.getName()).log(Level.SEVERE, null, ex); - System.out.println("\t\t\t... failed"); - fail("Error loading external certificate"); + System.out.println("\t\t... failed"); + fail("Error loading external certificate"); } - System.out.println("\t\t\t... passed"); + System.out.println("\t\t... passed"); } - + public void test_getExtensionSet() { - System.out.print("\tgetExtensionSet"); - + System.out.print("\t\tgetExtensionSet"); + if (this.cert.getExtensionSet("2.5.29.19") != 1) { - System.out.println("\t\t\t... failed"); + System.out.println("\t\t... failed"); fail("Error with basic constraint extension"); } - + if (this.cert.getExtensionSet("2.5.29.14") != 1) { - System.out.println("\t\t\t... failed"); + System.out.println("\t\t... failed"); fail("Error with subject key ID extension"); } - System.out.println("\t\t\t... passed"); + System.out.println("\t\t... passed"); } - + public void test_toString() { String s; - System.out.print("\ttoString"); + System.out.print("\t\ttoString"); s = cert.toString(); if (s == null) { - System.out.println("\t\t\t... failed"); + System.out.println("\t\t... failed"); fail("Error getting certificate string"); } + System.out.println("\t\t... passed"); + } + + public void test_free() { + System.out.print("\t\tfree"); + this.cert.free(); System.out.println("\t\t\t... passed"); } - - public void test_free() { - System.out.print("\tfree"); - this.cert.free(); - System.out.println("\t\t\t\t... passed"); - } }