You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by co...@apache.org on 2014/12/17 17:41:20 UTC

[1/4] cxf git commit: Fixing failing test

Repository: cxf
Updated Branches:
  refs/heads/master 5b017782e -> 593525139


Fixing failing test


Project: http://git-wip-us.apache.org/repos/asf/cxf/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/59352513
Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/59352513
Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/59352513

Branch: refs/heads/master
Commit: 593525139909e081ae809278d301688c0ba03697
Parents: fb9d265
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Wed Dec 17 16:40:24 2014 +0000
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Wed Dec 17 16:40:34 2014 +0000

----------------------------------------------------------------------
 .../security/AllowAllHostnameVerifier.java      | 47 ++++++++++++++++++++
 .../jaxrs/security/JAXRS20HttpsBookTest.java    |  5 +--
 2 files changed, 49 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf/blob/59352513/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/AllowAllHostnameVerifier.java
----------------------------------------------------------------------
diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/AllowAllHostnameVerifier.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/AllowAllHostnameVerifier.java
new file mode 100644
index 0000000..f440240
--- /dev/null
+++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/AllowAllHostnameVerifier.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ 
+package org.apache.cxf.systest.jaxrs.security;
+
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+
+/**
+ * Allow all hostnames. This is only suitable for use in testing, and NOT in production! 
+ */
+class AllowAllHostnameVerifier implements javax.net.ssl.HostnameVerifier {
+
+    @Override
+    public boolean verify(String host, SSLSession session) {
+        try {
+            Certificate[] certs = session.getPeerCertificates();
+            if (certs != null && certs[0] instanceof X509Certificate) {
+                return true;
+            }
+            return false;
+        } catch (SSLException e) {
+            return false;
+        }
+    }
+
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cxf/blob/59352513/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/JAXRS20HttpsBookTest.java
----------------------------------------------------------------------
diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/JAXRS20HttpsBookTest.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/JAXRS20HttpsBookTest.java
index 87fdd62..818749c 100644
--- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/JAXRS20HttpsBookTest.java
+++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/JAXRS20HttpsBookTest.java
@@ -33,7 +33,6 @@ import javax.ws.rs.core.MediaType;
 import org.apache.cxf.configuration.jsse.TLSClientParameters;
 import org.apache.cxf.systest.jaxrs.Book;
 import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
-import org.apache.cxf.transport.https.CertificateHostnameVerifier;
 import org.apache.cxf.transport.https.SSLUtils;
 
 import org.junit.BeforeClass;
@@ -58,7 +57,7 @@ public class JAXRS20HttpsBookTest extends AbstractBusClientServerTestBase {
                                        "password");
         
         builder.trustStore(trustStore);
-        builder.hostnameVerifier(CertificateHostnameVerifier.ALLOW_ALL);
+        builder.hostnameVerifier(new AllowAllHostnameVerifier());
         
         KeyStore keyStore = loadStore("src/test/java/org/apache/cxf/systest/http/resources/Morpit.jks",
             "password");
@@ -79,7 +78,7 @@ public class JAXRS20HttpsBookTest extends AbstractBusClientServerTestBase {
         SSLContext sslContext = createSSLContext();
         builder.sslContext(sslContext);
         
-        builder.hostnameVerifier(CertificateHostnameVerifier.ALLOW_ALL);
+        builder.hostnameVerifier(new AllowAllHostnameVerifier());
         
         
         Client client = builder.build();


[3/4] cxf git commit: [CXF-6143] - SSL/TLS hostname verification does not strictly follow HTTPS RFC2818 - Code ported from httpclient

Posted by co...@apache.org.
http://git-wip-us.apache.org/repos/asf/cxf/blob/90430115/rt/transports/http/src/test/java/org/apache/cxf/transport/https/httpclient/DefaultHostnameVerifierTest.java
----------------------------------------------------------------------
diff --git a/rt/transports/http/src/test/java/org/apache/cxf/transport/https/httpclient/DefaultHostnameVerifierTest.java b/rt/transports/http/src/test/java/org/apache/cxf/transport/https/httpclient/DefaultHostnameVerifierTest.java
new file mode 100644
index 0000000..da284e8
--- /dev/null
+++ b/rt/transports/http/src/test/java/org/apache/cxf/transport/https/httpclient/DefaultHostnameVerifierTest.java
@@ -0,0 +1,301 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.cxf.transport.https.httpclient;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+
+import javax.net.ssl.SSLException;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Unit tests for the DefaultHostnameVerifier
+ * 
+ * Copied from httpclient
+ */
+public class DefaultHostnameVerifierTest {
+
+    private DefaultHostnameVerifier impl;
+    private PublicSuffixMatcher publicSuffixMatcher;
+    private DefaultHostnameVerifier implWithPublicSuffixCheck;
+
+    @Before
+    public void setUp() {
+        impl = new DefaultHostnameVerifier();
+        publicSuffixMatcher = new PublicSuffixMatcher(Arrays.asList("com", "co.jp", "gov.uk"), null);
+        implWithPublicSuffixCheck = new DefaultHostnameVerifier(publicSuffixMatcher);
+    }
+
+    @Test
+    public void testVerify() throws Exception {
+        final CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        InputStream in;
+        X509Certificate x509;
+        in = new ByteArrayInputStream(CertificatesToPlayWith.X509_FOO);
+        x509 = (X509Certificate) cf.generateCertificate(in);
+
+        impl.verify("foo.com", x509);
+        exceptionPlease(impl, "a.foo.com", x509);
+        exceptionPlease(impl, "bar.com", x509);
+
+        in = new ByteArrayInputStream(CertificatesToPlayWith.X509_HANAKO);
+        x509 = (X509Certificate) cf.generateCertificate(in);
+        impl.verify("\u82b1\u5b50.co.jp", x509);
+        exceptionPlease(impl, "a.\u82b1\u5b50.co.jp", x509);
+
+        in = new ByteArrayInputStream(CertificatesToPlayWith.X509_FOO_BAR);
+        x509 = (X509Certificate) cf.generateCertificate(in);
+        exceptionPlease(impl, "foo.com", x509);
+        exceptionPlease(impl, "a.foo.com", x509);
+        impl.verify("bar.com", x509);
+        exceptionPlease(impl, "a.bar.com", x509);
+
+        in = new ByteArrayInputStream(CertificatesToPlayWith.X509_FOO_BAR_HANAKO);
+        x509 = (X509Certificate) cf.generateCertificate(in);
+        exceptionPlease(impl, "foo.com", x509);
+        exceptionPlease(impl, "a.foo.com", x509);
+        impl.verify("bar.com", x509);
+        exceptionPlease(impl, "a.bar.com", x509);
+
+        /*
+           Java isn't extracting international subjectAlts properly.  (Or
+           OpenSSL isn't storing them properly).
+        */
+        // DEFAULT.verify("\u82b1\u5b50.co.jp", x509 );
+        // impl.verify("\u82b1\u5b50.co.jp", x509 );
+        exceptionPlease(impl, "a.\u82b1\u5b50.co.jp", x509);
+
+        in = new ByteArrayInputStream(CertificatesToPlayWith.X509_NO_CNS_FOO);
+        x509 = (X509Certificate) cf.generateCertificate(in);
+        impl.verify("foo.com", x509);
+        exceptionPlease(impl, "a.foo.com", x509);
+
+        in = new ByteArrayInputStream(CertificatesToPlayWith.X509_NO_CNS_FOO);
+        x509 = (X509Certificate) cf.generateCertificate(in);
+        impl.verify("foo.com", x509);
+        exceptionPlease(impl, "a.foo.com", x509);
+
+        in = new ByteArrayInputStream(CertificatesToPlayWith.X509_THREE_CNS_FOO_BAR_HANAKO);
+        x509 = (X509Certificate) cf.generateCertificate(in);
+        exceptionPlease(impl, "foo.com", x509);
+        exceptionPlease(impl, "a.foo.com", x509);
+        exceptionPlease(impl, "bar.com", x509);
+        exceptionPlease(impl, "a.bar.com", x509);
+        impl.verify("\u82b1\u5b50.co.jp", x509);
+        exceptionPlease(impl, "a.\u82b1\u5b50.co.jp", x509);
+
+        in = new ByteArrayInputStream(CertificatesToPlayWith.X509_WILD_FOO);
+        x509 = (X509Certificate) cf.generateCertificate(in);
+        exceptionPlease(impl, "foo.com", x509);
+        impl.verify("www.foo.com", x509);
+        impl.verify("\u82b1\u5b50.foo.com", x509);
+        exceptionPlease(impl, "a.b.foo.com", x509);
+
+        in = new ByteArrayInputStream(CertificatesToPlayWith.X509_WILD_CO_JP);
+        x509 = (X509Certificate) cf.generateCertificate(in);
+        // Silly test because no-one would ever be able to lookup an IP address
+        // using "*.co.jp".
+        impl.verify("*.co.jp", x509);
+        impl.verify("foo.co.jp", x509);
+        impl.verify("\u82b1\u5b50.co.jp", x509);
+
+        exceptionPlease(implWithPublicSuffixCheck, "foo.co.jp", x509);
+        exceptionPlease(implWithPublicSuffixCheck, "\u82b1\u5b50.co.jp", x509);
+
+        in = new ByteArrayInputStream(CertificatesToPlayWith.X509_WILD_FOO_BAR_HANAKO);
+        x509 = (X509Certificate) cf.generateCertificate(in);
+        // try the foo.com variations
+        exceptionPlease(impl, "foo.com", x509);
+        exceptionPlease(impl, "www.foo.com", x509);
+        exceptionPlease(impl, "\u82b1\u5b50.foo.com", x509);
+        exceptionPlease(impl, "a.b.foo.com", x509);
+        // try the bar.com variations
+        exceptionPlease(impl, "bar.com", x509);
+        impl.verify("www.bar.com", x509);
+        impl.verify("\u82b1\u5b50.bar.com", x509);
+        exceptionPlease(impl, "a.b.bar.com", x509);
+
+        in = new ByteArrayInputStream(CertificatesToPlayWith.X509_MULTIPLE_VALUE_AVA);
+        x509 = (X509Certificate) cf.generateCertificate(in);
+        impl.verify("repository.infonotary.com", x509);
+    }
+
+    @Test
+    public void testSubjectAlt() throws Exception {
+        final CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        final InputStream in = new ByteArrayInputStream(CertificatesToPlayWith.X509_MULTIPLE_SUBJECT_ALT);
+        final X509Certificate x509 = (X509Certificate) cf.generateCertificate(in);
+
+        Assert.assertEquals("CN=localhost, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=CH",
+                x509.getSubjectDN().getName());
+
+        impl.verify("localhost.localdomain", x509);
+        impl.verify("127.0.0.1", x509);
+
+        try {
+            impl.verify("localhost", x509);
+            Assert.fail("SSLException should have been thrown");
+        } catch (final SSLException ex) {
+            // expected
+        }
+        try {
+            impl.verify("local.host", x509);
+            Assert.fail("SSLException should have been thrown");
+        } catch (final SSLException ex) {
+            // expected
+        }
+        try {
+            impl.verify("127.0.0.2", x509);
+            Assert.fail("SSLException should have been thrown");
+        } catch (final SSLException ex) {
+            // expected
+        }
+    }
+
+    public void exceptionPlease(final DefaultHostnameVerifier hv, final String host,
+                                final X509Certificate x509) {
+        try {
+            hv.verify(host, x509);
+            Assert.fail("HostnameVerifier shouldn't allow [" + host + "]");
+        } catch (final SSLException e) {
+            // whew!  we're okay!
+        }
+    }
+
+    @Test
+    public void testIdentityMatching() {
+
+        Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("a.b.c", "*.b.c"));
+        Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.b.c", "*.b.c"));
+
+        Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("s.a.b.c", "*.b.c"));
+        Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("s.a.b.c", "*.b.c")); // subdomain not OK
+
+        Assert.assertFalse(DefaultHostnameVerifier.matchIdentity("a.gov.uk", "*.gov.uk", publicSuffixMatcher));
+        Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict(
+            "a.gov.uk", "*.gov.uk", publicSuffixMatcher));  // Bad 2TLD
+
+        Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("s.a.gov.uk", "*.a.gov.uk", publicSuffixMatcher));
+        Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("s.a.gov.uk", "*.a.gov.uk", publicSuffixMatcher));
+
+        Assert.assertFalse(DefaultHostnameVerifier.matchIdentity(
+            "s.a.gov.uk", "*.gov.uk", publicSuffixMatcher));
+        Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict(
+            "s.a.gov.uk", "*.gov.uk", publicSuffixMatcher));  // BBad 2TLD/no subdomain allowed
+
+        Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("a.gov.com", "*.gov.com", publicSuffixMatcher));
+        Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.gov.com", "*.gov.com", publicSuffixMatcher));
+
+        Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("s.a.gov.com", "*.gov.com", publicSuffixMatcher));
+        Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict(
+            "s.a.gov.com", "*.gov.com", publicSuffixMatcher)); // no subdomain allowed
+
+        Assert.assertFalse(DefaultHostnameVerifier.matchIdentity("a.gov.uk", "a*.gov.uk", publicSuffixMatcher));
+        Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict(
+            "a.gov.uk", "a*.gov.uk", publicSuffixMatcher)); // Bad 2TLD
+
+        Assert.assertFalse(DefaultHostnameVerifier.matchIdentity(
+            "s.a.gov.uk", "a*.gov.uk", publicSuffixMatcher)); // Bad 2TLD
+        Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict(
+            "s.a.gov.uk", "a*.gov.uk", publicSuffixMatcher)); // Bad 2TLD/no subdomain allowed
+
+        Assert.assertFalse(DefaultHostnameVerifier.matchIdentity("a.b.c", "*.b.*"));
+        Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("a.b.c", "*.b.*"));
+
+        Assert.assertFalse(DefaultHostnameVerifier.matchIdentity("a.b.c", "*.*.c"));
+        Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("a.b.c", "*.*.c"));
+    }
+
+    @Test
+    public void testHttpClient1097() {
+        Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("a.b.c", "a*.b.c"));
+        Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.b.c", "a*.b.c"));
+
+        Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("a.a.b.c", "a*.b.c"));
+        Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("a.a.b.c", "a*.b.c"));
+    }
+
+    @Test
+    public void testHttpClient1255() {
+        Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("mail.a.b.c.com", "m*.a.b.c.com"));
+        Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("mail.a.b.c.com", "m*.a.b.c.com"));
+    }
+
+    @Test // Check compressed IPv6 hostname matching
+    public void testHttpClient1316() throws Exception {
+        final String host1 = "2001:0db8:aaaa:bbbb:cccc:0:0:0001";
+        DefaultHostnameVerifier.matchIPv6Address(host1, Arrays.asList("2001:0db8:aaaa:bbbb:cccc:0:0:0001"));
+        DefaultHostnameVerifier.matchIPv6Address(host1, Arrays.asList("2001:0db8:aaaa:bbbb:cccc::1"));
+        try {
+            DefaultHostnameVerifier.matchIPv6Address(host1, Arrays.asList("2001:0db8:aaaa:bbbb:cccc::10"));
+            Assert.fail("SSLException expected");
+        } catch (SSLException expected) {
+            //
+        }
+        final String host2 = "2001:0db8:aaaa:bbbb:cccc::1";
+        DefaultHostnameVerifier.matchIPv6Address(host2, Arrays.asList("2001:0db8:aaaa:bbbb:cccc:0:0:0001"));
+        DefaultHostnameVerifier.matchIPv6Address(host2, Arrays.asList("2001:0db8:aaaa:bbbb:cccc::1"));
+        try {
+            DefaultHostnameVerifier.matchIPv6Address(host2, Arrays.asList("2001:0db8:aaaa:bbbb:cccc::10"));
+            Assert.fail("SSLException expected");
+        } catch (SSLException expected) {
+            //
+        }
+    }
+
+    @Test
+    public void testExtractCN() throws Exception {
+        Assert.assertEquals("blah", DefaultHostnameVerifier.extractCN("cn=blah, ou=blah, o=blah"));
+        Assert.assertEquals("blah", DefaultHostnameVerifier.extractCN("cn=blah, cn=yada, cn=booh"));
+        Assert.assertEquals("blah", DefaultHostnameVerifier.extractCN(
+            "c = pampa ,  cn  =    blah    , ou = blah , o = blah"));
+        Assert.assertEquals("blah", DefaultHostnameVerifier.extractCN("cn=\"blah\", ou=blah, o=blah"));
+        Assert.assertEquals("blah  blah", DefaultHostnameVerifier.extractCN("cn=\"blah  blah\", ou=blah, o=blah"));
+        Assert.assertEquals("blah, blah", DefaultHostnameVerifier.extractCN("cn=\"blah, blah\", ou=blah, o=blah"));
+        Assert.assertEquals("blah, blah", DefaultHostnameVerifier.extractCN("cn=blah\\, blah, ou=blah, o=blah"));
+        Assert.assertEquals("blah", DefaultHostnameVerifier.extractCN("c = cn=uuh, cn=blah, ou=blah, o=blah"));
+        try {
+            DefaultHostnameVerifier.extractCN("blah,blah");
+            Assert.fail("SSLException expected");
+        } catch (SSLException expected) {
+            //
+        }
+        try {
+            DefaultHostnameVerifier.extractCN("cn,o=blah");
+            Assert.fail("SSLException expected");
+        } catch (SSLException expected) {
+            //
+        }
+    }
+
+}


