All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sviolet.thistle.util.net.SimpleHostnameVerifier Maven / Gradle / Ivy

/*
 * Copyright (C) 2015-2020 S.Violet
 *
 * 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.
 *
 * Project GitHub: https://github.com/shepherdviolet/thistle
 * Email: [email protected]
 */

package sviolet.thistle.util.net;

import sviolet.thistle.util.judge.CheckUtils;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.List;

/**
 * 简易的HostnameVerifier
 *
 * 1.实现hostname与CN的匹配
 * 2.实现hostname与subjectAlternativeNames的匹配
 * 3.支持通配符域名(*)
 *
 * @author S.Violet
 */
public class SimpleHostnameVerifier implements HostnameVerifier {

    private static final Integer DNS_NAME = 2;

    /**
     * @param hostname 实际访问的域名
     * @param session SSL会话, 可以从中获取证书
     */
    @Override
    public boolean verify(String hostname, SSLSession session) {
        try {
            if (CheckUtils.isEmptyOrBlank(hostname)) {
                return false;
            }

            Certificate[] certificates = session.getPeerCertificates();
            if (certificates == null || certificates.length <= 0) {
                return false;
            }

            //第一个证书是站点证书
            X509Certificate x509Certificate = (X509Certificate) certificates[0];
            String dn = x509Certificate.getSubjectX500Principal().getName();
            String cn = getCn(dn);

            //验证CN与域名是否相符
            if (isHostnameMatch(hostname, cn)) {
                return true;
            }

            //获取subjectAlternativeNames
            Collection> subjectAlternativeNames = x509Certificate.getSubjectAlternativeNames();
            if (subjectAlternativeNames == null) {
                return false;
            }

            //遍历subjectAlternativeNames
            for (List subjectAlternativeName : subjectAlternativeNames) {
                //正常的格式类似于[2, *.test.com], 2表示该项是DNS Name, 后面是域名
                if (subjectAlternativeName == null ||
                        subjectAlternativeName.size() != 2 ||
                        !DNS_NAME.equals(subjectAlternativeName.get(0))) {
                    continue;
                }
                if (isHostnameMatch(hostname, String.valueOf(subjectAlternativeName.get(1)))) {
                    return true;
                }
            }

        } catch (Throwable ignored) {
        }

        return false;
    }

    protected String getCn(String dn) {
        if (CheckUtils.isEmptyOrBlank(dn)) {
            return null;
        }

        int cnStart = dn.indexOf("CN=");
        if (cnStart < 0) {
            cnStart = dn.indexOf("cn=");
        }
        if (cnStart < 0) {
            return null;
        }

        int cnEnd = dn.indexOf(',', cnStart);
        if (cnEnd < 0) {
            cnEnd = dn.length();
        }

        return dn.substring(cnStart + 3, cnEnd).trim();
    }

    /**
     * @param hostname 实际访问的域名
     * @param cn 证书的CN或者subjectAlternativeNames
     * @return true: 实际访问的域名与证书声明的域名相符, false: 不符, 会继续匹配其他的subjectAlternativeNames
     */
    protected boolean isHostnameMatch(String hostname, String cn) {
        if (CheckUtils.isEmptyOrBlank(cn)) {
            return false;
        }
        if (cn.charAt(0) == '*') {
            cn = cn.substring(1);
            return hostname.endsWith(cn);
        } else {
            return hostname.equals(cn);
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy