【问题标题】:Difference in dealing with ssl certificate in respect of browser and jvm?在浏览器和 jvm 方面处理 ssl 证书的区别?
【发布时间】:2012-05-30 09:55:06
【问题描述】:

这是浏览器在连接到 https 站点时处理 ssl 证书的方式

  1. 当我在浏览器中输入 https://myDemoSite.com 时,首先我的浏览器会从 myDemoSite.com 请求证书(由于 https),然后 myDemoSite 将该证书发送到浏览器

  2. 一旦浏览器收到该证书,浏览器将检查它是否像威瑞信那样由经过验证的权威机构签署

  3. 如果第二步是,那么作为第三步,它检查证书问题是否与用户在浏览器中输入的 url 相同

现在我通过 java 程序连接 https 站点说 HttpsConnectProg1。我的问题是程序 i.e HttpsConnectProg1 将如何处理这个 使用 https 站点连接颁发的证书(尽管此 https 站点颁发的证书是经过验证的,即由经过验证的权威机构签署)。

我只是尝试了一个小 连接到颁发认证证书的 https 站点的程序。我预计会出现一些错误,例如 sslhandshake 错误或一些证书错误 (因为我没有在 $JAVA_HOME\jre\lib\security 文件夹中添加这个认证证书)但令我惊讶的是我没有收到任何错误。

现在重要的问题是 HttpsConnectProg1 是否检查浏览器完成的第 3 步,因为它是非常重要的一步?在这里供您参考

 import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLConnection;
 import java.util.Properties;
 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.HttpsURLConnection;
 import javax.net.ssl.SSLSession;


 public class ConnectToDemoSite {

/**
 * @param args
 */
public static void main(String[] args) {
    // TODO Auto-generated method stub

    String urlStr = "https://www.myDemoSite.com/demo1/";
    URL url;
    Properties reply = new Properties();
    try {
        url = new URL(urlStr);
        URLConnection conn = url.openConnection();
        if(conn instanceof HttpsURLConnection)
        {
        HttpsURLConnection conn1 = (HttpsURLConnection)url.openConnection();
            conn1.setHostnameVerifier(new HostnameVerifier()  
            {        
                public boolean verify(String hostname, SSLSession session)  
                {  
                    return true;  
                }  
            });  

        reply.load(conn1.getInputStream());
        }
        else 
        {
             conn = url.openConnection();
             reply.load(conn.getInputStream());
        }
    } catch (MalformedURLException e) {
       e.printStackTrace();
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    catch (Exception e) {
        e.printStackTrace();
    }
    System.out.println("reply is"+reply);



 }

}

【问题讨论】:

    标签: java ssl https


    【解决方案1】:

    当您使用 Java 连接到 https:// URI 时,它会使用 Java Secure Socket Extension (JSSE)(除非您真的想使用 SSL/TLS 的自定义实现,但这非常罕见)。

    有多种方法可以调整信任管理(主要是使用自定义TrustManagers),否则它将使用一定数量的合理设置。

    在您的示例中,将使用默认的 SSLContext 验证证书,该证书本身配置有默认的 X509TrustManager,并从 cacerts 读取信任锚(请参阅Customization section of the JSSE Ref. Guide 中的表格)。

    默认情况下,JRE 在cacerts 中附带了许多预信任的 CA 证书(如大多数浏览器或操作系统),这通常类似于您在浏览器中找到的证书。这是 JSSE 参考。指南说:

    重要提示:JDK 附带有限数量的受信任根 /lib/security/cacerts 文件中的证书。作为 记录在 keytool 中,您有责任维护(即, 添加/删除)如果您使用此文件中包含的证书 文件作为信任库。

    根据您联系的服务器的证书配置, 您可能需要添加额外的根证书。获取所需 来自相应供应商的特定根证书。

    如果证书是可信的,它会检查主机名是否对预期的 URL 有效。 (请注意,检查的不是完整的 URL,而只是主机名。)

    这些规则在RFC 2818 (the HTTPS specification), Section 3.1 中定义。 (Java 7 还没有实现 RFC 6125,但规则非常相似,尤其是对于 HTTPS。)编辑: 建立连接后,URLConnection(以及底层的SSLSession)设置为服务器的主机名。简而言之,按照 RFC 2818 中的规则,它会在证书的主题备用名称 (SAN) 扩展中查看服务器证书以查找 DNS 条目,以查看它是否与为连接设置的主机名匹配,或者查找该名称在证书的主题 DN 的公用名 (CN) 中,当不存在 SAN DNS 条目时。

    主机名验证通常由默认主机名验证器完成。在您的示例中,您已将默认验证器替换为始终返回 true 的验证器。因此,在您的情况下,此验证实际上不会发生,并且所有内容都将被接受(您这样做会引入安全漏洞)。

    此外,在 Java 中完成的默认主机名验证比许多浏览器更严格地遵循 RFC 2818。特别是不会accept IP addresses in CNs

    (出于与您应该使用始终返回 true 的主机名验证程序相同的原因,您不应该使用不做任何事情的信任管理器,因为您会看到一些示例,提供快速修复一些 SSL 错误消息。)

    【讨论】:

    • 两个问题,即问题 1:- 如您所说,如果证书是可信的,它会检查主机名是否对预期的 URL 有效。似乎证书中也有主机信息,以便 HttpsURLConnection 可以检查证书中的主机是否与正在尝试连接的 url 程序匹配。正确的?问题 2:- 正如您所说,在您的示例中,您已将默认验证器替换为始终返回 true 的验证器,我这样做是因为我遇到了一些奇怪的错误,并且当时我使用的是自签名证书。我不是即使返回 true 也是一个好习惯
    • 我添加了更多关于问题 1 的内容:它确实将主机名与证书中的某些信息(SAN 扩展或 CN)进行了比较。关于问题 2,无论如何您都应该始终验证主机名;唯一的例外是,如果您想为特定主机锁定特定证书,但您必须事先手动验证此异常(类似于您在 Firefox 中添加手动异常时所做的检查:您d 需要使用其他受信任的信息来源查看证书以检查它是否符合您的预期)。
    • 关于问题 2 的更多信息。您说过无论如何您都应该始终验证主机名。根据我的理解,如果我不明确返回 true 内部验证方法,它将自动完成?您还在原始帖子中说过,出于与您应该使用始终返回 true 的主机名验证程序相同的原因,您不应该使用不做任何事情的信任管理器。我想你是说如果在 verify 方法中返回 true,我的信任管理器应该注意匹配主机名逻辑?
    • 不,我是说您不应该设置自定义主机名验证器。使用默认的。
    【解决方案2】:

    Java 包含来自知名证书颁发机构的根证书,因此如果您拥有真正的证书,它的运行方式与浏览器非常相似。

    如果您签署自己的证书,浏览器会向您发出警告并提供用户界面,以便将来可以使用该证书。这就是 Java 程序的不同之处——处理这个问题的正确方法完全取决于您正在编写的程序,并且不可能有一种万能的方法。

    如果您的应用程序具有 UI,您可以执行类似于浏览器的操作。

    另一方面,“无头”应用程序通常预先配置了必要的根证书。

    最终,浏览器和 Java PKIX 验证库都通过要求您提供或批准身份验证最终依赖的根证书来维护 TLS 的安全性。如果没有受信任的根证书,就无法对服务器进行身份验证,因此无法确保通信的隐私或完整性。

    【讨论】:

      猜你喜欢
      • 2012-06-19
      • 2018-05-04
      • 2016-08-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-05
      • 1970-01-01
      • 2012-12-04
      相关资源
      最近更新 更多