diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt index 6ee19b4b5..60e1df40a 100644 --- a/RELEASE_NOTES.txt +++ b/RELEASE_NOTES.txt @@ -1,6 +1,9 @@ Changes since 4.3 ALPHA1 ------------------- +* [HTTPCLIENT-1316] Certificate verification rejects IPv6 addresses which are not String-equal. + Contributed Sebastian Bazley . + * [HTTPCLIENT-1307] Future based asynchronous request execution. Contributed by Jilles van Gurp diff --git a/httpclient/src/main/java/org/apache/http/conn/ssl/AbstractVerifier.java b/httpclient/src/main/java/org/apache/http/conn/ssl/AbstractVerifier.java index 1220b1704..6ae50b74e 100644 --- a/httpclient/src/main/java/org/apache/http/conn/ssl/AbstractVerifier.java +++ b/httpclient/src/main/java/org/apache/http/conn/ssl/AbstractVerifier.java @@ -29,6 +29,8 @@ package org.apache.http.conn.ssl; import java.io.IOException; import java.io.InputStream; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.security.cert.Certificate; import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; @@ -44,6 +46,8 @@ import javax.net.ssl.SSLException; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.apache.http.annotation.Immutable; import org.apache.http.conn.util.InetAddressUtils; @@ -75,6 +79,8 @@ public abstract class AbstractVerifier implements X509HostnameVerifier { Arrays.sort(BAD_COUNTRY_2LDS); } + private final Log log = LogFactory.getLog(getClass()); + public AbstractVerifier() { super(); } @@ -177,7 +183,7 @@ public abstract class AbstractVerifier implements X509HostnameVerifier { // We're can be case-insensitive when comparing the host we used to // establish the socket to the hostname in the certificate. - final String hostName = host.trim().toLowerCase(Locale.US); + final String hostName = normaliseIPv6Address(host.trim().toLowerCase(Locale.US)); boolean match = false; for(final Iterator it = names.iterator(); it.hasNext();) { // Don't trim the CN, though! @@ -216,7 +222,7 @@ public abstract class AbstractVerifier implements X509HostnameVerifier { match = countDots(hostName) == countDots(cn); } } else { - match = hostName.equals(cn); + match = hostName.equals(normaliseIPv6Address(cn)); } if(match) { break; @@ -361,4 +367,19 @@ public abstract class AbstractVerifier implements X509HostnameVerifier { InetAddressUtils.isIPv6Address(hostname)); } + /* + * Check if hostname is IPv6, and if so, convert to standard format. + */ + private String normaliseIPv6Address(final String hostname) { + if (hostname == null || !InetAddressUtils.isIPv6Address(hostname)) { + return hostname; + } + try { + InetAddress inetAddress = InetAddress.getByName(hostname); + return inetAddress.getHostAddress(); + } catch (UnknownHostException uhe) { // Should not happen, because we check for IPv6 address above + log.error("Unexpected error converting "+hostname, uhe); + return hostname; + } + } } diff --git a/httpclient/src/test/java/org/apache/http/conn/ssl/TestHostnameVerifier.java b/httpclient/src/test/java/org/apache/http/conn/ssl/TestHostnameVerifier.java index 39dc19245..0986d4bba 100644 --- a/httpclient/src/test/java/org/apache/http/conn/ssl/TestHostnameVerifier.java +++ b/httpclient/src/test/java/org/apache/http/conn/ssl/TestHostnameVerifier.java @@ -301,6 +301,22 @@ public class TestHostnameVerifier { } + @Test + // Check compressed IPv6 hostname matching + public void testHTTPCLIENT_1316() throws Exception{ + String cns[] = {"2001:0db8:aaaa:bbbb:cccc:0:0:0001"}; + String alt[] = {}; + final X509HostnameVerifier bhv = new BrowserCompatHostnameVerifier(); + final X509HostnameVerifier shv = new StrictHostnameVerifier(); + checkMatching(bhv, "2001:0db8:aaaa:bbbb:cccc:0:0:0001", cns, alt, false); + checkMatching(shv, "2001:0db8:aaaa:bbbb:cccc:0:0:0001", cns, alt, false); + checkMatching(bhv, "2001:0db8:aaaa:bbbb:cccc::1", cns, alt, false); + checkMatching(shv, "2001:0db8:aaaa:bbbb:cccc::1", cns, alt, false); + checkMatching(bhv, "2001:0db8:aaaa:bbbb:cccc::10", cns, alt, true); + checkMatching(shv, "2001:0db8:aaaa:bbbb:cccc::10", cns, alt, true); + // TODO need some more samples + } + @Test public void testHTTPCLIENT_1097() { String cns[];