[2/4] cxf git commit: Removing CertificateHostnameVerifier on master

Posted by co...@apache.org.
Removing CertificateHostnameVerifier on master


Project: http://git-wip-us.apache.org/repos/asf/cxf/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/fb9d2658
Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/fb9d2658
Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/fb9d2658

Branch: refs/heads/master
Commit: fb9d26581c364c62c7343847ebe4136b8c97700f
Parents: 9043011
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Wed Dec 17 15:50:34 2014 +0000
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Wed Dec 17 16:40:34 2014 +0000

----------------------------------------------------------------------
 .../https/CertificateHostnameVerifier.java      | 581 -------------------
 1 file changed, 581 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf/blob/fb9d2658/rt/transports/http/src/main/java/org/apache/cxf/transport/https/CertificateHostnameVerifier.java
----------------------------------------------------------------------
diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/https/CertificateHostnameVerifier.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/CertificateHostnameVerifier.java
deleted file mode 100644
index d9f543b..0000000
--- a/rt/transports/http/src/main/java/org/apache/cxf/transport/https/CertificateHostnameVerifier.java
+++ /dev/null
@@ -1,581 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
- 
-package org.apache.cxf.transport.https;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import java.security.cert.Certificate;
-import java.security.cert.CertificateParsingException;
-import java.security.cert.X509Certificate;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.Set;
-import java.util.TreeSet;
-
-import javax.naming.InvalidNameException;
-import javax.naming.NamingException;
-import javax.naming.directory.Attribute;
-import javax.naming.directory.Attributes;
-import javax.naming.ldap.LdapName;
-import javax.naming.ldap.Rdn;
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLPeerUnverifiedException;
-import javax.net.ssl.SSLSession;
-import javax.net.ssl.SSLSocket;
-import javax.security.auth.x500.X500Principal;
-
-/**
- * ************************************************************************
- * Copied from the not-yet-commons-ssl project at http://juliusdavies.ca/commons-ssl/
- * As the above project is accepted into Apache and its JARs become available in
- * the Maven 2 repos, we will have to switch to using the JARs instead
- * ************************************************************************
- * <p/>
- * Interface for checking if a hostname matches the names stored inside the
- * server's X.509 certificate.  Correctly implements
- * javax.net.ssl.HostnameVerifier, but that interface is not recommended.
- * Instead we added several check() methods that take SSLSocket,
- * or X509Certificate, or ultimately (they all end up calling this one),
- * String.  (It's easier to supply JUnit with Strings instead of mock
- * SSLSession objects!)
- * </p><p>Our check() methods throw exceptions if the name is
- * invalid, whereas javax.net.ssl.HostnameVerifier just returns true/false.
- * <p/>
- * We provide the HostnameVerifier.DEFAULT, HostnameVerifier.STRICT, and
- * HostnameVerifier.ALLOW_ALL implementations.  We also provide the more
- * specialized HostnameVerifier.DEFAULT_AND_LOCALHOST, as well as
- * HostnameVerifier.STRICT_IE6.  But feel free to define your own
- * implementations!
- * <p/>
- * Inspired by Sebastian Hauer's original StrictSSLProtocolSocketFactory in the
- * HttpClient "contrib" repository.
- *
- */
-
-public interface CertificateHostnameVerifier extends javax.net.ssl.HostnameVerifier {
-
-
-
-    /**
-     * The DEFAULT HostnameVerifier works the same way as Curl and Firefox.
-     * <p/>
-     * The hostname must match either the first CN, or any of the subject-alts.
-     * A wildcard can occur in the CN, and in any of the subject-alts.
-     * <p/>
-     * The only difference between DEFAULT and STRICT is that a wildcard (such
-     * as "*.foo.com") with DEFAULT matches all subdomains, including
-     * "a.b.foo.com".
-     */
-    CertificateHostnameVerifier DEFAULT =
-        new AbstractVerifier() {
-            public final void check(final String[] hosts, final String[] cns,
-                final String[] subjectAlts)
-                throws SSLException {
-                check(hosts, cns, subjectAlts, false, false);
-            }
-
-            public final String toString() {
-                return "DEFAULT";
-            }
-        };
-
-
-    /**
-     * The DEFAULT_AND_LOCALHOST HostnameVerifier works like the DEFAULT
-     * one with one additional relaxation:  a host of "localhost",
-     * "localhost.localdomain", "127.0.0.1", "::1" will always pass, no matter
-     * what is in the server's certificate.
-     */
-    CertificateHostnameVerifier DEFAULT_AND_LOCALHOST =
-        new AbstractVerifier() {
-            public final void check(final String[] hosts, final String[] cns,
-                final String[] subjectAlts)
-                throws SSLException {
-                if (isLocalhost(hosts[0])) {
-                    return;
-                }
-                check(hosts, cns, subjectAlts, false, false);
-            }
-
-            public final String toString() {
-                return "DEFAULT_AND_LOCALHOST";
-            }
-        };
-
-    /**
-     * The STRICT HostnameVerifier works the same way as java.net.URL in Sun
-     * Java 1.4, Sun Java 5, Sun Java 6.  It's also pretty close to IE6.
-     * This implementation appears to be compliant with RFC 2818 for dealing
-     * with wildcards.
-     * <p/>
-     * The hostname must match either the first CN, or any of the subject-alts.
-     * A wildcard can occur in the CN, and in any of the subject-alts.  The
-     * one divergence from IE6 is how we only check the first CN.  IE6 allows
-     * a match against any of the CNs present.  We decided to follow in
-     * Sun Java 1.4's footsteps and only check the first CN.
-     * <p/>
-     * A wildcard such as "*.foo.com" matches only subdomains in the same
-     * level, for example "a.foo.com".  It does not match deeper subdomains
-     * such as "a.b.foo.com".
-     */
-    CertificateHostnameVerifier STRICT =
-        new AbstractVerifier() {
-            public final void check(final String[] host, final String[] cns,
-                final String[] subjectAlts)
-                throws SSLException {
-                check(host, cns, subjectAlts, false, true);
-            }
-
-            public final String toString() {
-                return "STRICT";
-            }
-        };
-
-    /**
-     * The STRICT_IE6 HostnameVerifier works just like the STRICT one with one
-     * minor variation:  the hostname can match against any of the CN's in the
-     * server's certificate, not just the first one.  This behaviour is
-     * identical to IE6's behaviour.
-     */
-    CertificateHostnameVerifier STRICT_IE6 =
-        new AbstractVerifier() {
-            public final void check(final String[] host, final String[] cns,
-                final String[] subjectAlts)
-                throws SSLException {
-                check(host, cns, subjectAlts, true, true);
-            }
-
-            public final String toString() {
-                return "STRICT_IE6";
-            }
-        };
-
-    /**
-     * The ALLOW_ALL HostnameVerifier essentially turns hostname verification
-     * off.  This implementation is a no-op, and never throws the SSLException.
-     */
-    CertificateHostnameVerifier ALLOW_ALL =
-        new AbstractVerifier() {
-            public final void check(final String[] host, final String[] cns,
-                final String[] subjectAlts) {
-                // Allow everything - so never blowup.
-            }
-
-            public final String toString() {
-                return "ALLOW_ALL";
-            }
-        };
-
-        
-
-    boolean verify(String host, SSLSession session);
-
-    void check(String host, SSLSocket ssl) throws IOException;
-
-    void check(String host, X509Certificate cert) throws SSLException;
-
-    void check(String host, String[] cns, String[] subjectAlts)
-        throws SSLException;
-
-    void check(String[] hosts, SSLSocket ssl) throws IOException;
-
-    void check(String[] hosts, X509Certificate cert) throws SSLException;
-
-
-    /**
-     * Checks to see if the supplied hostname matches any of the supplied CNs
-     * or "DNS" Subject-Alts.  Most implementations only look at the first CN,
-     * and ignore any additional CNs.  Most implementations do look at all of
-     * the "DNS" Subject-Alts. The CNs or Subject-Alts may contain wildcards
-     * according to RFC 2818.
-     *
-     * @param cns         CN fields, in order, as extracted from the X.509
-     *                    certificate.
-     * @param subjectAlts Subject-Alt fields of type 2 ("DNS"), as extracted
-     *                    from the X.509 certificate.
-     * @param hosts       The array of hostnames to verify.
-     * @throws SSLException If verification failed.
-     */
-    void check(String[] hosts, String[] cns, String[] subjectAlts)
-        throws SSLException;
-        
-    abstract class AbstractVerifier implements CertificateHostnameVerifier {
-
-        /**
-         * This contains a list of 2nd-level domains that aren't allowed to
-         * have wildcards when combined with country-codes.
-         * For example: [*.co.uk].
-         * <p/>
-         * The [*.co.uk] problem is an interesting one.  Should we just hope
-         * that CA's would never foolishly allow such a certificate to happen?
-         * Looks like we're the only implementation guarding against this.
-         * Firefox, Curl, Sun Java 1.4, 5, 6 don't bother with this check.
-         */
-        private static final String[] BAD_COUNTRY_2LDS = {
-            "ac", "co", "com", "ed", "edu", "go", "gouv", "gov", "info",
-            "lg", "ne", "net", "or", "org"};
-
-        private static final String[] LOCALHOSTS = { 
-            "::1", "127.0.0.1",
-            "localhost",
-            "localhost.localdomain"};
-
-
-        static {
-            // Just in case developer forgot to manually sort the array.  :-)
-            Arrays.sort(BAD_COUNTRY_2LDS);
-            Arrays.sort(LOCALHOSTS);
-        }
-
-        protected AbstractVerifier() {
-        }
-
-        /**
-         * The javax.net.ssl.HostnameVerifier contract.
-         *
-         * @param host    'hostname' we used to create our socket
-         * @param session SSLSession with the remote server
-         * @return true if the host matched the one in the certificate.
-         */
-        public boolean verify(String host, SSLSession session) {
-            try {
-                Certificate[] certs = session.getPeerCertificates();
-                X509Certificate x509 = (X509Certificate) certs[0];
-                check(new String[]{host}, x509);
-                return true;
-            } catch (SSLException e) {
-                return false;
-            }
-        }
-
-        public void check(String host, SSLSocket ssl) throws IOException {
-            check(new String[]{host}, ssl);
-        }
-
-        public void check(String host, X509Certificate cert)
-            throws SSLException {
-            check(new String[]{host}, cert);
-        }
-
-        public void check(String host, String[] cns, String[] subjectAlts)
-            throws SSLException {
-            check(new String[]{host}, cns, subjectAlts);
-        }
-
-        public void check(String host[], SSLSocket ssl)
-            throws IOException {
-            if (host == null) {
-                throw new NullPointerException("host to verify is null");
-            }
-
-            SSLSession session = ssl.getSession();
-            if (session == null) {
-                // In our experience this only happens under IBM 1.4.x when
-                // spurious (unrelated) certificates show up in the server'
-                // chain.  Hopefully this will unearth the real problem:
-                InputStream in = ssl.getInputStream();
-                in.available();
-                /*
-                  If you're looking at the 2 lines of code above because
-                  you're running into a problem, you probably have two
-                  options:
-
-                    #1.  Clean up the certificate chain that your server
-                         is presenting (e.g. edit "/etc/apache2/server.crt"
-                         or wherever it is your server's certificate chain
-                         is defined).
-
-                                               OR
-
-                    #2.   Upgrade to an IBM 1.5.x or greater JVM, or switch
-                          to a non-IBM JVM.
-                */
-
-                // If ssl.getInputStream().available() didn't cause an
-                // exception, maybe at least now the session is available?
-                session = ssl.getSession();
-                if (session == null) {
-                    // If it's still null, probably a startHandshake() will
-                    // unearth the real problem.
-                    ssl.startHandshake();
-
-                    // Okay, if we still haven't managed to cause an exception,
-                    // might as well go for the NPE.  Or maybe we're okay now?
-                    session = ssl.getSession();
-                }
-            }
-            Certificate[] certs;
-            try {
-                certs = session.getPeerCertificates();
-            } catch (SSLPeerUnverifiedException spue) {
-                InputStream in = ssl.getInputStream();
-                in.available();
-                // Didn't trigger anything interesting?  Okay, just throw
-                // original.
-                throw spue;
-            }
-            X509Certificate x509 = (X509Certificate) certs[0];
-            check(host, x509);
-        }
-
-        public void check(String[] host, X509Certificate cert)
-            throws SSLException {
-
-            String[] cns = Certificates.getCNs(cert);
-            String[] subjectAlts = Certificates.getDNSSubjectAlts(cert);
-            check(host, cns, subjectAlts);
-
-        }
-
-        public void check(final String[] hosts, final String[] cns,
-            final String[] subjectAlts, final boolean ie6,
-            final boolean strictWithSubDomains)
-            throws SSLException {
-            // Build up lists of allowed hosts For logging/debugging purposes.
-            StringBuilder buf = new StringBuilder(32);
-            buf.append('<');
-            for (int i = 0; i < hosts.length; i++) {
-                String h = hosts[i];
-                h = h != null ? h.trim().toLowerCase() : "";
-                hosts[i] = h;
-                if (i > 0) {
-                    buf.append('/');
-                }
-                buf.append(h);
-            }
-            buf.append('>');
-            String hostnames = buf.toString();
-            // Build the list of names we're going to check.  Our DEFAULT and
-            // STRICT implementations of the HostnameVerifier only use the
-            // first CN provided.  All other CNs are ignored.
-            // (Firefox, wget, curl, Sun Java 1.4, 5, 6 all work this way).
-            Set<String> names = new TreeSet<String>();
-            if (cns != null && cns.length > 0 && cns[0] != null) {
-                names.add(cns[0]);
-                if (ie6) {
-                    for (int i = 1; i < cns.length; i++) {
-                        names.add(cns[i]);
-                    }
-                }
-            }
-            if (subjectAlts != null) {
-                for (int i = 0; i < subjectAlts.length; i++) {
-                    if (subjectAlts[i] != null) {
-                        names.add(subjectAlts[i]);
-                    }
-                }
-            }
-            if (names.isEmpty()) {
-                String msg = "Certificate for " + hosts[0] + " doesn't contain CN or DNS subjectAlt";
-                throw new SSLException(msg);
-            }
-
-            // StringBuilder for building the error message.
-            buf = new StringBuilder();
-
-            boolean match = false;
-        out:
-            for (Iterator<String> it = names.iterator(); it.hasNext();) {
-                // Don't trim the CN, though!
-                String cn = it.next();
-                cn = cn.toLowerCase();
-                // Store CN in StringBuilder in case we need to report an error.
-                buf.append(" <");
-                buf.append(cn);
-                buf.append('>');
-                if (it.hasNext()) {
-                    buf.append(" OR");
-                }
-
-                // The CN better have at least two dots if it wants wildcard
-                // action.  It also can't be [*.co.uk] or [*.co.jp] or
-                // [*.org.uk], etc...
-                boolean doWildcard = cn.startsWith("*.") 
-                    && cn.lastIndexOf('.') >= 0 
-                    && !isIP4Address(cn) 
-                    && acceptableCountryWildcard(cn);
-
-                for (int i = 0; i < hosts.length; i++) {
-                    final String hostName = hosts[i].trim().toLowerCase();
-                    if (doWildcard) {
-                        match = hostName.endsWith(cn.substring(1));
-                        if (match && strictWithSubDomains) {
-                            // If we're in strict mode, then [*.foo.com] is not
-                            // allowed to match [a.b.foo.com]
-                            match = countDots(hostName) == countDots(cn);
-                        }
-                    } else {
-                        match = hostName.equals(cn);
-                    }
-                    if (match) {
-                        break out;
-                    }
-                }
-            }
-            if (!match) {
-                throw new SSLException("hostname in certificate didn't match: " + hostnames + " !=" + buf);
-            }
-        }
-
-        public static boolean isIP4Address(final String cn) {
-            boolean isIP4 = true;
-            String tld = cn;
-            int x = cn.lastIndexOf('.');
-            // We only bother analyzing the characters after the final dot
-            // in the name.
-            if (x >= 0 && x + 1 < cn.length()) {
-                tld = cn.substring(x + 1);
-            }
-            for (int i = 0; i < tld.length(); i++) {
-                if (!Character.isDigit(tld.charAt(0))) {
-                    isIP4 = false;
-                    break;
-                }
-            }
-            return isIP4;
-        }
-
-        public static boolean acceptableCountryWildcard(final String cn) {
-            int cnLen = cn.length();
-            if (cnLen >= 7 && cnLen <= 9
-                // Look for the '.' in the 3rd-last position:
-                && cn.charAt(cnLen - 3) == '.') {
-                // Trim off the [*.] and the [.XX].
-                String s = cn.substring(2, cnLen - 3);
-                // And test against the sorted array of bad 2lds:
-                int x = Arrays.binarySearch(BAD_COUNTRY_2LDS, s);
-                return x < 0;               
-            }
-            return true;
-        }
-
-        public static boolean isLocalhost(String host) {
-            host = host != null ? host.trim().toLowerCase() : "";
-            if (host.startsWith("::1")) {
-                int x = host.lastIndexOf('%');
-                if (x >= 0) {
-                    host = host.substring(0, x);
-                }
-            }
-            int x = Arrays.binarySearch(LOCALHOSTS, host);
-            return x >= 0;
-        }
-
-        /**
-         * Counts the number of dots "." in a string.
-         *
-         * @param s string to count dots from
-         * @return number of dots
-         */
-        public static int countDots(final String s) {
-            int count = 0;
-            for (int i = 0; i < s.length(); i++) {
-                if (s.charAt(i) == '.') {
-                    count++;
-                }
-            }
-            return count;
-        }
-    }
-
-    final class Certificates {
-        private Certificates() {
-            //utility class
-        }
-        public static String[] getCNs(X509Certificate cert) {
-            try {
-                final String subjectPrincipal = cert.getSubjectX500Principal().getName(X500Principal.RFC2253);
-                final List<String> cnList = new LinkedList<String>();
-                final LdapName subjectDN = new LdapName(subjectPrincipal);
-                for (final Rdn rds : subjectDN.getRdns()) {
-                    final Attributes attributes = rds.toAttributes();
-                    final Attribute cn = attributes.get("cn");
-                    if (cn != null) {
-                        try {
-                            final Object value = cn.get();
-                            if (value != null) {
-                                cnList.add(value.toString());
-                            }
-                        } catch (NoSuchElementException ignore) {
-                            //ignore
-                        } catch (NamingException ignore) {
-                            //ignore
-                        }
-                    }
-                }
-                if (!cnList.isEmpty()) {
-                    return cnList.toArray(new String[cnList.size()]);
-                }
-            } catch (InvalidNameException ignore) {
-                //ignore
-            }
-            return null;
-        }
-
-        /**
-         * Extracts the array of SubjectAlt DNS names from an X509Certificate.
-         * Returns null if there aren't any.
-         * <p/>
-         * Note:  Java doesn't appear able to extract international characters
-         * from the SubjectAlts.  It can only extract international characters
-         * from the CN field.
-         * <p/>
-         * (Or maybe the version of OpenSSL I'm using to test isn't storing the
-         * international characters correctly in the SubjectAlts?).
-         *
-         * @param cert X509Certificate
-         * @return Array of SubjectALT DNS names stored in the certificate.
-         */
-        public static String[] getDNSSubjectAlts(X509Certificate cert) {
-            List<String> subjectAltList = new LinkedList<String>();
-            Collection<List<?>> c = null;
-            try {
-                c = cert.getSubjectAlternativeNames();
-            } catch (CertificateParsingException cpe) {
-                // Should probably log.debug() this?
-                cpe.printStackTrace();
-            }
-            if (c != null) {
-                Iterator<List<?>> it = c.iterator();
-                while (it.hasNext()) {
-                    List<?> list = it.next();
-                    int type = ((Integer) list.get(0)).intValue();
-                    // If type is 2, then we've got a dNSName
-                    if (type == 2) {
-                        String s = (String) list.get(1);
-                        subjectAltList.add(s);
-                    }
-                }
-            }
-            if (!subjectAltList.isEmpty()) {
-                String[] subjectAlts = new String[subjectAltList.size()];
-                subjectAltList.toArray(subjectAlts);
-                return subjectAlts;
-            } else {
-                return null;
-            }
-        }
-    }
-}


