diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/DefaultHostnameVerifier.java b/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/DefaultHostnameVerifier.java index 9b7418bf38..e7f3d3785e 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/DefaultHostnameVerifier.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/DefaultHostnameVerifier.java @@ -355,7 +355,14 @@ static List getSubjectAltNames(final X509Certificate cert, final in if (o instanceof String) { result.add(new SubjectName((String) o, type)); } else if (o instanceof byte[]) { - // TODO ASN.1 DER encoded form + final byte[] bytes = (byte[]) o; + if (type == SubjectName.IP) { + if (bytes.length == 4) { + result.add(new SubjectName(byteArrayToIp(bytes), type)); // IPv4 + } else if (bytes.length == 16) { + result.add(new SubjectName(byteArrayToIPv6(bytes), type)); // IPv6 + } + } } } } @@ -380,4 +387,29 @@ static String normaliseAddress(final String hostname) { return hostname; } } + + private static String byteArrayToIp(final byte[] bytes) { + if (bytes.length != 4) { + throw new IllegalArgumentException("Invalid byte array length for IPv4 address"); + } + return (bytes[0] & 0xFF) + "." + + (bytes[1] & 0xFF) + "." + + (bytes[2] & 0xFF) + "." + + (bytes[3] & 0xFF); + } + + private static String byteArrayToIPv6(final byte[] bytes) { + if (bytes.length != 16) { + throw new IllegalArgumentException("Invalid byte array length for IPv6 address"); + } + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < bytes.length; i += 2) { + sb.append(String.format("%02x%02x", bytes[i], bytes[i + 1])); + if (i < bytes.length - 2) { + sb.append(":"); + } + } + return sb.toString(); + } + } diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/ssl/TestDefaultHostnameVerifier.java b/httpclient5/src/test/java/org/apache/hc/client5/http/ssl/TestDefaultHostnameVerifier.java index 6ca2ac488d..38da6e2c78 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/ssl/TestDefaultHostnameVerifier.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/ssl/TestDefaultHostnameVerifier.java @@ -31,12 +31,28 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.math.BigInteger; +import java.net.InetAddress; import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Principal; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateExpiredException; import java.security.cert.CertificateFactory; +import java.security.cert.CertificateNotYetValidException; import java.security.cert.X509Certificate; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; +import java.util.Date; import java.util.List; +import java.util.Set; import javax.net.ssl.SSLException; @@ -548,4 +564,193 @@ void testMatchIdentity() { ); } + + @Test + void testSimulatedByteProperties() throws Exception { + // Simulated byte array for an IP address + final byte[] ipAsByteArray = {1, 1, 1, 1}; // 1.1.1.1 in byte form + + final List> entries = new ArrayList<>(); + final List entry = new ArrayList<>(); + entry.add(SubjectName.IP); + entry.add(ipAsByteArray); + entries.add(entry); + + // Mocking the certificate behavior + final X509Certificate mockCert = generateX509Certificate(entries); + + final List result = DefaultHostnameVerifier.getSubjectAltNames(mockCert, -1); + Assertions.assertEquals(1, result.size(), "Should have one SubjectAltName"); + + final SubjectName sn = result.get(0); + Assertions.assertEquals(SubjectName.IP, sn.getType(), "Should be an IP type"); + // Here, you'll need logic to convert byte array to string for assertion + Assertions.assertEquals("1.1.1.1", sn.getValue(), "IP address should match after conversion"); + } + + @Test + void testSimulatedBytePropertiesIPv6() throws Exception { + final byte[] ipv6AsByteArray = InetAddress.getByName("2001:db8:85a3::8a2e:370:7334").getAddress(); + // IPv6 2001:db8:85a3::8a2e:370:7334 + + final List> entries = new ArrayList<>(); + final List entry = new ArrayList<>(); + entry.add(SubjectName.IP); + entry.add(ipv6AsByteArray); + entries.add(entry); + + // Mocking the certificate behavior + final X509Certificate mockCert = generateX509Certificate(entries); + + final List result = DefaultHostnameVerifier.getSubjectAltNames(mockCert, -1); + Assertions.assertEquals(1, result.size(), "Should have one SubjectAltName"); + + final SubjectName sn = result.get(0); + Assertions.assertEquals(SubjectName.IP, sn.getType(), "Should be an IP type"); + // Here, you'll need logic to convert byte array to string for assertion + Assertions.assertEquals("2001:0db8:85a3:0000:0000:8a2e:0370:7334", sn.getValue(), "IP address should match after conversion"); + } + + + private X509Certificate generateX509Certificate(final List> entries) { + return new X509Certificate() { + + @Override + public boolean hasUnsupportedCriticalExtension() { + return false; + } + + @Override + public Set getCriticalExtensionOIDs() { + return null; + } + + @Override + public Set getNonCriticalExtensionOIDs() { + return null; + } + + @Override + public byte[] getExtensionValue(final String oid) { + return new byte[0]; + } + + @Override + public byte[] getEncoded() throws CertificateEncodingException { + return new byte[0]; + } + + @Override + public void verify(final PublicKey key) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException { + + } + + @Override + public void verify(final PublicKey key, final String sigProvider) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException { + + } + + @Override + public String toString() { + return ""; + } + + @Override + public PublicKey getPublicKey() { + return null; + } + + @Override + public void checkValidity() throws CertificateExpiredException, CertificateNotYetValidException { + + } + + @Override + public void checkValidity(final Date date) throws CertificateExpiredException, CertificateNotYetValidException { + + } + + @Override + public int getVersion() { + return 0; + } + + @Override + public BigInteger getSerialNumber() { + return null; + } + + @Override + public Principal getIssuerDN() { + return null; + } + + @Override + public Principal getSubjectDN() { + return null; + } + + @Override + public Date getNotBefore() { + return null; + } + + @Override + public Date getNotAfter() { + return null; + } + + @Override + public byte[] getTBSCertificate() throws CertificateEncodingException { + return new byte[0]; + } + + @Override + public byte[] getSignature() { + return new byte[0]; + } + + @Override + public String getSigAlgName() { + return ""; + } + + @Override + public String getSigAlgOID() { + return ""; + } + + @Override + public byte[] getSigAlgParams() { + return new byte[0]; + } + + @Override + public boolean[] getIssuerUniqueID() { + return new boolean[0]; + } + + @Override + public boolean[] getSubjectUniqueID() { + return new boolean[0]; + } + + @Override + public boolean[] getKeyUsage() { + return new boolean[0]; + } + + @Override + public int getBasicConstraints() { + return 0; + } + + @Override + public Collection> getSubjectAlternativeNames() { + return entries; + } + }; + + } + } \ No newline at end of file