Return-Path: X-Original-To: apmail-cxf-commits-archive@www.apache.org Delivered-To: apmail-cxf-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 0FBDCC790 for ; Wed, 17 Dec 2014 16:41:21 +0000 (UTC) Received: (qmail 19270 invoked by uid 500); 17 Dec 2014 16:41:21 -0000 Delivered-To: apmail-cxf-commits-archive@cxf.apache.org Received: (qmail 19147 invoked by uid 500); 17 Dec 2014 16:41:20 -0000 Mailing-List: contact commits-help@cxf.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cxf.apache.org Delivered-To: mailing list commits@cxf.apache.org Received: (qmail 18880 invoked by uid 99); 17 Dec 2014 16:41:20 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 17 Dec 2014 16:41:20 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 7F98782EF87; Wed, 17 Dec 2014 16:41:20 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: coheigea@apache.org To: commits@cxf.apache.org Date: Wed, 17 Dec 2014 16:41:22 -0000 Message-Id: <908304bb3f46434bab584449124f1470@git.apache.org> In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [3/4] cxf git commit: [CXF-6143] - SSL/TLS hostname verification does not strictly follow HTTPS RFC2818 - Code ported from httpclient 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 + * . + * + */ + +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) { + // + } + } + +}