19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/* 28f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * Copyright (C) 2010 The Android Open Source Project 39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License. 69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at 79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and 149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License. 159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 162269d1572e5fcfb725ea55f5764d8c3280d69f6dDianne Hackbornpackage com.android.internal.net; 172269d1572e5fcfb725ea55f5764d8c3280d69f6dDianne Hackborn 189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 198f028a94fc533e75077485a7d11a04e4de820335Makoto Onukiimport android.util.Config; 208f028a94fc533e75077485a7d11a04e4de820335Makoto Onukiimport android.util.Log; 219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.net.InetAddress; 239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.net.UnknownHostException; 249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.security.cert.CertificateParsingException; 258f028a94fc533e75077485a7d11a04e4de820335Makoto Onukiimport java.security.cert.X509Certificate; 269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Collection; 279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Iterator; 289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.List; 299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.regex.Pattern; 309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.regex.PatternSyntaxException; 319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 328f028a94fc533e75077485a7d11a04e4de820335Makoto Onukiimport javax.security.auth.x500.X500Principal; 338f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 342269d1572e5fcfb725ea55f5764d8c3280d69f6dDianne Hackborn/** @hide */ 358f028a94fc533e75077485a7d11a04e4de820335Makoto Onukipublic class DomainNameValidator { 368f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki private final static String TAG = "DomainNameValidator"; 378f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 388f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki private static final boolean DEBUG = false; 398f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV; 408f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki 419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static Pattern QUICK_IP_PATTERN; 429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project static { 439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project QUICK_IP_PATTERN = Pattern.compile("^[a-f0-9\\.:]+$"); 459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (PatternSyntaxException e) {} 469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final int ALT_DNS_NAME = 2; 499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final int ALT_IPA_NAME = 7; 509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Checks the site certificate against the domain name of the site being visited 539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param certificate The certificate to check 549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param thisDomain The domain name of the site being visited 559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @return True iff if there is a domain match as specified by RFC2818 569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static boolean match(X509Certificate certificate, String thisDomain) { 589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (certificate == null || thisDomain == null || thisDomain.length() == 0) { 599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project thisDomain = thisDomain.toLowerCase(); 639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (!isIpAddress(thisDomain)) { 649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return matchDns(certificate, thisDomain); 659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return matchIpAddress(certificate, thisDomain); 679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @return True iff the domain name is specified as an IP address 729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static boolean isIpAddress(String domain) { 749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean rval = (domain != null && domain.length() != 0); 759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (rval) { 769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // do a quick-dirty IP match first to avoid DNS lookup 789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project rval = QUICK_IP_PATTERN.matcher(domain).matches(); 799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (rval) { 809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project rval = domain.equals( 819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project InetAddress.getByName(domain).getHostAddress()); 829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (UnknownHostException e) { 849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String errorMessage = e.getMessage(); 859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (errorMessage == null) { 869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project errorMessage = "unknown host exception"; 879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 898f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (LOG_ENABLED) { 908f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki Log.v(TAG, "DomainNameValidator.isIpAddress(): " + errorMessage); 919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project rval = false; 949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return rval; 989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Checks the site certificate against the IP domain name of the site being visited 1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param certificate The certificate to check 1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param thisDomain The DNS domain name of the site being visited 1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @return True iff if there is a domain match as specified by RFC2818 1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static boolean matchIpAddress(X509Certificate certificate, String thisDomain) { 1078f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (LOG_ENABLED) { 1088f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki Log.v(TAG, "DomainNameValidator.matchIpAddress(): this domain: " + thisDomain); 1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Collection subjectAltNames = certificate.getSubjectAlternativeNames(); 1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (subjectAltNames != null) { 1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Iterator i = subjectAltNames.iterator(); 1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while (i.hasNext()) { 1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project List altNameEntry = (List)(i.next()); 1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (altNameEntry != null && 2 <= altNameEntry.size()) { 1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Integer altNameType = (Integer)(altNameEntry.get(0)); 1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (altNameType != null) { 1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (altNameType.intValue() == ALT_IPA_NAME) { 1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String altName = (String)(altNameEntry.get(1)); 1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (altName != null) { 1238f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (LOG_ENABLED) { 1248f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki Log.v(TAG, "alternative IP: " + altName); 1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (thisDomain.equalsIgnoreCase(altName)) { 1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (CertificateParsingException e) {} 1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Checks the site certificate against the DNS domain name of the site being visited 1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param certificate The certificate to check 1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param thisDomain The DNS domain name of the site being visited 1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @return True iff if there is a domain match as specified by RFC2818 1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static boolean matchDns(X509Certificate certificate, String thisDomain) { 1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean hasDns = false; 1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Collection subjectAltNames = certificate.getSubjectAlternativeNames(); 1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (subjectAltNames != null) { 1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Iterator i = subjectAltNames.iterator(); 1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while (i.hasNext()) { 1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project List altNameEntry = (List)(i.next()); 1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (altNameEntry != null && 2 <= altNameEntry.size()) { 1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Integer altNameType = (Integer)(altNameEntry.get(0)); 1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (altNameType != null) { 1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (altNameType.intValue() == ALT_DNS_NAME) { 1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project hasDns = true; 1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String altName = (String)(altNameEntry.get(1)); 1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (altName != null) { 1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (matchDns(thisDomain, altName)) { 1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (CertificateParsingException e) { 1712683c6f72cb4256f7850bc17d5b2ed4e619c0c6dMakoto Onuki String errorMessage = e.getMessage(); 1722683c6f72cb4256f7850bc17d5b2ed4e619c0c6dMakoto Onuki if (errorMessage == null) { 1732683c6f72cb4256f7850bc17d5b2ed4e619c0c6dMakoto Onuki errorMessage = "failed to parse certificate"; 1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1752683c6f72cb4256f7850bc17d5b2ed4e619c0c6dMakoto Onuki 1762683c6f72cb4256f7850bc17d5b2ed4e619c0c6dMakoto Onuki Log.w(TAG, "DomainNameValidator.matchDns(): " + errorMessage); 1772683c6f72cb4256f7850bc17d5b2ed4e619c0c6dMakoto Onuki return false; 1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (!hasDns) { 1818f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki final String cn = new DNParser(certificate.getSubjectX500Principal()) 1828f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki .find("cn"); 1838f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (LOG_ENABLED) { 1848f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki Log.v(TAG, "Validating subject: DN:" 1858f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki + certificate.getSubjectX500Principal().getName(X500Principal.CANONICAL) 1868f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki + " CN:" + cn); 1878f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki } 1888f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (cn != null) { 1898f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki return matchDns(thisDomain, cn); 1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param thisDomain The domain name of the site being visited 1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param thatDomain The domain name from the certificate 1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @return True iff thisDomain matches thatDomain as specified by RFC2818 2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2018f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki // not private for testing 2028f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki public static boolean matchDns(String thisDomain, String thatDomain) { 2038f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki if (LOG_ENABLED) { 2048f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki Log.v(TAG, "DomainNameValidator.matchDns():" + 2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project " this domain: " + thisDomain + 2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project " that domain: " + thatDomain); 2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (thisDomain == null || thisDomain.length() == 0 || 2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project thatDomain == null || thatDomain.length() == 0) { 2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project thatDomain = thatDomain.toLowerCase(); 2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // (a) domain name strings are equal, ignoring case: X matches X 2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean rval = thisDomain.equals(thatDomain); 2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (!rval) { 2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String[] thisDomainTokens = thisDomain.split("\\."); 2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String[] thatDomainTokens = thatDomain.split("\\."); 2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int thisDomainTokensNum = thisDomainTokens.length; 2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int thatDomainTokensNum = thatDomainTokens.length; 2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // (b) OR thatHost is a '.'-suffix of thisHost: Z.Y.X matches X 2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (thisDomainTokensNum >= thatDomainTokensNum) { 2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = thatDomainTokensNum - 1; i >= 0; --i) { 2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project rval = thisDomainTokens[i].equals(thatDomainTokens[i]); 2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (!rval) { 2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // (c) OR we have a special *-match: 231106f9b244633171ab75dd98b9761b7137c1a4705Grace Kloba // *.Y.X matches Z.Y.X but *.X doesn't match Z.Y.X 2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project rval = (i == 0 && thisDomainTokensNum == thatDomainTokensNum); 2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (rval) { 2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project rval = thatDomainTokens[0].equals("*"); 2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (!rval) { 2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // (d) OR we have a *-component match: 2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // f*.com matches foo.com but not bar.com 2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project rval = domainTokenMatch( 2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project thisDomainTokens[0], thatDomainTokens[0]); 2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 245106f9b244633171ab75dd98b9761b7137c1a4705Grace Kloba } else { 246106f9b244633171ab75dd98b9761b7137c1a4705Grace Kloba // (e) OR thatHost has a '*.'-prefix of thisHost: 247106f9b244633171ab75dd98b9761b7137c1a4705Grace Kloba // *.Y.X matches Y.X 248106f9b244633171ab75dd98b9761b7137c1a4705Grace Kloba rval = thatDomain.equals("*." + thisDomain); 2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return rval; 2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param thisDomainToken The domain token from the current domain name 2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param thatDomainToken The domain token from the certificate 2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @return True iff thisDomainToken matches thatDomainToken, using the 2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * wildcard match as specified by RFC2818-3.1. For example, f*.com must 2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * match foo.com but not bar.com 2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static boolean domainTokenMatch(String thisDomainToken, String thatDomainToken) { 2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (thisDomainToken != null && thatDomainToken != null) { 2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int starIndex = thatDomainToken.indexOf('*'); 2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (starIndex >= 0) { 2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (thatDomainToken.length() - 1 <= thisDomainToken.length()) { 2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String prefix = thatDomainToken.substring(0, starIndex); 2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String suffix = thatDomainToken.substring(starIndex + 1); 2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return thisDomainToken.startsWith(prefix) && thisDomainToken.endsWith(suffix); 2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 278