TLS 1.2 + Java 1.6 + BouncyCastle

时间:2015-11-04 08:56:16

标签: java ssl bouncycastle tls1.2

为了支持使用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();

在测试期间,我将不同的Web和远程主机连接到SSLabs Tests

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上包含remoteCerts peerCertificate。也可以选择检查远程证书是否在 默认密钥库(cacerts等)。
  3. 我分享了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
    
        }
    
    }
    

1 个答案:

答案 0 :(得分:3)

如果你看一下RFC 4492 5.2,你会看到服务器CAN发送“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实现。