[4/4] cxf git commit: [CXF-6143] - SSL/TLS hostname verification does not strictly follow HTTPS RFC2818 - Code ported from httpclient

Posted by co...@apache.org.
[CXF-6143] - SSL/TLS hostname verification does not strictly follow HTTPS RFC2818
 - Code ported from httpclient


Project: http://git-wip-us.apache.org/repos/asf/cxf/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/90430115
Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/90430115
Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/90430115

Branch: refs/heads/master
Commit: 9043011518147bbc2dac036681fd64d7ce578bc2
Parents: 5b01778
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Wed Dec 17 15:44:52 2014 +0000
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Wed Dec 17 16:40:34 2014 +0000

----------------------------------------------------------------------
 rt/transports/http/pom.xml                      |  19 +
 .../https/AllowAllHostnameVerifier.java         |  47 ++
 .../apache/cxf/transport/https/SSLUtils.java    |   7 +-
 .../httpclient/DefaultHostnameVerifier.java     | 316 ++++++++++++
 .../https/httpclient/InetAddressUtils.java      | 119 +++++
 .../https/httpclient/PublicSuffixList.java      |  64 +++
 .../httpclient/PublicSuffixListParser.java      | 110 ++++
 .../https/httpclient/PublicSuffixMatcher.java   | 115 +++++
 .../httpclient/PublicSuffixMatcherLoader.java   | 110 ++++
 .../httpclient/CertificatesToPlayWith.java      | 515 +++++++++++++++++++
 .../httpclient/DefaultHostnameVerifierTest.java | 301 +++++++++++
 11 files changed, 1721 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf/blob/90430115/rt/transports/http/pom.xml
----------------------------------------------------------------------
diff --git a/rt/transports/http/pom.xml b/rt/transports/http/pom.xml
index 4f3cfde..445568e 100644
--- a/rt/transports/http/pom.xml
+++ b/rt/transports/http/pom.xml
@@ -194,6 +194,25 @@
                     </instructions>
                 </configuration>
             </plugin>
+            <plugin>
+                <groupId>com.googlecode.maven-download-plugin</groupId>
+                <artifactId>download-maven-plugin</artifactId>
+                <version>1.2.0</version>
+                <executions>
+                    <execution>
+                        <id>download-public-suffix-list</id>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>wget</goal>
+                        </goals>
+                        <configuration>
+                            <url>https://publicsuffix.org/list/effective_tld_names.dat</url>
+                            <outputDirectory>${project.build.outputDirectory}/mozilla</outputDirectory>
+                            <outputFileName>public-suffix-list.txt</outputFileName>
+                        </configuration>
+                    </execution>
+                </executions>
+             </plugin>
         </plugins>
     </build>
 </project>

