【问题标题】:Java: Overriding function to disable SSL certificate checkJava:禁用 SSL 证书检查的覆盖函数
【发布时间】:2013-11-12 10:57:04
【问题描述】:

Web 服务基于 SSL,它具有自签名证书,托管在远程系统中。我已经创建了一个访问该 Web 服务的客户端。这是通过将证书添加到key store programatically 来完成的。

现在我听说,没有必要将证书添加到密钥库以访问自签名 Web 服务。 相反,我们可以通过覆盖某些方法来禁用证书检查。这是真的?这些方法是什么?请帮忙。

【问题讨论】:

  • Web 服务不应具有自签名证书。时期。付钱并让CA签署。你所听到的确实是正确的,但它也是非常不可取的。它需要在客户端进行特殊编程,这会引入 (a) 安全问题、(b) 开发风险和 (c) 部署问题。
  • Web 服务尚未发布。它正在工作中。这就是为什么现在它没有得到可信 CA 的认证。众所周知,通过禁用这些证书检查来使用服务是不可取的。尽管我需要检查它。请帮忙。谢谢@EJP
  • 为临时解决方案付出的任何努力 (a) 都是浪费时间和金钱,并且 (b) 引入了安全风险,即您将“意外”将其部署到生产环境中。现在花钱。才几百块钱。
  • @EJP,所有问题都在那里。但出于研究目的,现在我需要通过禁用此检查来访问该服务。
  • @Sumithlal,出于学习的目的,如果你真的买不到证书,最好使用自己的 CA(xca 或 tinyca 之类的工具可以提供帮助)并将自己的 CA 证书部署在您客户的信任库(不一定以编程方式)。这肯定比更改代码中的证书验证方式更现实,更不容易出现安全错误。

标签: java web-services ssl self-signed overriding


【解决方案1】:

这应该足够了。我在针对没有正确签名证书的测试和登台服务器测试代码时使用它。 但是,您确实应该认真考虑在生产服务器上获取有效的 SSL 证书。没有人愿意被窃听并侵犯他们的隐私。

SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, new TrustManager[] { new TrustAllX509TrustManager() }, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier( new HostnameVerifier(){
    public boolean verify(String string,SSLSession ssls) {
        return true;
    }
});

还有这个。

import javax.net.ssl.X509TrustManager;
import java.security.cert.X509Certificate;

/**
 * DO NOT USE IN PRODUCTION!!!!
 * 
 * This class will simply trust everything that comes along.
 * 
 * @author frank
 *
 */
public class TrustAllX509TrustManager implements X509TrustManager {
    public X509Certificate[] getAcceptedIssuers() {
        return new X509Certificate[0];
    }

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

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

}

祝你好运!

===更新===

我只想指出,有一个名为Let's Encrypt 的服务可以自动生成和设置几乎所有人都认可的 SSL/TLS 证书,而且它完全免费!

【讨论】:

  • 感谢您的回复。这只有在 jssecert 或 cacert 中添加了证书时才有效。如果证书被删除并运行,错误我得到 SSLHandshakeException 。它还警告“无法发送消息。”
  • 不应该这样。使用带有自签名证书的密钥库时,您可以使用 javax.ssl.something=keystore.jks 设置密钥库。使用此方法时不应设置此项。我会尽快重新测试。
  • getAcceptedIssuers() 的返回值不能为空。请参阅 Javadoc。
  • 有没有人想知道这在生产代码中多久会变成复制粘贴?
  • 靠……我是不是降低了互联网的安全和隐私标准?
【解决方案2】:

忽略每个连接的证书更安全,因为任何其他代码仍将使用安全默认值。

以下代码:

  • 基于每个连接覆盖信任管理器和主机名验证器。
  • 重用 SSLSocketFactory 以支持持久连接,绕过对同一服务器的重复请求的昂贵 SSL 握手。

