5

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

它非常易于使用,只需:

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

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

90% 的时间都可以正常工作!但是在某些情况下,我们会收到一个恼人的错误:“Internal TLS error, this may be a attack”。已确认没有攻击。这是基于内部 BouncyCastle 异常处理的常见错误。我正在尝试为那些运气不佳的远程主机找到一种通用模式。

更新:

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

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)

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

扩展类型:11 扩展数据:

根据 ExtensionType 类,“ec_point_formats”。这会导致“UnexpectedServerExtension”->“UnexpectedServerExtension”导致--> TlsFatalAlert:illegal_parameter,最后这是“内部 TLS 错误,这可能是攻击”

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

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

  1. 覆盖 ClientExtensions 以包括“主机”ExtensionType。只是 ExtensionType.server_name (可能还有更多要包含的扩展名?)
  2. 创建一个 TlsAuthentication 以在 Socket 的 peerCertificate 上包含 remoteCerts 。还可以选择检查远程证书是否在默认密钥库中(cacerts 等)

我分享 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

    }

}
4

1 回答 1

3

如果您查看 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);
    }
}

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

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

于 2015-11-07T10:55:58.627 回答