6

我的主机名错误异常。我在我的程序中使用了此代码(从某个链接获取)。我的程序运行良好。我的问题是它足够安全吗?(因为它没有验证证书链)

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

4 回答 4

4

中的代码miTM实际上禁用了任何 SSL 安全检查,因此安全级别非常低(如果 SSL 证书损坏,您只会收到错误消息,但当证书与域不匹配时您不会收到错误消息)。

基本上,您尝试在没有任何安全性的情况下建立连接。如果这就是您想要的,那么解决方案可能“足够安全”,但最有可能的答案是“不”。

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

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

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

于 2012-04-10T09:10:04.217 回答
1

为了使连接安全,您必须(至少):

  • 验证您信任证书,
  • 验证主机名(除非您确定这是您可能信任的唯一且唯一的证书)。

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

  • 您正在使用的TrustManager根本不检查证书(它从不抛出异常,而 API 期望它CertificateException在证书不受信任时抛出一种形式)。
  • 您的主机名验证程序始终返回true.

要修复您的代码:

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

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

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

如果存在 dNSName 类型的 subjectAltName 扩展,则必须将其用作身份。否则,必须使用证书主题字段中的(最具体的)通用名称字段。尽管使用通用名称是现有的做法,但它已被弃用,并且鼓励证书颁发机构使用 dNSName 代替。

[...]

在某些情况下,URI 被指定为 IP 地址而不是主机名。在这种情况下,iPAddress subjectAltName 必须出现在证书中,并且必须与 URI 中的 IP 完全匹配。

这意味着您不能只是将 IP 地址放在服务器证书中主题 DN 的公用名 (CN) 中。如果您使用的是 IP 地址,则它必须位于主题备用名称条目中。(从 Java 7 开始,keytool可以选择生成此类证书。)

您将在此答案中找到有关使用哪些命令的更多详细信息。

话虽如此,使用 IP 地址最多只能在测试环境中真正起作用。我认为任何商业 CA 都不会为您提供基于 IP 地址的证书。我建议设置 DNS 条目(即使它只是在hosts测试环境中的文件中)。

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

于 2012-04-20T09:34:04.443 回答
1

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

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

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

keytool -import -alias myservice -file servercertificate.cer

我更喜欢的另一个选择是,在更新 Java 时避免回归,是cacerts在您自己的位置复制并声明它,这要归功于javax.net.ssl.trustStore系统属性。

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

于 2012-04-14T19:45:56.637 回答
1

在java中很多次用来获取这种异常

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

我已经通过使用其适当的 IP 地址并安装证书来解决它。

于 2012-04-20T04:01:34.333 回答