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