Java 为 SSL 套接字启用哪些密码套件?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1037590/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 22:33:31  来源:igfitidea点击:

Which Cipher Suites to enable for SSL Socket?

javasslencryption

提问by Zarkonnen

I'm using Java's SSLSocket to secure communications between a client and a server program. The server program also serves up HTTPS requests from web browsers.

我正在使用 Java 的 SSLSocket 来保护客户端和服务器程序之间的通信。服务器程序还提供来自 Web 浏览器的 HTTPS 请求。

According to "Beginning Cryptography with Java", page 371, you should always call setEnabledCipherSuiteson your SSLSocket/ SSLServerSocketto ensure that the cipher suite that ends up being negotiated is sufficiently strong for your purposes.

据“开始加密与Java”,371页,你应该总是叫setEnabledCipherSuites上你的SSLSocket/ SSLServerSocket,以确保密码套件最终被谈判是为您的目的足够强。

That being said, a call to my SSLSocketFactory's getDefaultCipherSuitesmethod yields some 180options. These options range from TLS_RSA_WITH_AES_256_CBC_SHA(which I think is fairly secure) to SSL_RSA_WITH_RC4_128_MD5(not so sure if that's secure, given MD5's current status) to SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA(not entirely sure what that does).

话虽这么说,我一个电话SSLSocketFactorygetDefaultCipherSuites方法产生了一些180的选项。这些选项范围从TLS_RSA_WITH_AES_256_CBC_SHA(我认为相当安全)到SSL_RSA_WITH_RC4_128_MD5(不太确定这是否安全,鉴于 MD5 的当前状态)到SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA(不完全确定它的作用)。

What's a sensible list of cipher suites to restrict the sockets to?

限制套接字的密码套件的合理列表是什么?

Note that the client and server have access to the Bouncy Castleservice provider, and that they may or may not have unlimited cryptographic policy files installed.

请注意,客户端和服务器可以访问Bouncy Castle服务提供者,并且它们可能安装也可能没有安装无限制的加密策略文件。

采纳答案by Matthew Flaschen

Don't use anything with export in it. That's crippleware due to export restrictions on strong cryptography.

不要使用任何带有 export 的东西。由于对强加密的出口限制,这是一种瘫痪软件。

EDIT: Changed to use 2009 document.

编辑:更改为使用 2009 文档。

A 2009 NIST recommendationlists the following, incluing TLS_RSA_WITH_AES_256_CBC_SHA (which you mentioned):

2009 年 NIST建议列出了以下内容,包括 TLS_RSA_WITH_AES_256_CBC_SHA(您提到的):

TLS_RSA_WITH_NULL_SHA(don't use this unless you're sure you don't need any privacy/confidentiality).

TLS_RSA_WITH_NULL_SHA(除非您确定不需要任何隐私/机密,否则不要使用它)。

TLS_RSA_WITH_3DES_EDE_CBC_SHA
TLS_RSA_WITH_AES_128_CBC_SHA
TLS_RSA_WITH_AES_256_CBC_SHA
TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA
TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA
TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
TLS_DH_DSS_WITH_AES_128_CBC_SHA
TLS_DH_RSA_WITH_AES_128_CBC_SHA
TLS_DHE_DSS_WITH_AES_128_CBC_SHA
TLS_DHE_RSA_WITH_AES_128_CBC_SHA
TLS_DH_DSS_WITH_AES_256_CBC_SHA
TLS_DH_RSA_WITH_AES_256_CBC_SHA
TLS_DHE_DSS_WITH_AES_256_CBC_SHA
TLS_DHE_RSA_WITH_AES_256_CBC_SHA
TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
TLS_PSK_WITH_3DES_EDE_CBC_SHA
TLS_PSK_WITH_AES_128_CBC_SHA
TLS_PSK_WITH_AES_256_CBC_SHA
TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA
TLS_DHE_PSK_WITH_AES_128_CBC_SHA
TLS_DHE_PSK_WITH_AES_256_CBC_SHA
TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA
TLS_RSA_PSK_WITH_AES_128_CBC_SHA
TLS_RSA_PSK_WITH_AES_256_CBC_SHA
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 

回答by jww

Below is the Java class I use to enforce cipher suites and protocols. Prior to SSLSocketFactoryEx, I was modifying properties on the SSLSocketwhen I had access to them. The Java folks on Stack Overflow helped with it, so its nice to be able to post it here.

下面是我用来强制执行密码套件和协议的 Java 类。在 之前SSLSocketFactoryExSSLSocket当我可以访问它们时,我正在修改它们的属性。Stack Overflow 上的 Java 人员帮助了它,所以很高兴能够在这里发布它。

SSLSocketFactoryExprefers stronger cipher suites (like ECDHEand DHE), and it omits weak and wounded cipher suites (like RC4and MD5). It does have to enable four RSA key transport ciphers for interop with Google and Microsoft when TLS 1.2 is notavailable. They are TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_RSA_WITH_AES_256_CBC_SHAand two friends. If possible, you should remove the TLS_RSA_*key transport schemes.

SSLSocketFactoryEx更喜欢更强的密码套件(如ECDHEDHE),并省略弱和受伤的密码套件(如RC4MD5)。当 TLS 1.2不可用时,它必须启用四个 RSA 密钥传输密码才能与 Google 和 Microsoft 进行互操作。他们是TLS_RSA_WITH_AES_256_CBC_SHA256TLS_RSA_WITH_AES_256_CBC_SHA还有两个朋友。如果可能,您应该删除TLS_RSA_*密钥传输方案。

Keep the cipher suite list as small as possible. If you advertise allavailable ciphers (similar to Flaschen's list), then your list will be 80+. That takes up 160 bytes in the ClientHello, and it can cause some appliances to fail because they have a small, fixed-size buffer for processing the ClientHello. Broken appliances include F5 and Ironport.

保持密码套件列表尽可能小。如果您宣传所有可用的密码(类似于 Flaschen 的列表),那么您的列表将是 80+。这ClientHelloClientHello. 损坏的设备包括 F5 和 Ironport。

In practice, the list in the code below is paired down to 10 or 15 cipher suites once the preferred list intersects with Java's supported cipher suites. For example, here's the list I get when preparing to connect or microsoft.com or google.com with an unlimited JCE policy in place:

在实践中,一旦首选列表与 Java 支持的密码套件相交,下面代码中的列表就会配对到 10 或 15 个密码套件。例如,这是我在准备连接或 microsoft.com 或 google.com 时获得的列表,其中包含无限制的 JCE 策略:

  • TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
  • TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
  • TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
  • TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
  • TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
  • TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
  • TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  • TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
  • TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
  • TLS_DHE_RSA_WITH_AES_128_CBC_SHA
  • TLS_DHE_DSS_WITH_AES_128_CBC_SHA
  • TLS_RSA_WITH_AES_256_CBC_SHA256
  • TLS_RSA_WITH_AES_256_CBC_SHA
  • TLS_RSA_WITH_AES_128_CBC_SHA256
  • TLS_RSA_WITH_AES_128_CBC_SHA
  • TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
  • TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
  • TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
  • TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
  • TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
  • TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
  • TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  • TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
  • TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
  • TLS_DHE_RSA_WITH_AES_128_CBC_SHA
  • TLS_DHE_DSS_WITH_AES_128_CBC_SHA
  • TLS_RSA_WITH_AES_256_CBC_SHA256
  • TLS_RSA_WITH_AES_256_CBC_SHA
  • TLS_RSA_WITH_AES_128_CBC_SHA256
  • TLS_RSA_WITH_AES_128_CBC_SHA

The list omits weak/wounded algorithms, like RC4 and MD5. If they are enabled, then you will likely get a Obsolete cryptography warning from Browseron occasion.

该列表省略了弱/受伤算法,如 RC4 和 MD5。如果它们被启用,那么您有时可能会从浏览器收到过时的加密警告

The list will be smaller with the default JCE policy because the policy removes AES-256 and some others. I think its about 7 cipher suites with the restricted policy.

默认 JCE 策略的列表会更小,因为该策略删除了 AES-256 和其他一些。我认为它大约有 7 个具有受限策略的密码套件。

The SSLSocketFactoryExclass also ensures protocols TLS 1.0 and above are used. Java clients prior to Java 8 disable TLS 1.1 and 1.2. SSLContext.getInstance("TLS")will also sneak in SSLv3(even in Java 8), so steps have to be taken to remove it.

SSLSocketFactoryEx类也确保协议TLS 1.0及以上使用。Java 8 之前的 Java 客户端禁用 TLS 1.1 和 1.2。SSLContext.getInstance("TLS")也会潜入SSLv3(即使在 Java 8 中),因此必须采取措施将其删除。

Finally, the class below is TLS 1.3 aware, so it should work when the provider makes them available. The *_CHACHA20_POLY1305cipher suites are preferred if available because they are so much faster than some of the current suites and they have better security properties. Google has already rolled it out on its servers. I'm not sure when Oracle will provide them. OpenSSL will provide them with OpenSSL 1.0.21.1.0.

最后,下面的类是 TLS 1.3 感知的,所以当提供者使它们可用时它应该可以工作。*_CHACHA20_POLY1305如果可用,密码套件是首选,因为它们比当前的某些套件快得多,并且具有更好的安全属性。谷歌已经在其服务器上推出了它。我不确定 Oracle 何时会提供它们。OpenSSL 将为他们提供 OpenSSL 1.0.2 1.1.0

You can use it like so:

你可以像这样使用它:

URL url = new URL("https://www.google.com:443");
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();

SSLSocketFactoryEx factory = new SSLSocketFactoryEx();
connection.setSSLSocketFactory(factory);
connection.setRequestProperty("charset", "utf-8");

InputStream input = connection.getInputStream();
InputStreamReader reader = new InputStreamReader(input, "utf-8");
BufferedReader buffer = new BufferedReader(reader);
...


class SSLSocketFactoryEx extends SSLSocketFactory
{
    public SSLSocketFactoryEx() throws NoSuchAlgorithmException, KeyManagementException
    {
        initSSLSocketFactoryEx(null,null,null);
    }

    public SSLSocketFactoryEx(KeyManager[] km, TrustManager[] tm, SecureRandom random) throws NoSuchAlgorithmException, KeyManagementException
    {
        initSSLSocketFactoryEx(km, tm, random);
    }

    public SSLSocketFactoryEx(SSLContext ctx) throws NoSuchAlgorithmException, KeyManagementException
    {
        initSSLSocketFactoryEx(ctx);
    }

    public String[] getDefaultCipherSuites()
    {
        return m_ciphers;
    }

    public String[] getSupportedCipherSuites()
    {
        return m_ciphers;
    }

    public String[] getDefaultProtocols()
    {
        return m_protocols;
    }

    public String[] getSupportedProtocols()
    {
        return m_protocols;
    }

    public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException
    {
        SSLSocketFactory factory = m_ctx.getSocketFactory();
        SSLSocket ss = (SSLSocket)factory.createSocket(s, host, port, autoClose);

        ss.setEnabledProtocols(m_protocols);
        ss.setEnabledCipherSuites(m_ciphers);

        return ss;
    }

    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException
    {
        SSLSocketFactory factory = m_ctx.getSocketFactory();
        SSLSocket ss = (SSLSocket)factory.createSocket(address, port, localAddress, localPort);

        ss.setEnabledProtocols(m_protocols);
        ss.setEnabledCipherSuites(m_ciphers);

        return ss;
    }

    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException
    {
        SSLSocketFactory factory = m_ctx.getSocketFactory();
        SSLSocket ss = (SSLSocket)factory.createSocket(host, port, localHost, localPort);

        ss.setEnabledProtocols(m_protocols);
        ss.setEnabledCipherSuites(m_ciphers);

        return ss;
    }

    public Socket createSocket(InetAddress host, int port) throws IOException
    {
        SSLSocketFactory factory = m_ctx.getSocketFactory();
        SSLSocket ss = (SSLSocket)factory.createSocket(host, port);

        ss.setEnabledProtocols(m_protocols);
        ss.setEnabledCipherSuites(m_ciphers);

        return ss;
    }

    public Socket createSocket(String host, int port) throws IOException
    {
        SSLSocketFactory factory = m_ctx.getSocketFactory();
        SSLSocket ss = (SSLSocket)factory.createSocket(host, port);

        ss.setEnabledProtocols(m_protocols);
        ss.setEnabledCipherSuites(m_ciphers);

        return ss;
    }

    private void initSSLSocketFactoryEx(KeyManager[] km, TrustManager[] tm, SecureRandom random)
    throws NoSuchAlgorithmException, KeyManagementException
    {
        m_ctx = SSLContext.getInstance("TLS");
        m_ctx.init(km, tm, random);

        m_protocols = GetProtocolList();
        m_ciphers = GetCipherList();
    }

    private void initSSLSocketFactoryEx(SSLContext ctx)
    throws NoSuchAlgorithmException, KeyManagementException
    {
        m_ctx = ctx;

        m_protocols = GetProtocolList();
        m_ciphers = GetCipherList();
    }

    protected String[] GetProtocolList()
    {
        String[] preferredProtocols = { "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3" };
        String[] availableProtocols = null;

        SSLSocket socket = null;

        try
        {
            SSLSocketFactory factory = m_ctx.getSocketFactory();
            socket = (SSLSocket)factory.createSocket();

            availableProtocols = socket.getSupportedProtocols();
            Arrays.sort(availableProtocols);
        }
        catch(Exception e)
        {
            return new String[]{ "TLSv1" };
        }
        finally
        {
            if(socket != null)
                socket.close();
        }

        List<String> aa = new ArrayList<String>();
        for(int i = 0; i < preferredProtocols.length; i++)
        {
            int idx = Arrays.binarySearch(availableProtocols, preferredProtocols[i]);
            if(idx >= 0)
                aa.add(preferredProtocols[i]);
        }

        return aa.toArray(new String[0]);
    }

    protected String[] GetCipherList()
    {
        String[] preferredCiphers = {

            // *_CHACHA20_POLY1305 are 3x to 4x faster than existing cipher suites.
            //   http://googleonlinesecurity.blogspot.com/2014/04/speeding-up-and-strengthening-https.html
            // Use them if available. Normative names can be found at (TLS spec depends on IPSec spec):
            //   http://tools.ietf.org/html/draft-nir-ipsecme-chacha20-poly1305-01
            //   http://tools.ietf.org/html/draft-mavrogiannopoulos-chacha-tls-02
            "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
            "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
            "TLS_ECDHE_ECDSA_WITH_CHACHA20_SHA",
            "TLS_ECDHE_RSA_WITH_CHACHA20_SHA",

            "TLS_DHE_RSA_WITH_CHACHA20_POLY1305",
            "TLS_RSA_WITH_CHACHA20_POLY1305",
            "TLS_DHE_RSA_WITH_CHACHA20_SHA",
            "TLS_RSA_WITH_CHACHA20_SHA",

            // Done with bleeding edge, back to TLS v1.2 and below
            "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
            "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
            "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
            "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",

            "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
            "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
            "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
            "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",

            // TLS v1.0 (with some SSLv3 interop)
            "TLS_DHE_RSA_WITH_AES_256_CBC_SHA384",
            "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
            "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
            "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",

            "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
            "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
            "SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA",
            "SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA",

            // RSA key transport sucks, but they are needed as a fallback.
            // For example, microsoft.com fails under all versions of TLS
            // if they are not included. If only TLS 1.0 is available at
            // the client, then google.com will fail too. TLS v1.3 is
            // trying to deprecate them, so it will be interesteng to see
            // what happens.
            "TLS_RSA_WITH_AES_256_CBC_SHA256",
            "TLS_RSA_WITH_AES_256_CBC_SHA",
            "TLS_RSA_WITH_AES_128_CBC_SHA256",
            "TLS_RSA_WITH_AES_128_CBC_SHA"
        };

        String[] availableCiphers = null;

        try
        {
            SSLSocketFactory factory = m_ctx.getSocketFactory();
            availableCiphers = factory.getSupportedCipherSuites();
            Arrays.sort(availableCiphers);
        }
        catch(Exception e)
        {
            return new String[] {
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_RSA_WITH_AES_128_CBC_SHA",
                "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
            };
        }

        List<String> aa = new ArrayList<String>();
        for(int i = 0; i < preferredCiphers.length; i++)
        {
            int idx = Arrays.binarySearch(availableCiphers, preferredCiphers[i]);
            if(idx >= 0)
                aa.add(preferredCiphers[i]);
        }

        aa.add("TLS_EMPTY_RENEGOTIATION_INFO_SCSV");

        return aa.toArray(new String[0]);
    }

    private SSLContext m_ctx;

    private String[] m_ciphers;
    private String[] m_protocols;
}