mirror of
https://github.com/square/okhttp.git
synced 2026-01-14 07:22:20 +03:00
Convert OkHostnameVerifier and BasicTrustRootIndex to Kotlin (#4940)
* Rename OkHostnameVerifier.java to .kt * Convert OkHostnameVerifier and BasicTrustRootIndex to Kotlin * Update okhttp/src/main/java/okhttp3/internal/tls/OkHostnameVerifier.kt Co-Authored-By: swankjesse <jesse@swank.ca> * Update okhttp/src/main/java/okhttp3/internal/tls/OkHostnameVerifier.kt Co-Authored-By: swankjesse <jesse@swank.ca> * Update okhttp/src/main/java/okhttp3/internal/tls/OkHostnameVerifier.kt Co-Authored-By: swankjesse <jesse@swank.ca>
This commit is contained in:
@@ -290,7 +290,7 @@ open class OkHttpClient internal constructor(
|
||||
internal var socketFactory: SocketFactory = SocketFactory.getDefault()
|
||||
internal var sslSocketFactory: SSLSocketFactory? = null
|
||||
internal var certificateChainCleaner: CertificateChainCleaner? = null
|
||||
internal var hostnameVerifier: HostnameVerifier = OkHostnameVerifier.INSTANCE
|
||||
internal var hostnameVerifier: HostnameVerifier = OkHostnameVerifier
|
||||
internal var certificatePinner: CertificatePinner = CertificatePinner.DEFAULT
|
||||
internal var proxyAuthenticator: Authenticator = Authenticator.NONE
|
||||
internal var authenticator: Authenticator = Authenticator.NONE
|
||||
|
||||
@@ -347,7 +347,8 @@ public final class RealConnection extends Http2Connection.Listener implements Co
|
||||
"Hostname " + address.url().host() + " not verified:"
|
||||
+ "\n certificate: " + CertificatePinner.pin(cert)
|
||||
+ "\n DN: " + cert.getSubjectDN().getName()
|
||||
+ "\n subjectAltNames: " + OkHostnameVerifier.allSubjectAltNames(cert));
|
||||
+ "\n subjectAltNames: "
|
||||
+ OkHostnameVerifier.INSTANCE.allSubjectAltNames(cert));
|
||||
} else {
|
||||
throw new SSLPeerUnverifiedException(
|
||||
"Hostname " + address.url().host() + " not verified (no certificates)");
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Square, Inc.
|
||||
*
|
||||
* Licensed 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 okhttp3.internal.tls;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
|
||||
/** A simple index that of trusted root certificates that have been loaded into memory. */
|
||||
public final class BasicTrustRootIndex implements TrustRootIndex {
|
||||
private final Map<X500Principal, Set<X509Certificate>> subjectToCaCerts;
|
||||
|
||||
public BasicTrustRootIndex(X509Certificate... caCerts) {
|
||||
subjectToCaCerts = new LinkedHashMap<>();
|
||||
for (X509Certificate caCert : caCerts) {
|
||||
X500Principal subject = caCert.getSubjectX500Principal();
|
||||
Set<X509Certificate> subjectCaCerts = subjectToCaCerts.get(subject);
|
||||
if (subjectCaCerts == null) {
|
||||
subjectCaCerts = new LinkedHashSet<>(1);
|
||||
subjectToCaCerts.put(subject, subjectCaCerts);
|
||||
}
|
||||
subjectCaCerts.add(caCert);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public X509Certificate findByIssuerAndSignature(X509Certificate cert) {
|
||||
X500Principal issuer = cert.getIssuerX500Principal();
|
||||
Set<X509Certificate> subjectCaCerts = subjectToCaCerts.get(issuer);
|
||||
if (subjectCaCerts == null) return null;
|
||||
|
||||
for (X509Certificate caCert : subjectCaCerts) {
|
||||
PublicKey publicKey = caCert.getPublicKey();
|
||||
try {
|
||||
cert.verify(publicKey);
|
||||
return caCert;
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public boolean equals(Object other) {
|
||||
if (other == this) return true;
|
||||
return other instanceof okhttp3.internal.tls.BasicTrustRootIndex
|
||||
&& ((okhttp3.internal.tls.BasicTrustRootIndex) other).subjectToCaCerts.equals(
|
||||
subjectToCaCerts);
|
||||
}
|
||||
|
||||
@Override public int hashCode() {
|
||||
return subjectToCaCerts.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Square, Inc.
|
||||
*
|
||||
* Licensed 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 okhttp3.internal.tls
|
||||
|
||||
import java.security.cert.X509Certificate
|
||||
import javax.security.auth.x500.X500Principal
|
||||
|
||||
/** A simple index that of trusted root certificates that have been loaded into memory. */
|
||||
class BasicTrustRootIndex(vararg caCerts: X509Certificate) : TrustRootIndex {
|
||||
private val subjectToCaCerts: Map<X500Principal, Set<X509Certificate>>
|
||||
|
||||
init {
|
||||
val map = mutableMapOf<X500Principal, MutableSet<X509Certificate>>()
|
||||
for (caCert in caCerts) {
|
||||
map.getOrPut(caCert.subjectX500Principal) { mutableSetOf() }.add(caCert)
|
||||
}
|
||||
this.subjectToCaCerts = map
|
||||
}
|
||||
|
||||
override fun findByIssuerAndSignature(cert: X509Certificate): X509Certificate? {
|
||||
val issuer = cert.issuerX500Principal
|
||||
val subjectCaCerts = subjectToCaCerts[issuer] ?: return null
|
||||
|
||||
return subjectCaCerts.firstOrNull {
|
||||
try {
|
||||
cert.verify(it.publicKey)
|
||||
return@firstOrNull true
|
||||
} catch (_: Exception) {
|
||||
return@firstOrNull false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other === this ||
|
||||
(other is BasicTrustRootIndex && other.subjectToCaCerts == subjectToCaCerts)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return subjectToCaCerts.hashCode()
|
||||
}
|
||||
}
|
||||
@@ -1,216 +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 okhttp3.internal.tls;
|
||||
|
||||
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.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLSession;
|
||||
|
||||
import static okhttp3.internal.Util.verifyAsIpAddress;
|
||||
|
||||
/**
|
||||
* A HostnameVerifier consistent with <a href="http://www.ietf.org/rfc/rfc2818.txt">RFC 2818</a>.
|
||||
*/
|
||||
public final class OkHostnameVerifier implements HostnameVerifier {
|
||||
public static final OkHostnameVerifier INSTANCE = new OkHostnameVerifier();
|
||||
|
||||
private static final int ALT_DNS_NAME = 2;
|
||||
private static final int ALT_IPA_NAME = 7;
|
||||
|
||||
private OkHostnameVerifier() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(String host, SSLSession session) {
|
||||
try {
|
||||
Certificate[] certificates = session.getPeerCertificates();
|
||||
return verify(host, (X509Certificate) certificates[0]);
|
||||
} catch (SSLException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean verify(String host, X509Certificate certificate) {
|
||||
return verifyAsIpAddress(host)
|
||||
? verifyIpAddress(host, certificate)
|
||||
: verifyHostname(host, certificate);
|
||||
}
|
||||
|
||||
/** Returns true if {@code certificate} matches {@code ipAddress}. */
|
||||
private boolean verifyIpAddress(String ipAddress, X509Certificate certificate) {
|
||||
List<String> altNames = getSubjectAltNames(certificate, ALT_IPA_NAME);
|
||||
for (int i = 0, size = altNames.size(); i < size; i++) {
|
||||
if (ipAddress.equalsIgnoreCase(altNames.get(i))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns true if {@code certificate} matches {@code hostname}. */
|
||||
private boolean verifyHostname(String hostname, X509Certificate certificate) {
|
||||
hostname = hostname.toLowerCase(Locale.US);
|
||||
List<String> altNames = getSubjectAltNames(certificate, ALT_DNS_NAME);
|
||||
for (String altName : altNames) {
|
||||
if (verifyHostname(hostname, altName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<String> allSubjectAltNames(X509Certificate certificate) {
|
||||
List<String> altIpaNames = getSubjectAltNames(certificate, ALT_IPA_NAME);
|
||||
List<String> altDnsNames = getSubjectAltNames(certificate, ALT_DNS_NAME);
|
||||
List<String> result = new ArrayList<>(altIpaNames.size() + altDnsNames.size());
|
||||
result.addAll(altIpaNames);
|
||||
result.addAll(altDnsNames);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<String> getSubjectAltNames(X509Certificate certificate, int type) {
|
||||
List<String> result = new ArrayList<>();
|
||||
try {
|
||||
Collection<?> subjectAltNames = certificate.getSubjectAlternativeNames();
|
||||
if (subjectAltNames == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
for (Object subjectAltName : subjectAltNames) {
|
||||
List<?> entry = (List<?>) subjectAltName;
|
||||
if (entry == null || entry.size() < 2) {
|
||||
continue;
|
||||
}
|
||||
Integer altNameType = (Integer) entry.get(0);
|
||||
if (altNameType == null) {
|
||||
continue;
|
||||
}
|
||||
if (altNameType == type) {
|
||||
String altName = (String) entry.get(1);
|
||||
if (altName != null) {
|
||||
result.add(altName);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} catch (CertificateParsingException e) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} iff {@code hostname} matches the domain name {@code pattern}.
|
||||
*
|
||||
* @param hostname lower-case host name.
|
||||
* @param pattern domain name pattern from certificate. May be a wildcard pattern such as {@code
|
||||
* *.android.com}.
|
||||
*/
|
||||
public boolean verifyHostname(String hostname, String pattern) {
|
||||
// Basic sanity checks
|
||||
// Check length == 0 instead of .isEmpty() to support Java 5.
|
||||
if ((hostname == null) || (hostname.length() == 0) || (hostname.startsWith("."))
|
||||
|| (hostname.endsWith(".."))) {
|
||||
// Invalid domain name
|
||||
return false;
|
||||
}
|
||||
if ((pattern == null) || (pattern.length() == 0) || (pattern.startsWith("."))
|
||||
|| (pattern.endsWith(".."))) {
|
||||
// Invalid pattern/domain name
|
||||
return false;
|
||||
}
|
||||
|
||||
// Normalize hostname and pattern by turning them into absolute domain names if they are not
|
||||
// yet absolute. This is needed because server certificates do not normally contain absolute
|
||||
// names or patterns, but they should be treated as absolute. At the same time, any hostname
|
||||
// presented to this method should also be treated as absolute for the purposes of matching
|
||||
// to the server certificate.
|
||||
// www.android.com matches www.android.com
|
||||
// www.android.com matches www.android.com.
|
||||
// www.android.com. matches www.android.com.
|
||||
// www.android.com. matches www.android.com
|
||||
if (!hostname.endsWith(".")) {
|
||||
hostname += '.';
|
||||
}
|
||||
if (!pattern.endsWith(".")) {
|
||||
pattern += '.';
|
||||
}
|
||||
// hostname and pattern are now absolute domain names.
|
||||
|
||||
pattern = pattern.toLowerCase(Locale.US);
|
||||
// hostname and pattern are now in lower case -- domain names are case-insensitive.
|
||||
|
||||
if (!pattern.contains("*")) {
|
||||
// Not a wildcard pattern -- hostname and pattern must match exactly.
|
||||
return hostname.equals(pattern);
|
||||
}
|
||||
// Wildcard pattern
|
||||
|
||||
// WILDCARD PATTERN RULES:
|
||||
// 1. Asterisk (*) is only permitted in the left-most domain name label and must be the
|
||||
// only character in that label (i.e., must match the whole left-most label).
|
||||
// For example, *.example.com is permitted, while *a.example.com, a*.example.com,
|
||||
// a*b.example.com, a.*.example.com are not permitted.
|
||||
// 2. Asterisk (*) cannot match across domain name labels.
|
||||
// For example, *.example.com matches test.example.com but does not match
|
||||
// sub.test.example.com.
|
||||
// 3. Wildcard patterns for single-label domain names are not permitted.
|
||||
|
||||
if ((!pattern.startsWith("*.")) || (pattern.indexOf('*', 1) != -1)) {
|
||||
// Asterisk (*) is only permitted in the left-most domain name label and must be the only
|
||||
// character in that label
|
||||
return false;
|
||||
}
|
||||
|
||||
// Optimization: check whether hostname is too short to match the pattern. hostName must be at
|
||||
// least as long as the pattern because asterisk must match the whole left-most label and
|
||||
// hostname starts with a non-empty label. Thus, asterisk has to match one or more characters.
|
||||
if (hostname.length() < pattern.length()) {
|
||||
// hostname too short to match the pattern.
|
||||
return false;
|
||||
}
|
||||
|
||||
if ("*.".equals(pattern)) {
|
||||
// Wildcard pattern for single-label domain name -- not permitted.
|
||||
return false;
|
||||
}
|
||||
|
||||
// hostname must end with the region of pattern following the asterisk.
|
||||
String suffix = pattern.substring(1);
|
||||
if (!hostname.endsWith(suffix)) {
|
||||
// hostname does not end with the suffix
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that asterisk did not match across domain name labels.
|
||||
int suffixStartIndexInHostname = hostname.length() - suffix.length();
|
||||
if ((suffixStartIndexInHostname > 0)
|
||||
&& (hostname.lastIndexOf('.', suffixStartIndexInHostname - 1) != -1)) {
|
||||
// Asterisk is matching across domain name labels -- not permitted.
|
||||
return false;
|
||||
}
|
||||
|
||||
// hostname matches pattern
|
||||
return true;
|
||||
}
|
||||
}
|
||||
182
okhttp/src/main/java/okhttp3/internal/tls/OkHostnameVerifier.kt
Normal file
182
okhttp/src/main/java/okhttp3/internal/tls/OkHostnameVerifier.kt
Normal file
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* 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 okhttp3.internal.tls
|
||||
|
||||
import okhttp3.internal.Util.verifyAsIpAddress
|
||||
import java.security.cert.CertificateParsingException
|
||||
import java.security.cert.X509Certificate
|
||||
import java.util.Locale
|
||||
import javax.net.ssl.HostnameVerifier
|
||||
import javax.net.ssl.SSLException
|
||||
import javax.net.ssl.SSLSession
|
||||
|
||||
/**
|
||||
* A HostnameVerifier consistent with [RFC 2818][rfc_2818].
|
||||
*
|
||||
* [rfc_2818]: http://www.ietf.org/rfc/rfc2818.txt
|
||||
*/
|
||||
object OkHostnameVerifier : HostnameVerifier {
|
||||
private const val ALT_DNS_NAME = 2
|
||||
private const val ALT_IPA_NAME = 7
|
||||
|
||||
override fun verify(host: String, session: SSLSession): Boolean {
|
||||
return try {
|
||||
verify(host, session.peerCertificates[0] as X509Certificate)
|
||||
} catch (_: SSLException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun verify(host: String, certificate: X509Certificate): Boolean {
|
||||
return when {
|
||||
verifyAsIpAddress(host) -> verifyIpAddress(host, certificate)
|
||||
else -> verifyHostname(host, certificate)
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true if `certificate` matches `ipAddress`. */
|
||||
private fun verifyIpAddress(ipAddress: String, certificate: X509Certificate): Boolean {
|
||||
return getSubjectAltNames(certificate, ALT_IPA_NAME).any {
|
||||
ipAddress.equals(it, ignoreCase = true)
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true if `certificate` matches `hostname`. */
|
||||
private fun verifyHostname(hostname: String, certificate: X509Certificate): Boolean {
|
||||
val hostname = hostname.toLowerCase(Locale.US)
|
||||
return getSubjectAltNames(certificate, ALT_DNS_NAME).any {
|
||||
verifyHostname(hostname, it)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if [hostname] matches the domain name [pattern].
|
||||
*
|
||||
* @param hostname lower-case host name.
|
||||
* @param pattern domain name pattern from certificate. May be a wildcard pattern such as
|
||||
* `*.android.com`.
|
||||
*/
|
||||
fun verifyHostname(hostname: String?, pattern: String?): Boolean {
|
||||
var hostname = hostname
|
||||
var pattern = pattern
|
||||
// Basic sanity checks
|
||||
if (hostname.isNullOrEmpty() ||
|
||||
hostname.startsWith(".") ||
|
||||
hostname.endsWith("..")) {
|
||||
// Invalid domain name
|
||||
return false
|
||||
}
|
||||
if (pattern.isNullOrEmpty() ||
|
||||
pattern.startsWith(".") ||
|
||||
pattern.endsWith("..")) {
|
||||
// Invalid pattern/domain name
|
||||
return false
|
||||
}
|
||||
|
||||
// Normalize hostname and pattern by turning them into absolute domain names if they are not
|
||||
// yet absolute. This is needed because server certificates do not normally contain absolute
|
||||
// names or patterns, but they should be treated as absolute. At the same time, any hostname
|
||||
// presented to this method should also be treated as absolute for the purposes of matching
|
||||
// to the server certificate.
|
||||
// www.android.com matches www.android.com
|
||||
// www.android.com matches www.android.com.
|
||||
// www.android.com. matches www.android.com.
|
||||
// www.android.com. matches www.android.com
|
||||
if (!hostname.endsWith(".")) {
|
||||
hostname += "."
|
||||
}
|
||||
if (!pattern.endsWith(".")) {
|
||||
pattern += "."
|
||||
}
|
||||
// Hostname and pattern are now absolute domain names.
|
||||
|
||||
pattern = pattern.toLowerCase(Locale.US)
|
||||
// Hostname and pattern are now in lower case -- domain names are case-insensitive.
|
||||
|
||||
if ("*" !in pattern) {
|
||||
// Not a wildcard pattern -- hostname and pattern must match exactly.
|
||||
return hostname == pattern
|
||||
}
|
||||
|
||||
// Wildcard pattern
|
||||
|
||||
// WILDCARD PATTERN RULES:
|
||||
// 1. Asterisk (*) is only permitted in the left-most domain name label and must be the
|
||||
// only character in that label (i.e., must match the whole left-most label).
|
||||
// For example, *.example.com is permitted, while *a.example.com, a*.example.com,
|
||||
// a*b.example.com, a.*.example.com are not permitted.
|
||||
// 2. Asterisk (*) cannot match across domain name labels.
|
||||
// For example, *.example.com matches test.example.com but does not match
|
||||
// sub.test.example.com.
|
||||
// 3. Wildcard patterns for single-label domain names are not permitted.
|
||||
|
||||
if (!pattern.startsWith("*.") || pattern.indexOf('*', 1) != -1) {
|
||||
// Asterisk (*) is only permitted in the left-most domain name label and must be the only
|
||||
// character in that label
|
||||
return false
|
||||
}
|
||||
|
||||
// Optimization: check whether hostname is too short to match the pattern. hostName must be at
|
||||
// least as long as the pattern because asterisk must match the whole left-most label and
|
||||
// hostname starts with a non-empty label. Thus, asterisk has to match one or more characters.
|
||||
if (hostname.length < pattern.length) {
|
||||
return false // Hostname too short to match the pattern.
|
||||
}
|
||||
|
||||
if ("*." == pattern) {
|
||||
return false // Wildcard pattern for single-label domain name -- not permitted.
|
||||
}
|
||||
|
||||
// Hostname must end with the region of pattern following the asterisk.
|
||||
val suffix = pattern.substring(1)
|
||||
if (!hostname.endsWith(suffix)) {
|
||||
return false // Hostname does not end with the suffix.
|
||||
}
|
||||
|
||||
// Check that asterisk did not match across domain name labels.
|
||||
val suffixStartIndexInHostname = hostname.length - suffix.length
|
||||
if (suffixStartIndexInHostname > 0 &&
|
||||
hostname.lastIndexOf('.', suffixStartIndexInHostname - 1) != -1) {
|
||||
return false // Asterisk is matching across domain name labels -- not permitted.
|
||||
}
|
||||
|
||||
// Hostname matches pattern.
|
||||
return true
|
||||
}
|
||||
|
||||
fun allSubjectAltNames(certificate: X509Certificate): List<String> {
|
||||
val altIpaNames = getSubjectAltNames(certificate, OkHostnameVerifier.ALT_IPA_NAME)
|
||||
val altDnsNames = getSubjectAltNames(certificate, OkHostnameVerifier.ALT_DNS_NAME)
|
||||
return altIpaNames + altDnsNames
|
||||
}
|
||||
|
||||
private fun getSubjectAltNames(certificate: X509Certificate, type: Int): List<String> {
|
||||
try {
|
||||
val subjectAltNames = certificate.subjectAlternativeNames ?: return emptyList()
|
||||
val result = mutableListOf<String>()
|
||||
for (subjectAltName in subjectAltNames) {
|
||||
if (subjectAltName == null || subjectAltName.size < 2) continue
|
||||
if (subjectAltName[0] != type) continue
|
||||
val altName = subjectAltName[1] ?: continue
|
||||
result.add(altName as String)
|
||||
}
|
||||
return result
|
||||
} catch (_: CertificateParsingException) {
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -866,7 +866,7 @@ class KotlinSourceCompatibilityTest {
|
||||
builder = builder.dns(Dns.SYSTEM)
|
||||
builder = builder.socketFactory(SocketFactory.getDefault())
|
||||
builder = builder.sslSocketFactory(localhost().sslSocketFactory(), localhost().trustManager())
|
||||
builder = builder.hostnameVerifier(OkHostnameVerifier.INSTANCE)
|
||||
builder = builder.hostnameVerifier(OkHostnameVerifier)
|
||||
builder = builder.certificatePinner(CertificatePinner.DEFAULT)
|
||||
builder = builder.authenticator(Authenticator.NONE)
|
||||
builder = builder.proxyAuthenticator(Authenticator.NONE)
|
||||
@@ -1161,7 +1161,7 @@ class KotlinSourceCompatibilityTest {
|
||||
Dns.SYSTEM,
|
||||
SocketFactory.getDefault(),
|
||||
localhost().sslSocketFactory(),
|
||||
OkHostnameVerifier.INSTANCE,
|
||||
OkHostnameVerifier,
|
||||
CertificatePinner.DEFAULT,
|
||||
Authenticator.NONE,
|
||||
Proxy.NO_PROXY,
|
||||
|
||||
Reference in New Issue
Block a user