繁体   English   中英

TLS 1.2 + Java 1.6 + BouncyCastle

[英]TLS 1.2 + Java 1.6 + BouncyCastle

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

它非常容易使用,只需:

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

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

90%的时间这个工作正常! 但是在某些情况下我们会遇到一个恼人的错误: “内部TLS错误,这可能是一种攻击” 已经检查过没有攻击。 这是基于内部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)

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

ExtensionType:11 ExtensionData:

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

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

诀窍在于重写startHandShake以使用Bouncy的TLSClientProtocol:

  1. 覆盖ClientExtensions以包含“host”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

    }

}

如果你看一下RFC 4492 5.2,你会看到服务器CAN发送“ec_point_formats”扩展名,但是在“协商ECC密码套件时”应该这样做。 如果你希望TLSClient只是忽略额外的扩展而不是引发异常,我建议重写TlsClient.allowUnexpectedServerExtension(...)以允许ec_point_formats,就像默认实现允许的elltic_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实现上覆盖它们。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM