TLS 1.2 + Java 1.6 + BouncyCastle

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33517476/
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-11-02 21:48:15  来源:igfitidea点击:

TLS 1.2 + Java 1.6 + BouncyCastle

javasslbouncycastletls1.2

提问by Azimuts

For supporting HTTPS connections through a Java 1.6 API to remote hosts using TLS 1.2, we have developed a customized TLS SocketConnection factory based on Bouncy Castle Libraries (v. 1.53)

为了支持通过 Java 1.6 API 到使用 TLS 1.2 的远程主机的 HTTPS 连接,我们开发了一个基于 Bouncy Castle Libraries (v. 1.53) 的定制 TLS SocketConnection 工厂

It's very easy to use, just:

它非常易于使用,只需:

        String httpsURL =  xxxxxxxxxx
        URL myurl = new URL(httpsURL);      
        HttpsURLConnection  con = (HttpsURLConnection )myurl.openConnection();
        con.setSSLSocketFactory(new TSLSocketConnectionFactory());   
        InputStream ins = con.getInputStream();

During testing, I connect different web and remote hosts exposed into SSLabs Tests

在测试期间,我连接了暴露在SSLabs 测试中的不同 Web 和远程主机

90% of the time this works fine! But there are some cases in which we get an annoying error: "Internal TLS error, this could be an attack". It has been checked that there is no attack. That's a common error based on the treatment of internal BouncyCastle exceptions. I'm trying to find a common pattern to those remote host that fails with little luck.

这在 90% 的情况下都能正常工作!但在某些情况下,我们会收到令人讨厌的错误:“内部 TLS 错误,这可能是一次攻击”。已经检查没有攻击。这是基于内部 BouncyCastle 异常处理的常见错误。我正在尝试为那些运气不佳的远程主机找到一个通用模式。

Updated:

更新:

Updating some code for extra information, we get this:

更新一些代码以获取额外信息,我们得到:

org.bouncycastle.crypto.tls.TlsFatalAlert: illegal_parameter(47)
    at org.bouncycastle.crypto.tls.AbstractTlsClient.checkForUnexpectedServerExtension(AbstractTlsClient.java:56)
    at org.bouncycastle.crypto.tls.AbstractTlsClient.processServerExtensions(AbstractTlsClient.java:207)
    at org.bouncycastle.crypto.tls.TlsClientProtocol.receiveServerHelloMessage(TlsClientProtocol.java:773)

The extension type i get is this:

我得到的扩展类型是这样的:

ExtensionType:11 ExtensionData:

扩展类型:11 扩展数据:

Acording to ExtensionType class, "ec_point_formats". This causes "UnexpectedServerExtension" --> The "UnexpectedServerExtension" causes a --> TlsFatalAlert: illegal_parameter , and at last this a "Internal TLS error, this could be an attack"

根据 ExtensionType 类,“ec_point_formats”。这会导致“UnexpectedServerExtension”-->“UnexpectedServerExtension”导致--> TlsFatalAlert:非法参数,最后这是一个“内部TLS错误,这可能是一次攻击”

Any advise to log or trace this strange TLS Errors....???? As i say, this code works 90%...but with some remote host i get this errof

任何建议记录或跟踪这个奇怪的 TLS 错误....???? 正如我所说,这段代码可以工作 90%...但是对于一些远程主机,我得到了这个错误

The trick consists in overriding startHandShake to use Bouncy's TLSClientProtocol:

诀窍在于覆盖 startHandShake 以使用 Bouncy 的 TLSClientProtocol:

  1. Override ClientExtensions to include "host" ExtensionType. Just ExtensionType.server_name ( maybe any more Extension to include?)
  2. Create a TlsAuthentication to include remoteCerts on Socket's peerCertificate .Also optionally check if remote certs are in default keystore (cacerts,etc..)
  1. 覆盖 ClientExtensions 以包含“主机”扩展类型。只是 ExtensionType.server_name (也许还有更多的扩展要包括?)
  2. 创建一个 TlsAuthentication 以在 Socket 的 peerCertificate 上包含 remoteCerts。还可以选择检查远程证书是否在默认密钥库(cacerts 等)中

I share the code of TLSSocketConnectionFactory:

我分享 TLSSocketConnectionFactory 的代码:

public class TLSSocketConnectionFactory extends SSLSocketFactory {  

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Adding Custom BouncyCastleProvider
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

    static {
        if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
            Security.addProvider(new BouncyCastleProvider());
        }
    }   

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//SECURE RANDOM
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

    private SecureRandom _secureRandom = new SecureRandom();

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Adding Custom BouncyCastleProvider
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

    @Override
    public Socket createSocket(Socket socket, final String host, int port, boolean arg3)
            throws IOException {
        if (socket == null) {
            socket = new Socket();
        }
        if (!socket.isConnected()) {
            socket.connect(new InetSocketAddress(host, port));
        }

        final TlsClientProtocol tlsClientProtocol = new TlsClientProtocol(socket.getInputStream(), socket.getOutputStream(), _secureRandom);       

        return _createSSLSocket(host, tlsClientProtocol);

    }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SOCKET FACTORY  METHODS  
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////  

    @Override
    public String[] getDefaultCipherSuites() {
        return null;
    }

    @Override
    public String[] getSupportedCipherSuites(){
        return null;
    }

    @Override
    public Socket createSocket( String host,
                                int port) throws IOException,UnknownHostException{
        return null;
    }

    @Override
    public Socket createSocket( InetAddress host,
                                int port) throws IOException {
        return null;
    }

    @Override
    public Socket createSocket( String host, 
                                int port, 
                                InetAddress localHost,
                                int localPort) throws IOException, UnknownHostException {
        return null;
    }

    @Override
    public Socket createSocket( InetAddress address,
                                int port,
                                InetAddress localAddress,
                                int localPort) throws IOException{
        return null;
    }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//SOCKET CREATION
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    private SSLSocket _createSSLSocket(final String host , final TlsClientProtocol tlsClientProtocol) {
        return new SSLSocket() {
            private java.security.cert.Certificate[] peertCerts;

            @Override
            public InputStream getInputStream() throws IOException {
                return tlsClientProtocol.getInputStream();
            }

            @Override
            public OutputStream getOutputStream() throws IOException {
                return tlsClientProtocol.getOutputStream();
            }

            @Override
            public synchronized void close() throws IOException {
                Log.to("util").info("\\n::::::Close Socket");
                tlsClientProtocol.close();
            }

            @Override
            public void addHandshakeCompletedListener(HandshakeCompletedListener arg0) {

            }

            @Override
            public boolean getEnableSessionCreation() {
                return false;
            }

            @Override
            public String[] getEnabledCipherSuites() {
                return null;
            }

            @Override
            public String[] getEnabledProtocols() {
                return null;
            }

            @Override
            public boolean getNeedClientAuth(){
                return false;
            }

            @Override
            public SSLSession getSession() {
                return new SSLSession() {

                    @Override
                    public int getApplicationBufferSize() {
                        return 0;
                    }

                    @Override
                    public String getCipherSuite() {
                        throw new UnsupportedOperationException();
                    }

                    @Override
                    public long getCreationTime() {
                        throw new UnsupportedOperationException();
                    }

                    @Override
                    public byte[] getId() {
                        throw new UnsupportedOperationException();
                    }

                    @Override
                    public long getLastAccessedTime() {
                        throw new UnsupportedOperationException();
                    }

                    @Override
                    public java.security.cert.Certificate[] getLocalCertificates() {
                        throw new UnsupportedOperationException();
                    }

                    @Override
                    public Principal getLocalPrincipal() {
                        throw new UnsupportedOperationException();
                    }

                    @Override
                    public int getPacketBufferSize() {
                        throw new UnsupportedOperationException();
                    }

                    @Override
                    public X509Certificate[] getPeerCertificateChain()
                            throws SSLPeerUnverifiedException {
                        return null;
                    }

                    @Override
                    public java.security.cert.Certificate[] getPeerCertificates()throws SSLPeerUnverifiedException {
                        return peertCerts;
                    }

                    @Override
                    public String getPeerHost() {
                        throw new UnsupportedOperationException();
                    }

                    @Override
                    public int getPeerPort() {
                        return 0;
                    }

                    @Override
                    public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
                        return null;
                        //throw new UnsupportedOperationException();
                    }

                    @Override
                    public String getProtocol() {
                        throw new UnsupportedOperationException();
                    }

                    @Override
                    public SSLSessionContext getSessionContext() {
                        throw new UnsupportedOperationException();
                    }

                    @Override
                    public Object getValue(String arg0) {
                        throw new UnsupportedOperationException();
                    }

                    @Override
                    public String[] getValueNames() {
                        throw new UnsupportedOperationException();
                    }

                    @Override
                    public void invalidate() {
                        throw new UnsupportedOperationException();
                    }

                    @Override
                    public boolean isValid() {
                        throw new UnsupportedOperationException();
                    }

                    @Override
                    public void putValue(String arg0, Object arg1) {
                        throw new UnsupportedOperationException();
                    }

                    @Override
                    public void removeValue(String arg0) {
                        throw new UnsupportedOperationException();
                    }

                };
            }


            @Override
            public String[] getSupportedProtocols() {
                return null;
            }

            @Override
            public boolean getUseClientMode() {
                return false;
            }

            @Override
            public boolean getWantClientAuth() {
                return false;
            }

            @Override
            public void removeHandshakeCompletedListener(HandshakeCompletedListener arg0) {

            }

            @Override
            public void setEnableSessionCreation(boolean arg0) {

            }

            @Override
            public void setEnabledCipherSuites(String[] arg0) {

            }

            @Override
            public void setEnabledProtocols(String[] arg0) {

            }

            @Override
            public void setNeedClientAuth(boolean arg0) {

            }

            @Override
            public void setUseClientMode(boolean arg0) {

            }

            @Override
            public void setWantClientAuth(boolean arg0) {

            }

            @Override
            public String[] getSupportedCipherSuites() {
                return null;
            }
            @Override
            public void startHandshake() throws IOException {

                Log.to("util").info("TSLSocketConnectionFactory:startHandshake()");
                tlsClientProtocol.connect(new DefaultTlsClient() {
                    @SuppressWarnings("unchecked")
                    @Override
                    public Hashtable<Integer, byte[]> getClientExtensions() throws IOException {
                        Hashtable<Integer, byte[]> clientExtensions = super.getClientExtensions();
                        if (clientExtensions == null) {
                            clientExtensions = new Hashtable<Integer, byte[]>();
                        }

                        //Add host_name
                        byte[] host_name = host.getBytes();

                        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
                        final DataOutputStream dos = new DataOutputStream(baos);
                        dos.writeShort(host_name.length + 3);
                        dos.writeByte(0); // 
                        dos.writeShort(host_name.length);
                        dos.write(host_name);
                        dos.close();
                        clientExtensions.put(ExtensionType.server_name, baos.toByteArray());
                        return clientExtensions;
                    }

                    @Override
                    public TlsAuthentication getAuthentication()
                            throws IOException {
                        return new TlsAuthentication() {

                            @Override
                            public void notifyServerCertificate(Certificate serverCertificate) throws IOException {

                                try {
                                    KeyStore ks = _loadKeyStore();
                                    Log.to("util").info(">>>>>>>> KeyStore : "+ks.size());

                                    CertificateFactory cf = CertificateFactory.getInstance("X.509");
                                    List<java.security.cert.Certificate> certs = new LinkedList<java.security.cert.Certificate>();
                                    boolean trustedCertificate = false;
                                    for ( org.bouncycastle.asn1.x509.Certificate c : serverCertificate.getCertificateList()) {
                                        java.security.cert.Certificate cert = cf.generateCertificate(new ByteArrayInputStream(c.getEncoded()));
                                        certs.add(cert);

                                        String alias = ks.getCertificateAlias(cert);
                                        if(alias != null) {
                                            Log.to("util").info(">>> Trusted cert\n" + c.getSubject().toString());
                                            if (cert instanceof java.security.cert.X509Certificate) {
                                                try {
                                                    ( (java.security.cert.X509Certificate) cert).checkValidity();
                                                    trustedCertificate = true;
                                                    Log.to("util").info("Certificate is active for current date\n"+cert);
                                                } catch(CertificateExpiredException cee) {
                                                    R01FLog.to("r01f.util").info("Certificate is expired...");
                                                }
                                            }
                                        } else {
                                        Log.to("util").info(">>> Unknown cert " + c.getSubject().toString());
                                            Log.to("util").fine(""+cert);
                                        }

                                    }
                                    if (!trustedCertificate) {
                                        throw new CertificateException("Unknown cert " + serverCertificate);
                                    }
                                    peertCerts = certs.toArray(new java.security.cert.Certificate[0]);
                                } catch (Exception ex) {
                                    ex.printStackTrace();
                                    throw new IOException(ex);
                                }

                            }

                            @Override
                            public TlsCredentials getClientCredentials(CertificateRequest arg0)
                                    throws IOException {
                                return null;
                            }

                            /**
                             * Private method to load keyStore with system or default properties.
                             * @return
                             * @throws Exception
                             */
                            private KeyStore _loadKeyStore() throws Exception {
                                FileInputStream trustStoreFis = null;
                                try {
                                    String sysTrustStore = null;
                                    File trustStoreFile = null;

                                    KeyStore localKeyStore = null;

                                    sysTrustStore = System.getProperty("javax.net.ssl.trustStore");
                                    String javaHome;
                                    if (!"NONE".equals(sysTrustStore)) {
                                        if (sysTrustStore != null) {
                                            trustStoreFile = new File(sysTrustStore);
                                            trustStoreFis = _getFileInputStream(trustStoreFile);
                                        } else {
                                            javaHome = System.getProperty("java.home");
                                            trustStoreFile = new File(javaHome + File.separator + "lib" + File.separator + "security" + File.separator + "jssecacerts");

                                            if ((trustStoreFis = _getFileInputStream(trustStoreFile)) == null) {
                                                trustStoreFile = new File(javaHome + File.separator + "lib" + File.separator + "security" + File.separator + "cacerts");
                                                trustStoreFis = _getFileInputStream(trustStoreFile);
                                            }
                                        }

                                        if (trustStoreFis != null) {
                                            sysTrustStore = trustStoreFile.getPath();
                                        } else {
                                            sysTrustStore = "No File Available, using empty keystore.";
                                        }
                                    }

                                    String trustStoreType = System.getProperty("javax.net.ssl.trustStoreType")!=null?System.getProperty("javax.net.ssl.trustStoreType"):KeyStore.getDefaultType();
                                    String trustStoreProvider = System.getProperty("javax.net.ssl.trustStoreProvider")!=null?System.getProperty("javax.net.ssl.trustStoreProvider"):"";

                                    if (trustStoreType.length() != 0) {
                                        if (trustStoreProvider.length() == 0) {
                                            localKeyStore = KeyStore.getInstance(trustStoreType);
                                        } else {
                                            localKeyStore = KeyStore.getInstance(trustStoreType, trustStoreProvider);
                                        }

                                        char[] keyStorePass = null;
                                        String str5 = System.getProperty("javax.net.ssl.trustStorePassword")!=null?System.getProperty("javax.net.ssl.trustStorePassword"):"";

                                        if (str5.length() != 0) {
                                            keyStorePass = str5.toCharArray();
                                        }

                                        localKeyStore.load(trustStoreFis, (char[]) keyStorePass);

                                        if (keyStorePass != null) {
                                            for (int i = 0; i < keyStorePass.length; i++) {
                                                keyStorePass[i] = 0;
                                            }
                                        }
                                    }
                                    return (KeyStore)localKeyStore;
                                } finally {
                                    if (trustStoreFis != null) {
                                        trustStoreFis.close();
                                    }
                                }
                            }

                            private FileInputStream _getFileInputStream(File paramFile) throws Exception {
                                if (paramFile.exists()) {
                                    return new FileInputStream(paramFile);
                                }
                                return null;
                            }

                        };

                    }

                });

            }

        };//Socket

    }

}

回答by Peter Dettman

If you look at RFC 4492 5.2, you'll see that the server CAN send the "ec_point_formats" extension, but is only supposed to do so "when negotiating an ECC cipher suite". If you want TLSClient to just ignore the extra extension instead of raising an exception, I suggest overriding TlsClient.allowUnexpectedServerExtension(...) to allow ec_point_formats in the same way the default implementation allows elliptic_curves:

如果您查看 RFC 4492 5.2,您会看到服务器可以发送“ec_point_formats”扩展名,但仅在“协商 ECC 密码套件时”才应该这样做。如果您希望 TLSClient 只是忽略额外的扩展而不是引发异常,我建议覆盖 TlsClient.allowUnexpectedServerExtension(...) 以允许 ec_point_formats 以与默认实现允许 elliptic_curves 相同的方式:

protected boolean allowUnexpectedServerExtension(Integer extensionType, byte[] extensionData)
    throws IOException
{
    switch (extensionType.intValue())
    {
    case ExtensionType.ec_point_formats:
        /*
         * Exception added based on field reports that some servers send Supported
         * Point Format Extension even when not negotiating an ECC cipher suite.
         * If present, we still require that it is a valid ECPointFormatList.
         */
        TlsECCUtils.readSupportedPointFormatsExtension(extensionData);
        return true;
    default:
        return super.allowUnexpectedServerExtension(extensionType, extensionData);
    }
}

If this is a widespread problem, we might consider adding this case to the default implementation.

如果这是一个普遍存在的问题,我们可能会考虑将此案例添加到默认实现中。

For logging, there are the (TLSPeer) methods notifyAlertRaised and notifyAlertReceived that you can override on your TLSClient implementation.

对于日志记录,有 (TLSPeer) 方法 notifyAlertRaised 和 notifyAlertReceived 您可以在 TLSClient 实现上覆盖它们。