diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLX509.java b/src/java/com/wolfssl/provider/jsse/WolfSSLX509.java index c375af7..52d62d1 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLX509.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLX509.java @@ -698,7 +698,7 @@ public class WolfSSLX509 extends X509Certificate { 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 @@ public class WolfSSLX509 extends X509Certificate { 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 list = new ArrayList(); - if (in == null) { return null; } - ret = in.split("/"); - - while (tmp.length() > 3) { - for (i = tmp.length() - 3; i >= 0; i--) { - if ((j = containsDN(in.substring(i))) >= 0) { - String current = tmp.substring(i, tmp.length()); - current = current.replaceAll(DNs[j], - getReplace(DNs[j])); - list.add(current); - tmp = tmp.substring(0, i); - break; - } + /* Split before each '/' followed by a "TAG=" attribute type, + * then reverse the RDN order. wolfSSL does not escape '/', so + * '/' inside a value followed by "TAG=" will split too. + * Unrecognized RDN types pass through unchanged. */ + String[] parts = in.split("/(?=[A-Za-z0-9.]+=)"); + ArrayList list = new ArrayList(); + for (int i = 0; i < parts.length; i++) { + if (!parts[i].isEmpty()) { + list.add(mapComponent(parts[i])); } } - ret = list.toArray(new String[list.size()]); - tmp = ""; - for (i = 0; i < ret.length - 1; i++) { - tmp = tmp.concat(ret[i]); - tmp = tmp.concat(", "); + StringBuilder sb = new StringBuilder(); + for (int i = list.size() - 1; i >= 0; i--) { + sb.append(list.get(i)); + if (i > 0) { + sb.append(", "); + } } - tmp = tmp.concat(ret[i]); - return tmp; + return sb.toString(); } private WolfSSLPrincipal(String in) { diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLX509Test.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLX509Test.java index e334ac5..2f0e441 100644 --- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLX509Test.java +++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLX509Test.java @@ -49,6 +49,7 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateExpiredException; import java.security.cert.CertificateNotYetValidException; import java.security.cert.X509Certificate; +import java.util.Base64; import java.util.Date; import java.util.Set; import java.util.List; @@ -116,6 +117,62 @@ public class WolfSSLX509Test { subjectDN = x509.getSubjectDN(); } + /* Self-signed cert whose subject and issuer use DC (domainComponent) + * RDNs, an attribute type outside the reformat tag table. + * DN: DC=com, DC=example, CN=test.example.com */ + private static final String DC_RDN_CERT_DER = + "MIIDczCCAlugAwIBAgIUJvqgr93mm9aonQB+pyzrjyu7TBowDQYJKoZIhvcN" + + "AQELBQAwSTETMBEGCgmSJomT8ixkARkWA2NvbTEXMBUGCgmSJomT8ixkARkW" + + "B2V4YW1wbGUxGTAXBgNVBAMMEHRlc3QuZXhhbXBsZS5jb20wHhcNMjYwODI0" + + "MjA0NjUyWhcNMzYwODIxMjA0NjUyWjBJMRMwEQYKCZImiZPyLGQBGRYDY29t" + + "MRcwFQYKCZImiZPyLGQBGRYHZXhhbXBsZTEZMBcGA1UEAwwQdGVzdC5leGFt" + + "cGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALAo9F5Q" + + "xsEOHMFK0sNiHi5HUscCQWtFxA5AlH9mqfgSAYbEpm9YmHbvgBH2Mvc5vEZY" + + "hB1CRQT7pipS4iXUG+7cnz7M8YyKUuh5TtQTIARaPu2ZpTHOxQ2QMip/eHsI" + + "l8IggiVZZuJcnXHj1nrB8pcx4wreXyi+7r7INViffNV61bGOes6ftIH/sptD" + + "Sw0CXQUf1KfiM1rXaXw8cezYmmJWFoie0R6tbrMEfTRAKb1j4IEqWa3e7Krq" + + "+bZEmiw5kfP2vlEW2w+TylNWOm7uQu742VAJnIqgZ3nnVPxqd/5Ef5E8sMmI" + + "Xy/F+rhEFxZtmAiDnBflmPypXSp0w5c01P0CAwEAAaNTMFEwHQYDVR0OBBYE" + + "FPHoxpJFtgM3/ntZjdyu3Zy2CtSnMB8GA1UdIwQYMBaAFPHoxpJFtgM3/ntZ" + + "jdyu3Zy2CtSnMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB" + + "AJvFrnuTfU9MNTRCJGM3JAyex5RK3g5/SOs6uBjM8A1oM5NzTuZrjQzFWMtd" + + "srS8HMIoatKOxGYJUU1OHy4JpfF6Rfv38+q6QuugemIGmDjBEqmO100ZoYgF" + + "ppujEABELIyMpvqp48e6U8uo1mRoK8BWp4KeZiPo7jzi32HoDkeu+ZUNvn1G" + + "0DwgDiYl5ailSofBKplprpI77hx11fypekatWqsfR01Q/0OEkSsj/gO50em1" + + "VAMUYB1v8WXAivovfkUjx+J8xaund6Tx595jTHMOpQMckM/4C2ecvmixEKrF" + + "uz8gim9WCP+Pe6WZI948Kia+btaslCf58Y6/qrgJTJE="; + + @Test + public void testGetSubjectIssuerDNUnrecognizedRdnTerminates() + throws Exception { + + Assume.assumeTrue(WolfSSL.RsaEnabled()); + + byte[] der = Base64.getDecoder().decode(DC_RDN_CERT_DER); + final WolfSSLX509 cert = new WolfSSLX509(der); + + /* Reversed DN, matching getSubjectX500Principal() output. */ + final String expected = "CN=test.example.com, DC=example, DC=com"; + final String[] result = new String[2]; + + /* Run in a worker thread so a non-terminating reformat shows up as + * a still-alive thread instead of hanging the whole test run. */ + Thread worker = new Thread(new Runnable() { + public void run() { + result[0] = cert.getSubjectDN().getName(); + result[1] = cert.getIssuerDN().getName(); + } + }); + worker.setDaemon(true); + worker.start(); + worker.join(5000); + + assertFalse("getSubjectDN/getIssuerDN did not terminate on an " + + "unrecognized RDN type", worker.isAlive()); + assertEquals(expected, result[0]); + assertEquals(expected, result[1]); + } + @Test public void testServerParsing() { try {