http://git-wip-us.apache.org/repos/asf/cxf/blob/90430115/rt/transports/http/src/main/java/org/apache/cxf/transport/https/AllowAllHostnameVerifier.java
----------------------------------------------------------------------
diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/https/AllowAllHostnameVerifier.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/AllowAllHostnameVerifier.java
new file mode 100644
index 0000000..cfabf5f
--- /dev/null
+++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/AllowAllHostnameVerifier.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ 
+package org.apache.cxf.transport.https;
+
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+
+/**
+ * Allow all hostnames. This is only suitable for use in testing, and NOT in production! 
+ */
+class AllowAllHostnameVerifier implements javax.net.ssl.HostnameVerifier {
+
+    @Override
+    public boolean verify(String host, SSLSession session) {
+        try {
+            Certificate[] certs = session.getPeerCertificates();
+            if (certs != null && certs[0] instanceof X509Certificate) {
+                return true;
+            }
+            return false;
+        } catch (SSLException e) {
+            return false;
+        }
+    }
+
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cxf/blob/90430115/rt/transports/http/src/main/java/org/apache/cxf/transport/https/SSLUtils.java
----------------------------------------------------------------------
diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/https/SSLUtils.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/SSLUtils.java
index 803cd1c..183f80e 100644
--- a/rt/transports/http/src/main/java/org/apache/cxf/transport/https/SSLUtils.java
+++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/SSLUtils.java
@@ -31,6 +31,9 @@ import org.apache.cxf.configuration.jsse.TLSClientParameters;
 import org.apache.cxf.configuration.jsse.TLSParameterBase;
 import org.apache.cxf.configuration.jsse.TLSServerParameters;
 
+import org.apache.cxf.transport.https.httpclient.DefaultHostnameVerifier;
+import org.apache.cxf.transport.https.httpclient.PublicSuffixMatcherLoader;
+
 public final class SSLUtils {
     private SSLUtils() {
         //Helper class
@@ -44,9 +47,9 @@ public final class SSLUtils {
         } else if (tlsClientParameters.isUseHttpsURLConnectionDefaultHostnameVerifier()) {
             verifier = HttpsURLConnection.getDefaultHostnameVerifier();
         } else if (tlsClientParameters.isDisableCNCheck()) {
-            verifier = CertificateHostnameVerifier.ALLOW_ALL;
+            verifier = new AllowAllHostnameVerifier();
         } else {
-            verifier = CertificateHostnameVerifier.DEFAULT;
+            verifier = new DefaultHostnameVerifier(PublicSuffixMatcherLoader.getDefault());
         }
         return verifier;
     }

http://git-wip-us.apache.org/repos/asf/cxf/blob/90430115/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/DefaultHostnameVerifier.java
----------------------------------------------------------------------
diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/DefaultHostnameVerifier.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/DefaultHostnameVerifier.java
new file mode 100644
index 0000000..8fb067f
--- /dev/null
+++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/DefaultHostnameVerifier.java
@@ -0,0 +1,316 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.cxf.transport.https.httpclient;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+import java.util.NoSuchElementException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.naming.InvalidNameException;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+import javax.security.auth.x500.X500Principal;
+
+import org.apache.cxf.common.logging.LogUtils;
+
+/**
+ * Default {@link javax.net.ssl.HostnameVerifier} implementation.
+ * Copied from httpclient.
+ */
+public final class DefaultHostnameVerifier implements HostnameVerifier {
+    
+    static final int DNS_NAME_TYPE = 2;
+    static final int IP_ADDRESS_TYPE = 7;
+    
+    private static final Logger LOG = LogUtils.getL7dLogger(DefaultHostnameVerifier.class);
+
+    private final PublicSuffixMatcher publicSuffixMatcher;
+    
+    public DefaultHostnameVerifier(final PublicSuffixMatcher publicSuffixMatcher) {
+        this.publicSuffixMatcher = publicSuffixMatcher;
+    }
+
+    public DefaultHostnameVerifier() {
+        this(null);
+    }
+
+    @Override
+    public boolean verify(final String host, final SSLSession session) {
+        try {
+            final Certificate[] certs = session.getPeerCertificates();
+            final X509Certificate x509 = (X509Certificate) certs[0];
+            verify(host, x509);
+            return true;
+        } catch (final SSLException ex) {
+            if (LOG.isLoggable(Level.FINE)) {
+                LOG.log(Level.FINE, ex.getMessage(), ex);
+            }
+            return false;
+        }
+    }
+
+    public void verify(
+            final String host, final X509Certificate cert) throws SSLException {
+        final boolean ipv4 = InetAddressUtils.isIPv4Address(host);
+        final boolean ipv6 = InetAddressUtils.isIPv6Address(host);
+        final int subjectType = ipv4 || ipv6 ? IP_ADDRESS_TYPE : DNS_NAME_TYPE;
+        final List<String> subjectAlts = extractSubjectAlts(cert, subjectType);
+        if (subjectAlts != null && !subjectAlts.isEmpty()) {
+            if (ipv4) {
+                matchIPAddress(host, subjectAlts);
+            } else if (ipv6) {
+                matchIPv6Address(host, subjectAlts);
+            } else {
+                matchDNSName(host, subjectAlts, this.publicSuffixMatcher);
+            }
+        } else {
+            // CN matching has been deprecated by rfc2818 and can be used
+            // as fallback only when no subjectAlts are available
+            final X500Principal subjectPrincipal = cert.getSubjectX500Principal();
+            final String cn = extractCN(subjectPrincipal.getName(X500Principal.RFC2253));
+            if (cn == null) {
+                throw new SSLException("Certificate subject for <" + host + "> doesn't contain " 
+                    + "a common name and does not have alternative names");
+            }
+            matchCN(host, cn, this.publicSuffixMatcher);
+        }
+    }
+
+    static void matchIPAddress(final String host, final List<String> subjectAlts) throws SSLException {
+        for (int i = 0; i < subjectAlts.size(); i++) {
+            final String subjectAlt = subjectAlts.get(i);
+            if (host.equals(subjectAlt)) {
+                return;
+            }
+        }
+        throw new SSLException("Certificate for <" + host + "> doesn't match any " 
+            + "of the subject alternative names: " + subjectAlts);
+    }
+
+    static void matchIPv6Address(final String host, final List<String> subjectAlts) throws SSLException {
+        final String normalisedHost = normaliseAddress(host);
+        for (int i = 0; i < subjectAlts.size(); i++) {
+            final String subjectAlt = subjectAlts.get(i);
+            final String normalizedSubjectAlt = normaliseAddress(subjectAlt);
+            if (normalisedHost.equals(normalizedSubjectAlt)) {
+                return;
+            }
+        }
+        throw new SSLException("Certificate for <" + host + "> doesn't match any " 
+            + "of the subject alternative names: " + subjectAlts);
+    }
+
+    static void matchDNSName(final String host, final List<String> subjectAlts,
+                             final PublicSuffixMatcher publicSuffixMatcher) throws SSLException {
+        final String normalizedHost = host.toLowerCase(Locale.ROOT);
+        for (int i = 0; i < subjectAlts.size(); i++) {
+            final String subjectAlt = subjectAlts.get(i);
+            final String normalizedSubjectAlt = subjectAlt.toLowerCase(Locale.ROOT);
+            if (matchIdentityStrict(normalizedHost, normalizedSubjectAlt, publicSuffixMatcher)) {
+                return;
+            }
+        }
+        throw new SSLException("Certificate for <" + host + "> doesn't match any " 
+            + "of the subject alternative names: " + subjectAlts);
+    }
+
+    static void matchCN(final String host, final String cn,
+                 final PublicSuffixMatcher publicSuffixMatcher) throws SSLException {
+        if (!matchIdentityStrict(host, cn, publicSuffixMatcher)) {
+            throw new SSLException("Certificate for <" + host + "> doesn't match " 
+                + "common name of the certificate subject: " + cn);
+        }
+    }
+
+    private static boolean matchIdentity(final String host, final String identity,
+                                         final PublicSuffixMatcher publicSuffixMatcher,
+                                         final boolean strict) {
+        if (host == null) {
+            return false;
+        }
+
+        if (publicSuffixMatcher != null && host.contains(".")) {
+            String domainRoot = publicSuffixMatcher.getDomainRoot(identity);
+            if (domainRoot == null) {
+                // Public domain
+                return false;
+            }
+            domainRoot = "." + domainRoot;
+            if (!host.endsWith(domainRoot)) {
+                // Domain root mismatch
+                return false;
+            }
+            if (strict && countDots(identity) != countDots(domainRoot)) {
+                return false;
+            }
+        }
+        
+        return matchServerIdentity(host, identity, strict);
+    }
+
+    private static boolean matchServerIdentity(final String host, final String identity,
+                                               boolean strict) {
+        // RFC 2818, 3.1. Server Identity
+        // "...Names may contain the wildcard
+        // character * which is considered to match any single domain name
+        // component or component fragment..."
+        // Based on this statement presuming only singular wildcard is legal
+        final int asteriskIdx = identity.indexOf('*');
+        if (asteriskIdx != -1) {
+            final String prefix = identity.substring(0, asteriskIdx);
+            final String suffix = identity.substring(asteriskIdx + 1);
+            if (!prefix.isEmpty() && !host.startsWith(prefix)) {
+                return false;
+            }
+            if (!suffix.isEmpty() && !host.endsWith(suffix)) {
+                return false;
+            }
+            // Additional sanity checks on content selected by wildcard can be done here
+            if (strict) {
+                final String remainder = host.substring(
+                        prefix.length(), host.length() - suffix.length());
+                if (remainder.contains(".")) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        return host.equalsIgnoreCase(identity);
+    }
+
+    static int countDots(final String s) {
+        int count = 0;
+        for (int i = 0; i < s.length(); i++) {
+            if (s.charAt(i) == '.') {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    static boolean matchIdentity(final String host, final String identity,
+                                 final PublicSuffixMatcher publicSuffixMatcher) {
+        return matchIdentity(host, identity, publicSuffixMatcher, false);
+    }
+
+    static boolean matchIdentity(final String host, final String identity) {
+        return matchIdentity(host, identity, null, false);
+    }
+
+    static boolean matchIdentityStrict(final String host, final String identity,
+                                       final PublicSuffixMatcher publicSuffixMatcher) {
+        return matchIdentity(host, identity, publicSuffixMatcher, true);
+    }
+
+    static boolean matchIdentityStrict(final String host, final String identity) {
+        return matchIdentity(host, identity, null, true);
+    }
+
+    static String extractCN(final String subjectPrincipal) throws SSLException {
+        if (subjectPrincipal == null) {
+            return null;
+        }
+        try {
+            final LdapName subjectDN = new LdapName(subjectPrincipal);
+            final List<Rdn> rdns = subjectDN.getRdns();
+            for (int i = rdns.size() - 1; i >= 0; i--) {
+                final Rdn rds = rdns.get(i);
+                final Attributes attributes = rds.toAttributes();
+                final Attribute cn = attributes.get("cn");
+                if (cn != null) {
+                    try {
+                        final Object value = cn.get();
+                        if (value != null) {
+                            return value.toString();
+                        }
+                    } catch (NoSuchElementException ignore) {
+                        //
+                    } catch (NamingException ignore) {
+                        //
+                    }
+                }
+            }
+            return null;
+        } catch (InvalidNameException e) {
+            throw new SSLException(subjectPrincipal + " is not a valid X500 distinguished name");
+        }
+    }
+
+    static List<String> extractSubjectAlts(final X509Certificate cert, final int subjectType) {
+        Collection<List<?>> c = null;
+        try {
+            c = cert.getSubjectAlternativeNames();
+        } catch (final CertificateParsingException ignore) {
+            //
+        }
+        List<String> subjectAltList = null;
+        if (c != null) {
+            for (final List<?> aC : c) {
+                final List<?> list = aC;
+                final int type = ((Integer) list.get(0)).intValue();
+                if (type == subjectType) {
+                    final String s = (String) list.get(1);
+                    if (subjectAltList == null) {
+                        subjectAltList = new ArrayList<String>();
+                    }
+                    subjectAltList.add(s);
+                }
+            }
+        }
+        return subjectAltList;
+    }
+
+    /*
+     * Normalize IPv6 or DNS name.
+     */
+    static String normaliseAddress(final String hostname) {
+        if (hostname == null) {
+            return hostname;
+        }
+        try {
+            final InetAddress inetAddress = InetAddress.getByName(hostname);
+            return inetAddress.getHostAddress();
+        } catch (final UnknownHostException unexpected) { // Should not happen, because we check for IPv6 address above
+            return hostname;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/90430115/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/InetAddressUtils.java
----------------------------------------------------------------------
diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/InetAddressUtils.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/InetAddressUtils.java
new file mode 100644
index 0000000..1aede8b
--- /dev/null
+++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/InetAddressUtils.java
@@ -0,0 +1,119 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.cxf.transport.https.httpclient;
+
+import java.util.regex.Pattern;
+
+/**
+ * A collection of utilities relating to InetAddresses.
+ * 
+ * Copied from httpclient.
+ */
+public final class InetAddressUtils {
+
+    private static final String IPV4_BASIC_PATTERN_STRING =
+            "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}" // initial 3 fields, 0-255 followed by .
+            + "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])"; // final field, 0-255
+
+    private static final Pattern IPV4_PATTERN =
+        Pattern.compile("^" + IPV4_BASIC_PATTERN_STRING + "$");
+
+    private static final Pattern IPV4_MAPPED_IPV6_PATTERN = // TODO does not allow for redundant leading zeros
+            Pattern.compile("^::[fF]{4}:" + IPV4_BASIC_PATTERN_STRING + "$");
+
+    private static final Pattern IPV6_STD_PATTERN =
+        Pattern.compile(
+                "^[0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4}){7}$");
+
+    private static final Pattern IPV6_HEX_COMPRESSED_PATTERN =
+        Pattern.compile(
+                "^(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,5})?)" // 0-6 hex fields
+                + "::"
+                + "(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,5})?)$"); // 0-6 hex fields
+
+    /*
+     *  The above pattern is not totally rigorous as it allows for more than 7 hex fields in total
+     */
+    private static final char COLON_CHAR = ':';
+
+    // Must not have more than 7 colons (i.e. 8 fields)
+    private static final int MAX_COLON_COUNT = 7;
+    
+    private InetAddressUtils() {
+    }
+
+    /**
+     * Checks whether the parameter is a valid IPv4 address
+     *
+     * @param input the address string to check for validity
+     * @return true if the input parameter is a valid IPv4 address
+     */
+    public static boolean isIPv4Address(final String input) {
+        return IPV4_PATTERN.matcher(input).matches();
+    }
+
+    public static boolean isIPv4MappedIPv64Address(final String input) {
+        return IPV4_MAPPED_IPV6_PATTERN.matcher(input).matches();
+    }
+
+    /**
+     * Checks whether the parameter is a valid standard (non-compressed) IPv6 address
+     *
+     * @param input the address string to check for validity
+     * @return true if the input parameter is a valid standard (non-compressed) IPv6 address
+     */
+    public static boolean isIPv6StdAddress(final String input) {
+        return IPV6_STD_PATTERN.matcher(input).matches();
+    }
+
+    /**
+     * Checks whether the parameter is a valid compressed IPv6 address
+     *
+     * @param input the address string to check for validity
+     * @return true if the input parameter is a valid compressed IPv6 address
+     */
+    public static boolean isIPv6HexCompressedAddress(final String input) {
+        int colonCount = 0;
+        for (int i = 0; i < input.length(); i++) {
+            if (input.charAt(i) == COLON_CHAR) {
+                colonCount++;
+            }
+        }
+        return  colonCount <= MAX_COLON_COUNT && IPV6_HEX_COMPRESSED_PATTERN.matcher(input).matches();
+    }
+
+    /**
+     * Checks whether the parameter is a valid IPv6 address (including compressed).
+     *
+     * @param input the address string to check for validity
+     * @return true if the input parameter is a valid standard or compressed IPv6 address
+     */
+    public static boolean isIPv6Address(final String input) {
+        return isIPv6StdAddress(input) || isIPv6HexCompressedAddress(input);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/90430115/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/PublicSuffixList.java
----------------------------------------------------------------------
diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/PublicSuffixList.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/PublicSuffixList.java
new file mode 100644
index 0000000..b714a46
--- /dev/null
+++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/PublicSuffixList.java
@@ -0,0 +1,64 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.cxf.transport.https.httpclient;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Public suffix is a set of DNS names or wildcards concatenated with dots. It represents
+ * the part of a domain name which is not under the control of the individual registrant
+ * <p>
+ * An up-to-date list of suffixes can be obtained from
+ * <a href="http://publicsuffix.org/">publicsuffix.org</a>
+ *
+ * Copied from httpclient
+ */
+public final class PublicSuffixList {
+
+    private final List<String> rules;
+    private final List<String> exceptions;
+
+    public PublicSuffixList(final List<String> rules, final List<String> exceptions) {
+        if (rules == null) {
+            throw new IllegalArgumentException("Domain suffix rules are null");
+        }
+        if (exceptions == null) {
+            throw new IllegalArgumentException("Domain suffix exceptions are null");
+        }
+        this.rules = Collections.unmodifiableList(rules);
+        this.exceptions = Collections.unmodifiableList(exceptions);
+    }
+
+    public List<String> getRules() {
+        return rules;
+    }
+
+    public List<String> getExceptions() {
+        return exceptions;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/90430115/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/PublicSuffixListParser.java
----------------------------------------------------------------------
diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/PublicSuffixListParser.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/PublicSuffixListParser.java
new file mode 100644
index 0000000..f4b61d7
--- /dev/null
+++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/PublicSuffixListParser.java
@@ -0,0 +1,110 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.cxf.transport.https.httpclient;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Parses the list from <a href="http://publicsuffix.org/">publicsuffix.org</a>
+ * and configures a PublicSuffixFilter.
+ *
+ * Copied from httpclient
+ */
+public final class PublicSuffixListParser {
+
+    private static final int MAX_LINE_LEN = 256;
+
+    public PublicSuffixListParser() {
+    }
+
+    /**
+     * Parses the public suffix list format. When creating the reader from the file, make sure to
+     * use the correct encoding (the original list is in UTF-8).
+     *
+     * @param reader the data reader. The caller is responsible for closing the reader.
+     * @throws java.io.IOException on error while reading from list
+     */
+    public PublicSuffixList parse(final Reader reader) throws IOException {
+        final List<String> rules = new ArrayList<String>();
+        final List<String> exceptions = new ArrayList<String>();
+        final BufferedReader r = new BufferedReader(reader);
+        final StringBuilder sb = new StringBuilder(256);
+        boolean more = true;
+        while (more) {
+            more = readLine(r, sb);
+            String line = sb.toString();
+            if (line.isEmpty()) {
+                continue;
+            }
+            if (line.startsWith("//")) {
+                continue; //entire lines can also be commented using //
+            }
+            if (line.startsWith(".")) {
+                line = line.substring(1); // A leading dot is optional
+            }
+            // An exclamation mark (!) at the start of a rule marks an exception to a previous wildcard rule
+            final boolean isException = line.startsWith("!");
+            if (isException) {
+                line = line.substring(1);
+            }
+
+            if (isException) {
+                exceptions.add(line);
+            } else {
+                rules.add(line);
+            }
+        }
+        return new PublicSuffixList(rules, exceptions);
+    }
+
+    private boolean readLine(final Reader r, final StringBuilder sb) throws IOException {
+        sb.setLength(0);
+        int b;
+        boolean hitWhitespace = false;
+        while ((b = r.read()) != -1) {
+            final char c = (char) b;
+            if (c == '\n') {
+                break;
+            }
+            // Each line is only read up to the first whitespace
+            if (Character.isWhitespace(c)) {
+                hitWhitespace = true;
+            }
+            if (!hitWhitespace) {
+                sb.append(c);
+            }
+            if (sb.length() > MAX_LINE_LEN) {
+                return false; // prevent excess memory usage
+            }
+        }
+        return b != -1;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/90430115/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/PublicSuffixMatcher.java
----------------------------------------------------------------------
diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/PublicSuffixMatcher.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/PublicSuffixMatcher.java
new file mode 100644
index 0000000..fa2318f
--- /dev/null
+++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/PublicSuffixMatcher.java
@@ -0,0 +1,115 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.cxf.transport.https.httpclient;
+
+import java.net.IDN;
+import java.util.Collection;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Utility class that can test if DNS names match the content of the Public Suffix List.
+ * <p>
+ * An up-to-date list of suffixes can be obtained from
+ * <a href="http://publicsuffix.org/">publicsuffix.org</a>
+ *
+ * Copied from httpclient.
+ */
+public final class PublicSuffixMatcher {
+
+    private final Map<String, String> rules;
+    private final Map<String, String> exceptions;
+
+    public PublicSuffixMatcher(final Collection<String> rules, final Collection<String> exceptions) {
+        if (rules == null) {
+            throw new IllegalArgumentException("Domain suffix rules are null");
+        }
+        this.rules = new ConcurrentHashMap<String, String>(rules.size());
+        for (String rule: rules) {
+            this.rules.put(rule, rule);
+        }
+        if (exceptions != null) {
+            this.exceptions = new ConcurrentHashMap<String, String>(exceptions.size());
+            for (String exception: exceptions) {
+                this.exceptions.put(exception, exception);
+            }
+        } else {
+            this.exceptions = null;
+        }
+    }
+
+    /**
+     * Returns registrable part of the domain for the given domain name of {@code null}
+     * if given domain represents a public suffix.
+     *
+     * @param domain
+     * @return domain root
+     */
+    public String getDomainRoot(final String domain) {
+        if (domain == null) {
+            return null;
+        }
+        if (domain.startsWith(".")) {
+            return null;
+        }
+        String domainName = null;
+        String segment = domain.toLowerCase(Locale.ROOT);
+        while (segment != null) {
+
+            // An exception rule takes priority over any other matching rule.
+            if (this.exceptions != null && this.exceptions.containsKey(IDN.toUnicode(segment))) {
+                return segment;
+            }
+
+            if (this.rules.containsKey(IDN.toUnicode(segment))) {
+                break;
+            }
+
+            final int nextdot = segment.indexOf('.');
+            final String nextSegment = nextdot != -1 ? segment.substring(nextdot + 1) : null;
+
+            if (nextSegment != null
+                && this.rules.containsKey("*." + IDN.toUnicode(nextSegment))) {
+                break;
+            }
+            if (nextdot != -1) {
+                domainName = segment;
+            }
+            segment = nextSegment;
+        }
+        return domainName;
+    }
+
+    public boolean matches(final String domain) {
+        if (domain == null) {
+            return false;
+        }
+        final String domainRoot = getDomainRoot(domain.startsWith(".") ? domain.substring(1) : domain);
+        return domainRoot == null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/90430115/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/PublicSuffixMatcherLoader.java
----------------------------------------------------------------------
diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/PublicSuffixMatcherLoader.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/PublicSuffixMatcherLoader.java
new file mode 100644
index 0000000..d51d0bd
--- /dev/null
+++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/PublicSuffixMatcherLoader.java
@@ -0,0 +1,110 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.cxf.transport.https.httpclient;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.cxf.common.logging.LogUtils;
+
+/**
+ * {@link org.apache.http.conn.util.PublicSuffixMatcher} loader.
+ *
+ * Copied from httpclient.
+ */
+public final class PublicSuffixMatcherLoader {
+    
+    private static final Logger LOG = LogUtils.getL7dLogger(PublicSuffixMatcherLoader.class);
+    private static volatile PublicSuffixMatcher defaultInstance;
+    
+    private PublicSuffixMatcherLoader() {
+        //
+    }
+
+    private static PublicSuffixMatcher load(final InputStream in) throws IOException {
+        final PublicSuffixList list = new PublicSuffixListParser().parse(
+                new InputStreamReader(in, "UTF-8"));
+        return new PublicSuffixMatcher(list.getRules(), list.getExceptions());
+    }
+
+    public static PublicSuffixMatcher load(final URL url) throws IOException {
+        if (url == null) {
+            throw new IllegalArgumentException("URL is null");
+        }
+        final InputStream in = url.openStream();
+        try {
+            return load(in);
+        } finally {
+            in.close();
+        }
+    }
+
+    public static PublicSuffixMatcher load(final File file) throws IOException {
+        if (file == null) {
+            throw new IllegalArgumentException("File is null");
+        }
+        final InputStream in = new FileInputStream(file);
+        try {
+            return load(in);
+        } finally {
+            in.close();
+        }
+    }
+
+    public static PublicSuffixMatcher getDefault() {
+        if (defaultInstance == null) {
+            synchronized (PublicSuffixMatcherLoader.class) {
+                if (defaultInstance == null) {
+                    final URL url = PublicSuffixMatcherLoader.class.getResource(
+                            "/mozilla/public-suffix-list.txt");
+                    if (url != null) {
+                        try {
+                            defaultInstance = load(url);
+                        } catch (IOException ex) {
+                            // Should never happen
+                            if (LOG.isLoggable(Level.WARNING)) {
+                                LOG.log(Level.WARNING, 
+                                        "Failure loading public suffix list from default resource", 
+                                        ex);
+                            }
+                        }
+                    } else {
+                        defaultInstance = new PublicSuffixMatcher(Arrays.asList("com"), null);
+                    }
+                }
+            }
+        }
+        return defaultInstance;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/90430115/rt/transports/http/src/test/java/org/apache/cxf/transport/https/httpclient/CertificatesToPlayWith.java
----------------------------------------------------------------------
diff --git a/rt/transports/http/src/test/java/org/apache/cxf/transport/https/httpclient/CertificatesToPlayWith.java b/rt/transports/http/src/test/java/org/apache/cxf/transport/https/httpclient/CertificatesToPlayWith.java
new file mode 100644
index 0000000..bbc82ce
--- /dev/null
+++ b/rt/transports/http/src/test/java/org/apache/cxf/transport/https/httpclient/CertificatesToPlayWith.java
@@ -0,0 +1,515 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.cxf.transport.https.httpclient;
+
+/**
+ * Some X509 certificates to test against.
+ * <p>
+ * Note:  some of these certificates have Japanese Kanji in the "subjectAlt"
+ * field (UTF8).  Not sure how realistic that is since international characters
+ * in DNS names usually get translated into ASCII using "xn--" style DNS
+ * entries.  "xn--i8s592g.co.jp" is what FireFox actually uses when trying to
+ * find &#x82b1;&#x5b50;.co.jp.  So would the CN in the certificate contain
+ * "xn--i8s592g.co.jp" in ASCII, or "&#x82b1;&#x5b50;.co.jp" in UTF8?  (Both?)
+ * </p>
+ *
+ * Copied from httpclient
+ */
+public final class CertificatesToPlayWith {
+
+    /**
+     * CN=foo.com
+     */
+    public static final byte[] X509_FOO = (
+          "-----BEGIN CERTIFICATE-----\n"
+          + "MIIERjCCAy6gAwIBAgIJAIz+EYMBU6aQMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n"
+          + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n"
+          + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n"
+          + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n"
+          + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE1MzE0MVoXDTI4MTEwNTE1MzE0MVowgaQx\n"
+          + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n"
+          + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n"
+          + "cnRpZmljYXRlczEQMA4GA1UEAxMHZm9vLmNvbTElMCMGCSqGSIb3DQEJARYWanVs\n"
+          + "aXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n"
+          + "ggEBAMhjr5aCPoyp0R1iroWAfnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2B\n"
+          + "lYho4O84X244QrZTRl8kQbYtxnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRy\n"
+          + "zerA/ZtrlUqf+lKo0uWcocxeRc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY\n"
+          + "07hNKXAb2odnVqgzcYiDkLV8ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8\n"
+          + "BqnGd87xQU3FVZI4tbtkB+KzjD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiV\n"
+          + "JTxpTKqym93whYk93l3ocEe55c0CAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgB\n"
+          + "hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE\n"
+          + "FJ8Ud78/OrbKOIJCSBYs2tDLXofYMB8GA1UdIwQYMBaAFHua2o+QmU5S0qzbswNS\n"
+          + "yoemDT4NMA0GCSqGSIb3DQEBBQUAA4IBAQC3jRmEya6sQCkmieULcvx8zz1euCk9\n"
+          + "fSez7BEtki8+dmfMXe3K7sH0lI8f4jJR0rbSCjpmCQLYmzC3NxBKeJOW0RcjNBpO\n"
+          + "c2JlGO9auXv2GDP4IYiXElLJ6VSqc8WvDikv0JmCCWm0Zga+bZbR/EWN5DeEtFdF\n"
+          + "815CLpJZNcYwiYwGy/CVQ7w2TnXlG+mraZOz+owr+cL6J/ZesbdEWfjoS1+cUEhE\n"
+          + "HwlNrAu8jlZ2UqSgskSWlhYdMTAP9CPHiUv9N7FcT58Itv/I4fKREINQYjDpvQcx\n"
+          + "SaTYb9dr5sB4WLNglk7zxDtM80H518VvihTcP7FHL+Gn6g4j5fkI98+S\n"
+          + "-----END CERTIFICATE-----\n").getBytes();
+
+    /**
+     * CN=&#x82b1;&#x5b50;.co.jp
+     */
+    public static final byte[] X509_HANAKO = (
+          "-----BEGIN CERTIFICATE-----\n"
+          + "MIIESzCCAzOgAwIBAgIJAIz+EYMBU6aTMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n"
+          + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n"
+          + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n"
+          + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n"
+          + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE1NDIxNVoXDTI4MTEwNTE1NDIxNVowgakx\n"
+          + "CzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNYXJ5bGFuZDEUMBIGA1UEBwwLRm9yZXN0\n"
+          + "IEhpbGwxFzAVBgNVBAoMDmh0dHBjb21wb25lbnRzMRowGAYDVQQLDBF0ZXN0IGNl\n"
+          + "cnRpZmljYXRlczEVMBMGA1UEAwwM6Iqx5a2QLmNvLmpwMSUwIwYJKoZIhvcNAQkB\n"
+          + "FhZqdWxpdXNkYXZpZXNAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n"
+          + "MIIBCgKCAQEAyGOvloI+jKnRHWKuhYB+cTIEwZhagKJ0f3rIY8WNgujB7PlpgpjU\n"
+          + "g4pNjYGViGjg7zhfbjhCtlNGXyRBti3GcaHiBIIP5nyCNn+Ay8tSWGo5v5Zc8BQc\n"
+          + "wHf0ZHLN6sD9m2uVSp/6UqjS5ZyhzF5FzvvUo3xw8fecdnStXQfHhkBnLpTjHE5t\n"
+          + "7iu1JVjTuE0pcBvah2dWqDNxiIOQtXyKW8Sag1YxaunxQGqRNykSFiEJindxOSAn\n"
+          + "AxK6q/wGqcZ3zvFBTcVVkji1u2QH4rOMP3PPxAIMkB8ONkdHTco1DmbE6BfDHArD\n"
+          + "qUYxqJUlPGlMqrKb3fCFiT3eXehwR7nlzQIDAQABo3sweTAJBgNVHRMEAjAAMCwG\n"
+          + "CWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV\n"
+          + "HQ4EFgQUnxR3vz86tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLS\n"
+          + "rNuzA1LKh6YNPg0wDQYJKoZIhvcNAQEFBQADggEBALJ27i3okV/KvlDp6KMID3gd\n"
+          + "ITl68PyItzzx+SquF8gahMh016NX73z/oVZoVUNdftla8wPUB1GwIkAnGkhQ9LHK\n"
+          + "spBdbRiCj0gMmLCsX8SrjFvr7cYb2cK6J/fJe92l1tg/7Y4o7V/s4JBe/cy9U9w8\n"
+          + "a0ctuDmEBCgC784JMDtT67klRfr/2LlqWhlOEq7pUFxRLbhpquaAHSOjmIcWnVpw\n"
+          + "9BsO7qe46hidgn39hKh1WjKK2VcL/3YRsC4wUi0PBtFW6ScMCuMhgIRXSPU55Rae\n"
+          + "UIlOdPjjr1SUNWGId1rD7W16Scpwnknn310FNxFMHVI0GTGFkNdkilNCFJcIoRA=\n"
+          + "-----END CERTIFICATE-----\n").getBytes();
+
+    /**
+     * CN=foo.com, subjectAlt=bar.com
+     */
+    public static final byte[] X509_FOO_BAR = (
+          "-----BEGIN CERTIFICATE-----\n"
+          + "MIIEXDCCA0SgAwIBAgIJAIz+EYMBU6aRMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n"
+          + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n"
+          + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n"
+          + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n"
+          + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE1MzYyOVoXDTI4MTEwNTE1MzYyOVowgaQx\n"
+          + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n"
+          + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n"
+          + "cnRpZmljYXRlczEQMA4GA1UEAxMHZm9vLmNvbTElMCMGCSqGSIb3DQEJARYWanVs\n"
+          + "aXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n"
+          + "ggEBAMhjr5aCPoyp0R1iroWAfnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2B\n"
+          + "lYho4O84X244QrZTRl8kQbYtxnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRy\n"
+          + "zerA/ZtrlUqf+lKo0uWcocxeRc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY\n"
+          + "07hNKXAb2odnVqgzcYiDkLV8ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8\n"
+          + "BqnGd87xQU3FVZI4tbtkB+KzjD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiV\n"
+          + "JTxpTKqym93whYk93l3ocEe55c0CAwEAAaOBkDCBjTAJBgNVHRMEAjAAMCwGCWCG\n"
+          + "SAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4E\n"
+          + "FgQUnxR3vz86tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuz\n"
+          + "A1LKh6YNPg0wEgYDVR0RBAswCYIHYmFyLmNvbTANBgkqhkiG9w0BAQUFAAOCAQEA\n"
+          + "dQyprNZBmVnvuVWjV42sey/PTfkYShJwy1j0/jcFZR/ypZUovpiHGDO1DgL3Y3IP\n"
+          + "zVQ26uhUsSw6G0gGRiaBDe/0LUclXZoJzXX1qpS55OadxW73brziS0sxRgGrZE/d\n"
+          + "3g5kkio6IED47OP6wYnlmZ7EKP9cqjWwlnvHnnUcZ2SscoLNYs9rN9ccp8tuq2by\n"
+          + "88OyhKwGjJfhOudqfTNZcDzRHx4Fzm7UsVaycVw4uDmhEHJrAsmMPpj/+XRK9/42\n"
+          + "2xq+8bc6HojdtbCyug/fvBZvZqQXSmU8m8IVcMmWMz0ZQO8ee3QkBHMZfCy7P/kr\n"
+          + "VbWx/uETImUu+NZg22ewEw==\n"
+          + "-----END CERTIFICATE-----\n").getBytes();
+
+    /**
+     * CN=foo.com, subjectAlt=bar.com, subjectAlt=&#x82b1;&#x5b50;.co.jp
+     * (hanako.co.jp in kanji)
+     */
+    public static final byte[] X509_FOO_BAR_HANAKO = (
+          "-----BEGIN CERTIFICATE-----\n"
+          + "MIIEajCCA1KgAwIBAgIJAIz+EYMBU6aSMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n"
+          + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n"
+          + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n"
+          + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n"
+          + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE1MzgxM1oXDTI4MTEwNTE1MzgxM1owgaQx\n"
+          + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n"
+          + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n"
+          + "cnRpZmljYXRlczEQMA4GA1UEAxMHZm9vLmNvbTElMCMGCSqGSIb3DQEJARYWanVs\n"
+          + "aXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n"
+          + "ggEBAMhjr5aCPoyp0R1iroWAfnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2B\n"
+          + "lYho4O84X244QrZTRl8kQbYtxnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRy\n"
+          + "zerA/ZtrlUqf+lKo0uWcocxeRc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY\n"
+          + "07hNKXAb2odnVqgzcYiDkLV8ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8\n"
+          + "BqnGd87xQU3FVZI4tbtkB+KzjD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiV\n"
+          + "JTxpTKqym93whYk93l3ocEe55c0CAwEAAaOBnjCBmzAJBgNVHRMEAjAAMCwGCWCG\n"
+          + "SAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4E\n"
+          + "FgQUnxR3vz86tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuz\n"
+          + "A1LKh6YNPg0wIAYDVR0RBBkwF4IHYmFyLmNvbYIM6Iqx5a2QLmNvLmpwMA0GCSqG\n"
+          + "SIb3DQEBBQUAA4IBAQBeZs7ZIYyKtdnVxVvdLgwySEPOE4pBSXii7XYv0Q9QUvG/\n"
+          + "++gFGQh89HhABzA1mVUjH5dJTQqSLFvRfqTHqLpxSxSWqMHnvRM4cPBkIRp/XlMK\n"
+          + "PlXadYtJLPTgpbgvulA1ickC9EwlNYWnowZ4uxnfsMghW4HskBqaV+PnQ8Zvy3L0\n"
+          + "12c7Cg4mKKS5pb1HdRuiD2opZ+Hc77gRQLvtWNS8jQvd/iTbh6fuvTKfAOFoXw22\n"
+          + "sWIKHYrmhCIRshUNohGXv50m2o+1w9oWmQ6Dkq7lCjfXfUB4wIbggJjpyEtbNqBt\n"
+          + "j4MC2x5rfsLKKqToKmNE7pFEgqwe8//Aar1b+Qj+\n"
+          + "-----END CERTIFICATE-----\n").getBytes();
+
+    /**
+     * CN=*.foo.com
+     */
+    public static final byte[] X509_WILD_FOO = (
+          "-----BEGIN CERTIFICATE-----\n"
+          + "MIIESDCCAzCgAwIBAgIJAIz+EYMBU6aUMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n"
+          + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n"
+          + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n"
+          + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n"
+          + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MTU1NVoXDTI4MTEwNTE2MTU1NVowgaYx\n"
+          + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n"
+          + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n"
+          + "cnRpZmljYXRlczESMBAGA1UEAxQJKi5mb28uY29tMSUwIwYJKoZIhvcNAQkBFhZq\n"
+          + "dWxpdXNkYXZpZXNAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n"
+          + "CgKCAQEAyGOvloI+jKnRHWKuhYB+cTIEwZhagKJ0f3rIY8WNgujB7PlpgpjUg4pN\n"
+          + "jYGViGjg7zhfbjhCtlNGXyRBti3GcaHiBIIP5nyCNn+Ay8tSWGo5v5Zc8BQcwHf0\n"
+          + "ZHLN6sD9m2uVSp/6UqjS5ZyhzF5FzvvUo3xw8fecdnStXQfHhkBnLpTjHE5t7iu1\n"
+          + "JVjTuE0pcBvah2dWqDNxiIOQtXyKW8Sag1YxaunxQGqRNykSFiEJindxOSAnAxK6\n"
+          + "q/wGqcZ3zvFBTcVVkji1u2QH4rOMP3PPxAIMkB8ONkdHTco1DmbE6BfDHArDqUYx\n"
+          + "qJUlPGlMqrKb3fCFiT3eXehwR7nlzQIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCG\n"
+          + "SAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4E\n"
+          + "FgQUnxR3vz86tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuz\n"
+          + "A1LKh6YNPg0wDQYJKoZIhvcNAQEFBQADggEBAH0ipG6J561UKUfgkeW7GvYwW98B\n"
+          + "N1ZooWX+JEEZK7+Pf/96d3Ij0rw9ACfN4bpfnCq0VUNZVSYB+GthQ2zYuz7tf/UY\n"
+          + "A6nxVgR/IjG69BmsBl92uFO7JTNtHztuiPqBn59pt+vNx4yPvno7zmxsfI7jv0ww\n"
+          + "yfs+0FNm7FwdsC1k47GBSOaGw38kuIVWqXSAbL4EX9GkryGGOKGNh0qvAENCdRSB\n"
+          + "G9Z6tyMbmfRY+dLSh3a9JwoEcBUso6EWYBakLbq4nG/nvYdYvG9ehrnLVwZFL82e\n"
+          + "l3Q/RK95bnA6cuRClGusLad0e6bjkBzx/VQ3VarDEpAkTLUGVAa0CLXtnyc=\n"
+          + "-----END CERTIFICATE-----\n").getBytes();
+
+    /**
+     * CN=*.co.jp
+     */
+    public static final byte[] X509_WILD_CO_JP = (
+          "-----BEGIN CERTIFICATE-----\n"
+          + "MIIERjCCAy6gAwIBAgIJAIz+EYMBU6aVMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n"
+          + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n"
+          + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n"
+          + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n"
+          + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MTYzMFoXDTI4MTEwNTE2MTYzMFowgaQx\n"
+          + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n"
+          + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n"
+          + "cnRpZmljYXRlczEQMA4GA1UEAxQHKi5jby5qcDElMCMGCSqGSIb3DQEJARYWanVs\n"
+          + "aXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n"
+          + "ggEBAMhjr5aCPoyp0R1iroWAfnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2B\n"
+          + "lYho4O84X244QrZTRl8kQbYtxnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRy\n"
+          + "zerA/ZtrlUqf+lKo0uWcocxeRc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY\n"
+          + "07hNKXAb2odnVqgzcYiDkLV8ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8\n"
+          + "BqnGd87xQU3FVZI4tbtkB+KzjD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiV\n"
+          + "JTxpTKqym93whYk93l3ocEe55c0CAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgB\n"
+          + "hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE\n"
+          + "FJ8Ud78/OrbKOIJCSBYs2tDLXofYMB8GA1UdIwQYMBaAFHua2o+QmU5S0qzbswNS\n"
+          + "yoemDT4NMA0GCSqGSIb3DQEBBQUAA4IBAQA0sWglVlMx2zNGvUqFC73XtREwii53\n"
+          + "CfMM6mtf2+f3k/d8KXhLNySrg8RRlN11zgmpPaLtbdTLrmG4UdAHHYr8O4y2BBmE\n"
+          + "1cxNfGxxechgF8HX10QV4dkyzp6Z1cfwvCeMrT5G/V1pejago0ayXx+GPLbWlNeZ\n"
+          + "S+Kl0m3p+QplXujtwG5fYcIpaGpiYraBLx3Tadih39QN65CnAh/zRDhLCUzKyt9l\n"
+          + "UGPLEUDzRHMPHLnSqT1n5UU5UDRytbjJPXzF+l/+WZIsanefWLsxnkgAuZe/oMMF\n"
+          + "EJMryEzOjg4Tfuc5qM0EXoPcQ/JlheaxZ40p2IyHqbsWV4MRYuFH4bkM\n"
+          + "-----END CERTIFICATE-----\n").getBytes();
+
+    /**
+     * CN=*.foo.com, subjectAlt=*.bar.com, subjectAlt=*.&#x82b1;&#x5b50;.co.jp
+     * (*.hanako.co.jp in kanji)
+     */
+    public static final byte[] X509_WILD_FOO_BAR_HANAKO = (
+          "-----BEGIN CERTIFICATE-----\n"
+          + "MIIEcDCCA1igAwIBAgIJAIz+EYMBU6aWMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n"
+          + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n"
+          + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n"
+          + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n"
+          + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MTczMVoXDTI4MTEwNTE2MTczMVowgaYx\n"
+          + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n"
+          + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n"
+          + "cnRpZmljYXRlczESMBAGA1UEAxQJKi5mb28uY29tMSUwIwYJKoZIhvcNAQkBFhZq\n"
+          + "dWxpdXNkYXZpZXNAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n"
+          + "CgKCAQEAyGOvloI+jKnRHWKuhYB+cTIEwZhagKJ0f3rIY8WNgujB7PlpgpjUg4pN\n"
+          + "jYGViGjg7zhfbjhCtlNGXyRBti3GcaHiBIIP5nyCNn+Ay8tSWGo5v5Zc8BQcwHf0\n"
+          + "ZHLN6sD9m2uVSp/6UqjS5ZyhzF5FzvvUo3xw8fecdnStXQfHhkBnLpTjHE5t7iu1\n"
+          + "JVjTuE0pcBvah2dWqDNxiIOQtXyKW8Sag1YxaunxQGqRNykSFiEJindxOSAnAxK6\n"
+          + "q/wGqcZ3zvFBTcVVkji1u2QH4rOMP3PPxAIMkB8ONkdHTco1DmbE6BfDHArDqUYx\n"
+          + "qJUlPGlMqrKb3fCFiT3eXehwR7nlzQIDAQABo4GiMIGfMAkGA1UdEwQCMAAwLAYJ\n"
+          + "YIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1Ud\n"
+          + "DgQWBBSfFHe/Pzq2yjiCQkgWLNrQy16H2DAfBgNVHSMEGDAWgBR7mtqPkJlOUtKs\n"
+          + "27MDUsqHpg0+DTAkBgNVHREEHTAbggkqLmJhci5jb22CDiou6Iqx5a2QLmNvLmpw\n"
+          + "MA0GCSqGSIb3DQEBBQUAA4IBAQBobWC+D5/lx6YhX64CwZ26XLjxaE0S415ajbBq\n"
+          + "DK7lz+Rg7zOE3GsTAMi+ldUYnhyz0wDiXB8UwKXl0SDToB2Z4GOgqQjAqoMmrP0u\n"
+          + "WB6Y6dpkfd1qDRUzI120zPYgSdsXjHW9q2H77iV238hqIU7qCvEz+lfqqWEY504z\n"
+          + "hYNlknbUnR525ItosEVwXFBJTkZ3Yw8gg02c19yi8TAh5Li3Ad8XQmmSJMWBV4XK\n"
+          + "qFr0AIZKBlg6NZZFf/0dP9zcKhzSriW27bY0XfzA6GSiRDXrDjgXq6baRT6YwgIg\n"
+          + "pgJsDbJtZfHnV1nd3M6zOtQPm1TIQpNmMMMd/DPrGcUQerD3\n"
+          + "-----END CERTIFICATE-----\n").getBytes();
+
+    /**
+     * CN=foo.com, CN=bar.com, CN=&#x82b1;&#x5b50;.co.jp
+     */
+    public static final byte[] X509_THREE_CNS_FOO_BAR_HANAKO = (
+          "-----BEGIN CERTIFICATE-----\n"
+          + "MIIEbzCCA1egAwIBAgIJAIz+EYMBU6aXMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n"
+          + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n"
+          + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n"
+          + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n"
+          + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MTk0NVoXDTI4MTEwNTE2MTk0NVowgc0x\n"
+          + "CzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNYXJ5bGFuZDEUMBIGA1UEBwwLRm9yZXN0\n"
+          + "IEhpbGwxFzAVBgNVBAoMDmh0dHBjb21wb25lbnRzMRowGAYDVQQLDBF0ZXN0IGNl\n"
+          + "cnRpZmljYXRlczEQMA4GA1UEAwwHZm9vLmNvbTEQMA4GA1UEAwwHYmFyLmNvbTEV\n"
+          + "MBMGA1UEAwwM6Iqx5a2QLmNvLmpwMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n"
+          + "ZXNAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyGOv\n"
+          + "loI+jKnRHWKuhYB+cTIEwZhagKJ0f3rIY8WNgujB7PlpgpjUg4pNjYGViGjg7zhf\n"
+          + "bjhCtlNGXyRBti3GcaHiBIIP5nyCNn+Ay8tSWGo5v5Zc8BQcwHf0ZHLN6sD9m2uV\n"
+          + "Sp/6UqjS5ZyhzF5FzvvUo3xw8fecdnStXQfHhkBnLpTjHE5t7iu1JVjTuE0pcBva\n"
+          + "h2dWqDNxiIOQtXyKW8Sag1YxaunxQGqRNykSFiEJindxOSAnAxK6q/wGqcZ3zvFB\n"
+          + "TcVVkji1u2QH4rOMP3PPxAIMkB8ONkdHTco1DmbE6BfDHArDqUYxqJUlPGlMqrKb\n"
+          + "3fCFiT3eXehwR7nlzQIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQf\n"
+          + "Fh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUnxR3vz86\n"
+          + "tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuzA1LKh6YNPg0w\n"
+          + "DQYJKoZIhvcNAQEFBQADggEBAGuZb8ai1NO2j4v3y9TLZvd5s0vh5/TE7n7RX+8U\n"
+          + "y37OL5k7x9nt0mM1TyAKxlCcY+9h6frue8MemZIILSIvMrtzccqNz0V1WKgA+Orf\n"
+          + "uUrabmn+CxHF5gpy6g1Qs2IjVYWA5f7FROn/J+Ad8gJYc1azOWCLQqSyfpNRLSvY\n"
+          + "EriQFEV63XvkJ8JrG62b+2OT2lqT4OO07gSPetppdlSa8NBSKP6Aro9RIX1ZjUZQ\n"
+          + "SpQFCfo02NO0uNRDPUdJx2huycdNb+AXHaO7eXevDLJ+QnqImIzxWiY6zLOdzjjI\n"
+          + "VBMkLHmnP7SjGSQ3XA4ByrQOxfOUTyLyE7NuemhHppuQPxE=\n"
+          + "-----END CERTIFICATE-----\n").getBytes();
+
+    /**
+     * subjectAlt=foo.com
+     */
+    public static final byte[] X509_NO_CNS_FOO = (
+          "-----BEGIN CERTIFICATE-----\n"
+          + "MIIESjCCAzKgAwIBAgIJAIz+EYMBU6aYMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n"
+          + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n"
+          + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n"
+          + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n"
+          + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MjYxMFoXDTI4MTEwNTE2MjYxMFowgZIx\n"
+          + "CzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNYXJ5bGFuZDEUMBIGA1UEBwwLRm9yZXN0\n"
+          + "IEhpbGwxFzAVBgNVBAoMDmh0dHBjb21wb25lbnRzMRowGAYDVQQLDBF0ZXN0IGNl\n"
+          + "cnRpZmljYXRlczElMCMGCSqGSIb3DQEJARYWanVsaXVzZGF2aWVzQGdtYWlsLmNv\n"
+          + "bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMhjr5aCPoyp0R1iroWA\n"
+          + "fnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2BlYho4O84X244QrZTRl8kQbYt\n"
+          + "xnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRyzerA/ZtrlUqf+lKo0uWcocxe\n"
+          + "Rc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY07hNKXAb2odnVqgzcYiDkLV8\n"
+          + "ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8BqnGd87xQU3FVZI4tbtkB+Kz\n"
+          + "jD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiVJTxpTKqym93whYk93l3ocEe5\n"
+          + "5c0CAwEAAaOBkDCBjTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NM\n"
+          + "IEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUnxR3vz86tso4gkJIFiza\n"
+          + "0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuzA1LKh6YNPg0wEgYDVR0RBAsw\n"
+          + "CYIHZm9vLmNvbTANBgkqhkiG9w0BAQUFAAOCAQEAjl78oMjzFdsMy6F1sGg/IkO8\n"
+          + "tF5yUgPgFYrs41yzAca7IQu6G9qtFDJz/7ehh/9HoG+oqCCIHPuIOmS7Sd0wnkyJ\n"
+          + "Y7Y04jVXIb3a6f6AgBkEFP1nOT0z6kjT7vkA5LJ2y3MiDcXuRNMSta5PYVnrX8aZ\n"
+          + "yiqVUNi40peuZ2R8mAUSBvWgD7z2qWhF8YgDb7wWaFjg53I36vWKn90ZEti3wNCw\n"
+          + "qAVqixM+J0qJmQStgAc53i2aTMvAQu3A3snvH/PHTBo+5UL72n9S1kZyNCsVf1Qo\n"
+          + "n8jKTiRriEM+fMFlcgQP284EBFzYHyCXFb9O/hMjK2+6mY9euMB1U1aFFzM/Bg==\n"
+          + "-----END CERTIFICATE-----\n").getBytes();
+
+    /**
+     * Intermediate CA for all of these.
+     */
+    public static final byte[] X509_INTERMEDIATE_CA = (
+          "-----BEGIN CERTIFICATE-----\n"
+          + "MIIEnDCCA4SgAwIBAgIJAJTNwZ6yNa5cMA0GCSqGSIb3DQEBBQUAMIGGMQswCQYD\n"
+          + "VQQGEwJDQTELMAkGA1UECBMCQkMxFjAUBgNVBAoTDXd3dy5jdWNiYy5jb20xFDAS\n"
+          + "BgNVBAsUC2NvbW1vbnNfc3NsMRUwEwYDVQQDFAxkZW1vX3Jvb3RfY2ExJTAjBgkq\n"
+          + "hkiG9w0BCQEWFmp1bGl1c2Rhdmllc0BnbWFpbC5jb20wHhcNMDYxMTA1MjE0OTMx\n"
+          + "WhcNMDcxMTA1MjE0OTMxWjCBojELMAkGA1UEBhMCQ0ExCzAJBgNVBAgTAkJDMRIw\n"
+          + "EAYDVQQHEwlWYW5jb3V2ZXIxFjAUBgNVBAoTDXd3dy5jdWNiYy5jb20xFDASBgNV\n"
+          + "BAsUC2NvbW1vbnNfc3NsMR0wGwYDVQQDFBRkZW1vX2ludGVybWVkaWF0ZV9jYTEl\n"
+          + "MCMGCSqGSIb3DQEJARYWanVsaXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZI\n"
+          + "hvcNAQEBBQADggEPADCCAQoCggEBAL0S4y3vUO0EM6lwqOEfK8fvrUprIbsikXaG\n"
+          + "XzejcZ+T3l2Dc7t8WtBfRf78i4JypMqJQSijrUicj3H6mOMIReKaXm6ls4hA5d8w\n"
+          + "Lhmgiqsz/kW+gA8SeWGWRN683BD/RbQmzOls6ynBvap9jZlthXWBrSIlPCQoBLXY\n"
+          + "KVaxGzbL4ezaq+XFMKMQSm2uKwVmHHQNbfmZlPsuendBVomb/ked53Ab9IH6dwwN\n"
+          + "qJH9WIrvIzIVEXWlpvQ5MCqozM7u1akU+G8cazr8theGPCaYkzoXnigWua4OjdpV\n"
+          + "9z5ZDknhfBzG1AjapdG07FIirwWWgIyZXqZSD96ikmLtwT29qnsCAwEAAaOB7jCB\n"
+          + "6zAdBgNVHQ4EFgQUe5raj5CZTlLSrNuzA1LKh6YNPg0wgbsGA1UdIwSBszCBsIAU\n"
+          + "rN8eFIvMiRFXXgDqKumS0/W2AhOhgYykgYkwgYYxCzAJBgNVBAYTAkNBMQswCQYD\n"
+          + "VQQIEwJCQzEWMBQGA1UEChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9u\n"
+          + "c19zc2wxFTATBgNVBAMUDGRlbW9fcm9vdF9jYTElMCMGCSqGSIb3DQEJARYWanVs\n"
+          + "aXVzZGF2aWVzQGdtYWlsLmNvbYIJAJTNwZ6yNa5bMAwGA1UdEwQFMAMBAf8wDQYJ\n"
+          + "KoZIhvcNAQEFBQADggEBAIB4KMZvHD20pdKajFtMBpL7X4W4soq6EeTtjml3NYa9\n"
+          + "Qc52bsQEGNccKY9afYSBIndaQvFdtmz6HdoN+B8TjYShw2KhyjtKimGLpWYoi1YF\n"
+          + "e4aHdmA/Gp5xk8pZzR18FmooxC9RqBux+NAM2iTFSLgDtGIIj4sg2rbn6Bb6ZlQT\n"
+          + "1rg6VucXCA1629lNfMeNcu7CBNmUKIdaxHR/YJQallE0KfGRiOIWPrPj/VNk0YA6\n"
+          + "XFg0ocjqXJ2/N0N9rWVshMUaXgOh7m4D/5zga5/nuxDU+PoToA6mQ4bV6eCYqZbh\n"
+          + "aa1kQYtR9B4ZiG6pB82qVc2dCqStOH2FAEWos2gAVkQ=\n"
+          + "-----END CERTIFICATE-----\n").getBytes();
+
+    /**
+     * Root CA for all of these.
+     */
+    public static final byte[] X509_ROOT_CA = (
+          "-----BEGIN CERTIFICATE-----\n"
+          + "MIIEgDCCA2igAwIBAgIJAJTNwZ6yNa5bMA0GCSqGSIb3DQEBBQUAMIGGMQswCQYD\n"
+          + "VQQGEwJDQTELMAkGA1UECBMCQkMxFjAUBgNVBAoTDXd3dy5jdWNiYy5jb20xFDAS\n"
+          + "BgNVBAsUC2NvbW1vbnNfc3NsMRUwEwYDVQQDFAxkZW1vX3Jvb3RfY2ExJTAjBgkq\n"
+          + "hkiG9w0BCQEWFmp1bGl1c2Rhdmllc0BnbWFpbC5jb20wHhcNMDYxMTA1MjEzNjQz\n"
+          + "WhcNMjYxMTA1MjEzNjQzWjCBhjELMAkGA1UEBhMCQ0ExCzAJBgNVBAgTAkJDMRYw\n"
+          + "FAYDVQQKEw13d3cuY3VjYmMuY29tMRQwEgYDVQQLFAtjb21tb25zX3NzbDEVMBMG\n"
+          + "A1UEAxQMZGVtb19yb290X2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZpZXNA\n"
+          + "Z21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv+OnocmJ\n"
+          + "79UeO2hlCwK+Cle5uZWnU6uwJl+08z5cvebb5tT64WL9+psDbfgUH/Gm9JsuxKTg\n"
+          + "w1tZO/4duIgnaLNSx4HoqaTjwigd/hR3TsoGEPXTCkz1ikgTCOEDvl+iMid6aOrd\n"
+          + "mViE8HhscxKZ+h5FE7oHZyuT6gFoiaIXhFq+xK2w4ZwDz9L+paiwqywyUJJMnh9U\n"
+          + "jKorY+nua81N0oxpIhHPspCanDU4neMzCzYOZyLR/LqV5xORvHcFY84GWMz5hI25\n"
+          + "JbgaWJsYKuCAvNsnQwVoqKPGa7x1fn7x6oGsXJaCVt8weUwIj2xwg1lxMhrNaisH\n"
+          + "EvKpEAEnGGwWKQIDAQABo4HuMIHrMB0GA1UdDgQWBBSs3x4Ui8yJEVdeAOoq6ZLT\n"
+          + "9bYCEzCBuwYDVR0jBIGzMIGwgBSs3x4Ui8yJEVdeAOoq6ZLT9bYCE6GBjKSBiTCB\n"
+          + "hjELMAkGA1UEBhMCQ0ExCzAJBgNVBAgTAkJDMRYwFAYDVQQKEw13d3cuY3VjYmMu\n"
+          + "Y29tMRQwEgYDVQQLFAtjb21tb25zX3NzbDEVMBMGA1UEAxQMZGVtb19yb290X2Nh\n"
+          + "MSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZpZXNAZ21haWwuY29tggkAlM3BnrI1\n"
+          + "rlswDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAlPl3/8h1LttR1svC\n"
+          + "S8RXbHpAWIT2BEDhGHUNjSmgDQNkE/itf/FCEXh0tlU4bYdtBSOHzflbnzOyIPId\n"
+          + "VZeSWs33V38xDFy6KoVg1gT8JxkLmE5S1vWkpsHIlpw/U6r7KD0Kx9FYx5AiXjw0\n"
+          + "lzz/zlVNuO2U09KIDwDPVG1mBzQiMiSWj1U1pM4KxINkWQwDy/fvu/I983s8lW5z\n"
+          + "hf2WuFNzQN3fcMK5dpBE9NVIu27oYuGYh2sak34v+7T700W2ooBB71qFXtm9P5rl\n"
+          + "Yp9RCEsg3KEEPNTtCBs8fROeXvLDrP0cmBIqwGYDuRNCxFDTOdjv6YGdA8nLOjaH\n"
+          + "2dDk0g==\n"
+          + "-----END CERTIFICATE-----\n").getBytes();
+
+    /**
+     * Below is the private key for all the server certificates above (but
+     * not the intermediate CA or the root CA).  All of those server certs
+     * came from the same private key.
+     */
+    public static final String RSA_PUBLIC_MODULUS =
+          "00c863af96823e8ca9d11d62ae85807e713204c1985a80a2747f7ac863c5"
+          + "8d82e8c1ecf9698298d4838a4d8d81958868e0ef385f6e3842b653465f24"
+          + "41b62dc671a1e204820fe67c82367f80cbcb52586a39bf965cf0141cc077"
+          + "f46472cdeac0fd9b6b954a9ffa52a8d2e59ca1cc5e45cefbd4a37c70f1f7"
+          + "9c7674ad5d07c78640672e94e31c4e6dee2bb52558d3b84d29701bda8767"
+          + "56a83371888390b57c8a5bc49a8356316ae9f1406a913729121621098a77"
+          + "713920270312baabfc06a9c677cef1414dc5559238b5bb6407e2b38c3f73"
+          + "cfc4020c901f0e3647474dca350e66c4e817c31c0ac3a94631a895253c69"
+          + "4caab29bddf085893dde5de87047b9e5cd";
+
+    public static final String RSA_PUBLIC_EXPONENT = "65537";
+
+    public static final String RSA_PRIVATE_EXPONENT =
+          "577abd3295553d0efd4d38c13b62a6d03fa7b7e40cce4f1d5071877d96c6"
+          + "7a39a63f0f7ab21a89db8acae45587b3ef251309a70f74dc1ac02bde68f3"
+          + "8ed658e54e685ed370a18c054449512ea66a2252ed36e82b565b5159ec83"
+          + "f23df40ae189550a183865b25fd77789e960f0d8cedcd72f32d7a66edb4b"
+          + "a0a2baf3fbeb6c7d75f56ef0af9a7cff1c8c7f297d72eae7982164e50a89"
+          + "d450698cf598d39343201094241d2d180a95882a7111e58f4a5bdbc5c125"
+          + "a967dd6ed9ec614c5853e88e4c71e8b682a7cf89cb1d82b6fe78cc865084"
+          + "c8c5dfbb50c939df2b839c977b0245bfa3615e0592b527b1013d5b675ecb"
+          + "44e6b355c1df581f50997175166eef39";
+
+    public static final String RSA_PRIME1 =
+          "00fe759c4f0ce8b763880215e82767e7a937297668f4e4b1e119c6b22a3c"
+          + "a2c7b06c547d88d0aa45f645d7d3aeadaf7f8bc594deae0978529592977c"
+          + "b1ff890f05033a9e9e15551cad9fbf9c41d12139ccd99c1c3ac7b2197eff"
+          + "350d236bb900c1440953b64956e0a058ef824a2e16894af175177c77dbe1"
+          + "fef7d8b532608d2513";
+
+    public static final String RSA_PRIME2 =
+          "00c99a45878737a4cf73f9896680b75487f1b669b7686a6ba07103856f31"
+          + "db668c2c440c44cdd116f708f631c37a9adf119f5b5cb58ffe3dc62e20af"
+          + "af72693d936dc6bb3c5194996468389c1f094079b81522e94572b4ad7d39"
+          + "529178e9b8ebaeb1f0fdd83b8731c5223f1dea125341d1d64917f6b1a6ae"
+          + "c18d320510d79f859f";
+
+    public static final String RSA_EXPONENT1 =
+          "029febf0d4cd41b7011c2465b4a259bd6118486464c247236f44a169d61e"
+          + "47b9062508f674508d5031003ceabc57e714e600d71b2c75d5443db2da52"
+          + "6bb45a374f0537c5a1aab3150764ce93cf386c84346a6bd01f6732e42075"
+          + "c7a0e9e78a9e73b934e7d871d0f75673820089e129a1604438edcbbeb4e2"
+          + "106467da112ce389";
+
+    public static final String RSA_EXPONENT2 =
+          "00827e76650c946afcd170038d32e1f8386ab00d6be78d830efe382e45d4"
+          + "7ad4bd04e6231ee22e66740efbf52838134932c9f8c460cdccdec58a1424"
+          + "4427859192fd6ab6c58b74e97941b0eaf577f2a11713af5e5952af3ae124"
+          + "9a9a892e98410dfa2628d9af668a43b5302fb7d496c9b2fec69f595292b6"
+          + "e997f079b0f6314eb7";
+
+    public static final String RSA_COEFFICIENT =
+          "00e6b62add350f1a2a8968903ff76c31cf703b0d7326c4a620aef01225b7"
+          + "1640b3f2ec375208c5f7299863f6005b7799b6e529bb1133c8435bf5fdb5"
+          + "a786f6cd8a19ee7094a384e6557c600a38845a0960ddbfd1df18d0af5740"
+          + "001853788f1b5ccbf9affb4c52c9d2efdb8aab0183d86735b32737fb4e79"
+          + "2b8a9c7d91c7d175ae";
+
+    /**
+     * subjectAlt=IP Address:127.0.0.1, email:oleg@ural.ru, DNS:localhost.localdomain
+     */
+    public static final byte[] X509_MULTIPLE_SUBJECT_ALT = (
+        "-----BEGIN CERTIFICATE-----\n"
+        + "MIIDcTCCAtqgAwIBAgIBATANBgkqhkiG9w0BAQUFADBAMQswCQYDVQQGEwJDSDEL\n"
+        + "MAkGA1UECBMCWkgxDzANBgNVBAcTBlp1cmljaDETMBEGA1UEAxMKTXkgVGVzdCBD\n"
+        + "QTAeFw0wODEwMzExMTU3NDVaFw0wOTEwMzExMTU3NDVaMGkxCzAJBgNVBAYTAkNI\n"
+        + "MRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYDVQQKEwdV\n"
+        + "bmtub3duMRAwDgYDVQQLEwdVbmtub3duMRIwEAYDVQQDEwlsb2NhbGhvc3QwggG4\n"
+        + "MIIBLAYHKoZIzjgEATCCAR8CgYEA/X9TgR11EilS30qcLuzk5/YRt1I870QAwx4/\n"
+        + "gLZRJmlFXUAiUftZPY1Y+r/F9bow9subVWzXgTuAHTRv8mZgt2uZUKWkn5/oBHsQ\n"
+        + "IsJPu6nX/rfGG/g7V+fGqKYVDwT7g/bTxR7DAjVUE1oWkTL2dfOuK2HXKu/yIgMZ\n"
+        + "ndFIAccCFQCXYFCPFSMLzLKSuYKi64QL8Fgc9QKBgQD34aCF1ps93su8q1w2uFe5\n"
+        + "eZSvu/o66oL5V0wLPQeCZ1FZV4661FlP5nEHEIGAtEkWcSPoTCgWE7fPCTKMyKbh\n"
+        + "PBZ6i1R8jSjgo64eK7OmdZFuo38L+iE1YvH7YnoBJDvMpPG+qFGQiaiD3+Fa5Z8G\n"
+        + "kotmXoB7VSVkAUw7/s9JKgOBhQACgYEA6ogAb/YLM1Rz9AoXKW4LA70VtFf7Mqqp\n"
+        + "divdu9f72WQc1vMKo1YMf3dQadkMfBYRvAAa1IXDnoiFCHhXnVRkWkoUBJyNebLB\n"
+        + "N92CZc0RVFZiMFgQMEh8UldnvAIi4cBk0/YuN3BGl4MzmquVIGrFovdWGqeaveOu\n"
+        + "Xcu4lKGJNiqjODA2MDQGA1UdEQQtMCuHBH8AAAGBDG9sZWdAdXJhbC5ydYIVbG9j\n"
+        + "YWxob3N0LmxvY2FsZG9tYWluMA0GCSqGSIb3DQEBBQUAA4GBAIgEwIoCSRkU3O7K\n"
+        + "USYaOYyfJB9hsvs6YpClvYXiQ/5kPGARP60pM62v4wC7wI9shEizokIAxY2+O3cC\n"
+        + "vwuJhNYaa2FJMELIwRN3XES8X8R6JHWbPaRjaAAPhczuEd8SZYy8yiVLmJTgw0gH\n"
+        + "BSW775NHlkjsscFVgXkNf0PobqJ9\n"
+        + "-----END CERTIFICATE-----").getBytes();
+
+    /**
+     * subject CN=repository.infonotary.com (Multiple AVA in RDN).
+     */
+    public static final byte[] X509_MULTIPLE_VALUE_AVA = (
+        "-----BEGIN CERTIFICATE-----\n"
+        + "MIIFxzCCBK+gAwIBAgIIRO/2+/XA7z4wDQYJKoZIhvcNAQEFBQAwgZwxgZkwCQYD\n"
+        + "VQQGDAJCRzAVBgNVBAoMDkluZm9Ob3RhcnkgUExDMBcGCgmSJomT8ixkARkWCWRv\n"
+        + "bWFpbi1jYTAtBgNVBAMMJmktTm90YXJ5IFRydXN0UGF0aCBWYWxpZGF0ZWQgRG9t\n"
+        + "YWluIENBMC0GA1UECwwmaS1Ob3RhcnkgVHJ1c3RQYXRoIFZhbGlkYXRlZCBEb21h\n"
+        + "aW4gQ0EwHhcNMTIwNjE4MDg1MzIyWhcNMTMwNjE4MDg1MzIyWjCBxjGBwzAJBgNV\n"
+        + "BAYTAkJHMBUGA1UEChMOSW5mb05vdGFyeSBQTEMwFwYDVQQLExBGaWxlcyBSZXBv\n"
+        + "c2l0b3J5MBcGCgmSJomT8ixkARkWCWRvbWFpbi1jYTAgBgNVBAMTGXJlcG9zaXRv\n"
+        + "cnkuaW5mb25vdGFyeS5jb20wIwYJKoZIhvcNAQkBFhZzdXBwb3J0QGluZm9ub3Rh\n"
+        + "cnkuY29tMCYGCSqGSIb3DQEJAhMZcmVwb3NpdG9yeS5pbmZvbm90YXJ5LmNvbTCC\n"
+        + "ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALKWjGpgsuz103xVEW/GSg5I\n"
+        + "tBoLbXPxockabOTHnOh0VO2sImycyhBH78nMj+VMexn4y+kdCOuJqAA5LApxyhTA\n"
+        + "KgKlRN7TfoC90IYHjB1dqLMIseg4YM7Oe0e4Z2nL50bHoqXg7OUHaILUQn7ufpYp\n"
+        + "+VCWxyI43KvaR4+HnST3x47wqeArg/rULGV1a16X+46cxq2eoMAcDfostXHaemvz\n"
+        + "vg/Wd5xcWfPbF/oY1/sBXH+AK+peVBMen82+3GtAWtNWbyPE3bT4RG+WgKUyfLZ1\n"
+        + "7A67rX9DkUEVMPQpa50MpLnrRveiM9w6R3mrMHMHbNnwID0Tqfds5zzOi/7cLD0C\n"
+        + "AwEAAaOCAd8wggHbMA4GA1UdDwEB/wQEAwIDuDATBgNVHSUEDDAKBggrBgEFBQcD\n"
+        + "ATBEBggrBgEFBQcBAQQ4MDYwNAYIKwYBBQUHMAGGKGh0dHA6Ly9vY3NwLmluZm9u\n"
+        + "b3RhcnkuY29tL3Jlc3BvbmRlci5jZ2kwgZAGA1UdIASBiDCBhTCBggYMKwYBBAGB\n"
+        + "rQABAgMBMHIwOAYIKwYBBQUHAgEWLGh0dHA6Ly9yZXBvc2l0b3J5LmluZm9ub3Rh\n"
+        + "cnkuY29tL2RvbWFpbi5odG1sMDYGCCsGAQUFBwICMCoaKGktTm90YXJ5IFZhbGlk\n"
+        + "YXRlZCBEb21haW4gQ2VydGlmaWNhdGUgQ1AwgYkGA1UdHwSBgTB/MDWgL6Athito\n"
+        + "dHRwOi8vY3JsLmluZm9ub3RhcnkuY29tL2NybC9kb21haW4tY2EuY3JsgQIBVjBG\n"
+        + "oECgPoY8bGRhcDovL2xkYXAuaW5mb25vdGFyeS5jb20vZGM9ZG9tYWluLWNhLGRj\n"
+        + "PWluZm9ub3RhcnksZGM9Y29tgQIBVjAPBgNVHRMBAf8EBTADAQEAMB0GA1UdDgQW\n"
+        + "BBTImKJZrgV/8n7mHrA0U5EeGsBvbzAfBgNVHSMEGDAWgBTbkorEK+bPdVPpvyVI\n"
+        + "PTxGFnuOoDANBgkqhkiG9w0BAQUFAAOCAQEAhsMbqsqvkbfVaKZ+wDY9rX3EtuDS\n"
+        + "isdAo4AjmWgTtj/aBGiEiXcIGP312x+0JF+mEEQ75ZOKN+WsM8eLB0F4aqylklk7\n"
+        + "6yRYauRXp8dfbXrT3ozxekt0cpSMqbzze456krI12nL+C00V2Iwq96k5J/yZboNW\n"
+        + "Q+ibCaEAHNiL4tGVHSHm6znkWvIuUTbDgDEsm5RdafO27suz5H6zMnV+VE6onN1J\n"
+        + "I1mQmUs44cg2HZAqnFBpDyJQhNYy8M7yGVaRkbfuVaMqiPa+xDPR5v7NFB3kxRq2\n"
+        + "Za2Snopi52eUxDEhJ0MNqFi3Jfj/ZSmJ+XHra5lU4R8lijCAq8SVLZCmIQ==\n"
+        + "-----END CERTIFICATE-----").getBytes();
+    
+    private CertificatesToPlayWith() {
+        //
+    }
+}