有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java主机名错误异常

我把主机名搞错了。我在程序中使用了this code(从某个链接获得)。我的程序运行良好。我的问题是它足够安全吗??(因为它不是在验证证书链)

public class Host {

    public String subscribe() throws Exception {   
        String resp = "";
        String urlString="https://xxx.xxx.xx.xx:8443/WebApplication3/NewServlet";
        URL url;
        URLConnection urlConn;
        DataOutputStream printout;
        DataInputStream input;
        String str = "";
        int flag=1;

        try {
            HostnameVerifier hv = new HostnameVerifier() {
                public boolean verify(String urlHostName, SSLSession session) {
                    System.out.println("Warning: URL Host: " + urlHostName + " vs. "
                      + session.getPeerHost());
                    return true;
                }
            };

            trustAllHttpsCertificates();
            HttpsURLConnection.setDefaultHostnameVerifier(hv);

            url = new URL(urlString);
            urlConn = url.openConnection();
            urlConn.setDoInput(true);
            Object object;
            urlConn.setUseCaches(false);

            urlConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            input = new DataInputStream(urlConn.getInputStream());

            while (null != ((str = input.readLine()))) {
                if (str.length() >0) {
                    str = str.trim();
                    if(!str.equals("")) {
                        //System.out.println(str);
                        resp += str;
                    }
                }
            }
            input.close();
        } catch ( MalformedURLException mue) { 
            mue.printStackTrace();
        } catch(IOException ioe) {
            ioe.printStackTrace();
        }
        return resp;
    }

    public static class miTM implements javax.net.ssl.TrustManager,
        javax.net.ssl.X509TrustManager {

        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        public boolean isServerTrusted(java.security.cert.X509Certificate[] certs) {
            return true;
        }

        public boolean isClientTrusted(java.security.cert.X509Certificate[] certs) {
            return true;
        }

        public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException {
            return;
        }

        public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException {
            return;
        }
    }

    private static void trustAllHttpsCertificates() throws Exception {

        //  Create a trust manager that does not validate certificate chains:
        javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[1];

        javax.net.ssl.TrustManager tm = new miTM();

        trustAllCerts[0] = tm;

        javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext.getInstance("SSL");

        sc.init(null, trustAllCerts, null);

        javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

    }

}

共 (4) 个答案

  1. # 1 楼答案

    下面是一种清理代码并保持安全的方法。我想代码连接到一个已知的服务(受信任的)。要使Java SSL堆栈即使在主机名不匹配的情况下也接受连接,最好的方法是将服务器证书添加到JVM信任存储中

    首先,您可以从浏览器中导出服务器证书并将其保存在磁盘上。在Linux中,您可以使用openssl s_client -connect xxx.xxx.xx.xx:8443并将ascii格式的服务器证书复制/粘贴到文本文件中

    然后使用keytool将服务器证书导入jre/lib/security/cacertsJKS文件

    keytool -import -alias myservice -file servercertificate.cer
    

    我更喜欢的另一种选择是,在更新Java时避免回归,即在自己的位置复制cacerts,并通过javax.net.ssl.trustStore系统属性声明它

    由于服务器证书位于信任存储中。。。它是受信任的,直到过期。这通常用于自签名服务器证书

  2. # 2 楼答案

    miTM中的代码实际上禁用了任何SSL安全检查,因此安全级别非常低(只有SSL证书损坏时才会出错,但当证书与域不匹配时不会出错)

    基本上,你试图在没有任何安全措施的情况下建立连接。如果这就是你想要的,解决方案可能是“足够安全”,但答案很可能是“不”

    这种问题的正确解决方案是为此域创建匹配的证书

    不幸的是,当HTTP服务器使用“虚拟主机”(许多域名映射到同一IP地址)时,这是不可能的。解决这个问题的正确方法是获取自己的IP地址

    如果您仍然想尝试纯Java的解决方案,请查看以下答案:https://stackoverflow.com/a/3293720/34088

  3. # 3 楼答案

    在java中,很多时候都会遇到这样的异常

    问题可能是ipconflict/ip域不匹配/证书无效

    我已经通过使用其适当的ip地址和安装证书解决了这个问题

  4. # 4 楼答案

    要确保连接安全,您必须(至少):

    • 验证您是否信任该证书
    • 验证主机名(除非您确定这是您唯一信任的证书)

    您的代码在这两点上失败:

    • 您使用的TrustManager根本不检查证书(它从不抛出异常,而API希望它在证书不受信任时抛出CertificateException形式)
    • 主机名验证程序总是返回true

    要修复代码:

    • 保留默认的信任管理器,或者使用您自己的信任存储和默认的TrustManagerFactory初始化它们
    • 保留默认的主机名验证程序

    问题的标题(“em>主机名错误异常”)和示例URL https://xxx.xxx.xx.xx:8443似乎表明您正在连接IP地址

    与某些浏览器不同,Java严格遵循规范(RFC 2818):

    If a subjectAltName extension of type dNSName is present, that MUST be used as the identity. Otherwise, the (most specific) Common Name field in the Subject field of the certificate MUST be used. Although the use of the Common Name is existing practice, it is deprecated and Certification Authorities are encouraged to use the dNSName instead.

    [...]

    In some cases, the URI is specified as an IP address rather than a hostname. In this case, the iPAddress subjectAltName must be present in the certificate and must exactly match the IP in the URI.

    这意味着您不能仅仅将IP地址放在服务器证书中的主题DN的通用名(CN)中。如果您使用的是IP地址,必须在主题替代名称条目中。(从Java 7开始,keytool有生成此类证书的选项。)

    您将找到有关在this answer中使用哪些命令的更多详细信息

    也就是说,使用IP地址最多只能在测试环境中工作。我不认为任何商业CA会给你一个基于IP地址的证书。我建议设置DNS条目(即使它只是在测试环境中的hosts文件中)

    即使您没有使用IP地址,也必须确保该证书对您试图与服务器联系的主机名有效:如果您有Subject Alternative name条目,其中一个条目必须与主机名匹配;否则,主机名必须位于此证书的主题DN的CN RDN中