【问题标题】:Writing a SSL Checker using Java使用 Java 编写 SSL 检查器
【发布时间】:2012-01-22 15:50:01
【问题描述】:

有没有人知道关于用 Java 编写 SSL 检查器的任何好的教程、网站和/或书籍?我正在尝试做可以在这里找到的事情:http://www.sslshopper.com/ssl-checker.html。 我不是要创建自签名证书或使用密钥库。我希望能够访问任何站点,确定是否存在有效的 SSL 证书,确定证书上的主机名是否与输入的名称匹配,并确定该证书何时到期。我已经用谷歌搜索了这个主题,但“如何使用 Java 创建 SSL 购物者”并没有给我任何帮助,我的其他搜索只为我提供了有关如何创建自签名证书的链接。任何帮助将不胜感激。

【问题讨论】:

    标签: java ssl x509


    【解决方案1】:

    首先要获取服务器证书以便手动进行验证,无论它是否有效,最简单的方法是在禁用任何证书验证后通过SSLSocket 连接。

    创建一个SSLContext 让任何东西都可以通过:

    SSLContext sslContext = SSLContext.getInstance("TLS");
    X509TrustManager passthroughTrustManager = new X509TrustManager() {
        @Override
        public void checkClientTrusted(X509Certificate[] chain,
                String authType) throws CertificateException {
        }
    
        @Override
        public void checkServerTrusted(X509Certificate[] chain,
                String authType) throws CertificateException {
        }
    
        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    };
    sslContext.init(null, new TrustManager[] { passthroughTrustManager },
            null);
    

    (旁注:对于寻找在内部使用测试证书的方法的人,我建议构建自己的测试 CA 而不是禁用检查,以防这些虚拟检查错误地留在生产代码中,并且因为它使测试更加真实。)

    创建一个套接字,连接并显式地开始握手(因为你不会真正阅读它):

    SSLSocketFactory ssf = sslContext.getSocketFactory();
    SSLSocket socket = (SSLSocket) ssf.createSocket(
            "the.host.name", 443);
    socket.startHandshake();
    

    获取对等证书链。第一项是实际的服务器证书。

    X509Certificate[] peerCertificates = (X509Certificate[]) socket
            .getSession().getPeerCertificates();
    

    如果您想针对默认信任锚(默认受信任的 CA 证书)进行验证,请根据默认值构建一个 X509TrustManager(这实际上是上面的 passthroughTrustManager 禁用的):

    // By default on Oracle JRE, algorithm is PKIX
    TrustManagerFactory tmf = TrustManagerFactory
            .getInstance(TrustManagerFactory.getDefaultAlgorithm());
    // 'null' will initialise the tmf with the default CA certs installed
    // with the JRE.
    tmf.init((KeyStore) null);
    X509TrustManager tm = (X509TrustManager) tmf.getTrustManagers()[0];
    

    现在,及时检查证书当前是否有效,是否针对受信任的锚进行验证,以及是否根据RFC 3280具有正确的扩展名。

    try {
        // Assuming RSA key here.
        tm.checkServerTrusted(peerCertificates, "RSA");
    } catch (CertificateException e) {
        // Here you may check which subclass of CertificateException to know what the error is.
    }
    

    以上所有内容都使用了JSSE API

    或者,如果您想深入了解 PKIX 的细节,可以使用Java Certification Path API(JSSE 使用)。

    (如果您只想要有效的证书,只需在开始时使用SSLContext sslContext = SSLContext.getDefault(),创建套接字并进行握手。)

    要直接获取服务器证书,可以这样:

    X509Certificate serverCert = peerCertificates[0];
    

    X509Certificate 有许多关于日期和各种扩展的方法。例如:

    Date expirationDate = serverCert.getNotAfter();
    

    主机名验证应遵循RFC 6125(或至少RFC 2818)。简而言之,检查您要连接的主机名是否是主题备用名称 (SAN) DNS 条目之一。否则,请返回检查主机名是否在证书的 CN RDN 中(为此,您需要将主题 DN 拆分为 RDN)。你可能也对this discussion (on the specific case of using IP addresses)感兴趣。

    这完全取决于您要进行多少“手动”验证。除了 API 文档之外,还有许多规范需要阅读(至少是 RFC 5280/RFC 3280 和 RFC 6125/RFC 2818)。

    Security.SE 上的这个问题也应该很有趣:

    【讨论】:

    • 我在尝试执行此操作时得到不可转换的类型:X509Certificate[] peerCertificates = (X509Certificate[])socket.getSession().getPeerCertificates();
    • 我什至使用上述方法尝试了 Oracles 网站上的一个示例,并不断获得“不可转换的类型”
    • 没关系,我看到了,我使用的是 import javax.security.cert.X509Certificate;而不是 import java.security.cert.X509Certificate;
    【解决方案2】:

    你可以做的是在例如Apache's HttpClient之上实现你自己的检查器
    您应该初始化 SSL 上下文并覆盖 TrustManagers 以执行您想要的任何检查。

    库可以自动完成主机名验证。

    例如如果主机名与证书的信息不匹配,以下将配置 SSL 套接字处理以引发异常:

    SSLSocketFactory sf = new SSLSocketFactory(
        SSLContext.getInstance("TLS"),
        SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
    

    【讨论】:

      猜你喜欢
      • 2020-11-18
      • 2021-12-03
      • 1970-01-01
      • 2013-08-04
      • 1970-01-01
      • 2010-09-08
      • 1970-01-01
      • 2016-11-13
      • 1970-01-01
      相关资源
      最近更新 更多