正如其他人所说,这只能用于测试,和/或用于与其他内部系统通信的内部系统。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class TestPersistentConnection
{
    private static SSLSocketFactory sslSocketFactory = null;

    /**
     * Use the VM argument <code>-Djavax.net.debug=ssl</code> for SSL specific debugging;
     * the SSL handshake will appear a single time when connections are re-used, and multiple
     * times when they are not.
     * 
     * Use the VM <code>-Djavax.net.debug=all</code> for all network related debugging, but 
     * note that it is verbose.
     * 
     * @throws Exception
     */
    public static void main(String[] args) throws Exception
    {

        //URL url = new URL("https://google.com/");
        URL url = new URL("https://localhost:8443/");

        // Disable first
        request(url, false);

        // Enable; verifies our previous disable isn't still in effect.
        request(url, true);
    }

    public static void request(URL url, boolean enableCertCheck) throws Exception {
        BufferedReader reader = null;
        // Repeat several times to check persistence.
        System.out.println("Cert checking=["+(enableCertCheck?"enabled":"disabled")+"]");
        for (int i = 0; i < 5; ++i) {
            try {

                HttpURLConnection httpConnection = (HttpsURLConnection) url.openConnection();

                // Normally, instanceof would also be used to check the type.
                if( ! enableCertCheck ) {
                    setAcceptAllVerifier((HttpsURLConnection)httpConnection);
                }

                reader = new BufferedReader(new InputStreamReader(httpConnection.getInputStream()), 1);

                char[] buf = new char[1024];
                StringBuilder sb = new StringBuilder();
                int count = 0;
                while( -1 < (count = reader.read(buf)) ) {
                    sb.append(buf, 0, count);
                }
                System.out.println(sb.toString());

                reader.close();

            } catch (IOException ex) {
                System.out.println(ex);

                if( null != reader ) {
                    reader.close();
                }
            }
        }
    }

    /**
     * Overrides the SSL TrustManager and HostnameVerifier to allow
     * all certs and hostnames.
     * WARNING: This should only be used for testing, or in a "safe" (i.e. firewalled)
     * environment.
     * 
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     */
    protected static void setAcceptAllVerifier(HttpsURLConnection connection) throws NoSuchAlgorithmException, KeyManagementException {

        // Create the socket factory.
        // Reusing the same socket factory allows sockets to be
        // reused, supporting persistent connections.
        if( null == sslSocketFactory) {
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, ALL_TRUSTING_TRUST_MANAGER, new java.security.SecureRandom());
            sslSocketFactory = sc.getSocketFactory();
        }

        connection.setSSLSocketFactory(sslSocketFactory);

        // Since we may be using a cert with a different name, we need to ignore
        // the hostname as well.
        connection.setHostnameVerifier(ALL_TRUSTING_HOSTNAME_VERIFIER);
    }

    private static final TrustManager[] ALL_TRUSTING_TRUST_MANAGER = new TrustManager[] {
        new X509TrustManager() {
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
            public void checkClientTrusted(X509Certificate[] certs, String authType) {}
            public void checkServerTrusted(X509Certificate[] certs, String authType) {}
        }
    };

    private static final HostnameVerifier ALL_TRUSTING_HOSTNAME_VERIFIER  = new HostnameVerifier() {
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    };

}

非常感谢: http://runtime32.blogspot.com/2008/11/let-java-ssl-trust-all-certificates.html

【讨论】:

  • 您完全确定它可以在每个连接的基础上工作吗?对于SSLContext.getInstance,我永远不知道它是吸气剂还是什么......
  • 相当肯定。示例程序中发生的唯一替换是 enableCertCheck 为 false 时(通过 setAcceptAllVerifier(...))。如果系统范围的套接字工厂和主机名验证器被替换,那么在 main 中对 request(url, true) 的第二次调用将在标准输出上打印一个异常。否则,改变它的唯一方法是暂时通过连接本身,这将在连接完成后恢复它,这似乎不太可能。
  • 这行得通!我有用于禁用证书检查的setDefaultSSLSocketFactory 版本,但它使我与确实需要证书检查的第二个站点的其他连接失败。我刚刚对此进行了调整,现在我可以连接到两个 https 站点
  • 是的,这也是我需要的。只需要禁用一个 HTTPS 验证来进行保持活动的 https 检查。但我仍然需要它用于 JVM 的其余部分。其他所有文章或帖子似乎都静态关闭了 all ssl 验证。
【解决方案3】:
TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
                    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }
                    public void checkClientTrusted(X509Certificate[] certs, String authType) {
                    }
                    public void checkServerTrusted(X509Certificate[] certs, String authType) {
                    }
                }
            };
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, trustAllCerts, new java.security.SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

            HostnameVerifier allHostsValid = new HostnameVerifier() {
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            };
            HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
            GetCustomerPhone http = new GetCustomerPhone(); 

    System.out.println("Processing..");     
     try{
            http.sendPost();    
        }
    catch(Exception e){
            e.printStackTrace();
        }               
}

我认为它会正常工作。因为我很好......

【讨论】:

    猜你喜欢
    • 2017-02-03
    • 2018-11-11
    • 2016-11-13
    • 2016-10-07
    • 1970-01-01
    • 2019-06-28
    • 2014-06-23
    • 2017-08-06
    • 1970-01-01
    相关资源
    最近更新 更多