Search code examples
javaexceptionssl-certificatermijsse

Connect to twitter after use SSLSocketFactory


I'm trying to retrieve tweets from the Twitter streaming API using Twitter4J. The project connects to a remote server using an SSLSocket to retrieve some data and, after this, the Twitter4J is called. The problem is that if I establish this connection the Twitter4J arise this exception:

[Wed Feb 20 11:32:02 CET 2013]sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

This doesn't happen if I don't make the connection and I clean the keystore defined with the next lines:

System.clearProperty("javax.net.ssl.trustStore");
System.clearProperty("javax.net.ssl.trustStorePassword");

The code to connect is this one:

private String publishFetcher() throws UnknownHostException, IOException {
    // Set trustStore.
    System.setProperty("javax.net.ssl.trustStore", properties.getProperty("trustedStore"));
    System.setProperty("javax.net.ssl.trustStorePassword", properties.getProperty("trustedStorePassword"));

    String host = properties.getProperty("host");
    int hostPort = Integer.parseInt(properties.getProperty("host_port"));
    SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
    SSLSocket socket = (SSLSocket) factory.createSocket(host, hostPort);

    // Get input and output streams from socket connection.
    BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    PrintWriter output = new PrintWriter(socket.getOutputStream());

    // Request connection token to controller
    String connectionToken = getConnectionToken(input, output);

    // Publish fetcher
    final int rmiPort = Integer.parseInt(properties.getProperty("rmi_port"));
    TwitterFetcher fetcher = new TwitterFetcher(connector);
    Fetcher stub = (Fetcher) UnicastRemoteObject.exportObject(fetcher, 0);
    Registry registry = LocateRegistry.createRegistry(rmiPort);
    registry.rebind(connectionToken, stub);

    // Send RMI port
    output.write(String.valueOf(rmiPort) + "\n");
    output.flush();

    input.close();
    output.close();

    // Clean trusteStore properties.
    System.clearProperty("javax.net.ssl.trustStore");
    System.clearProperty("javax.net.ssl.trustStorePassword");

    return connectionToken;
}

I think that the problem is related with SSLSocketFactory because I tested somethings in a different project. For example, this code works like a charm:

SSLSocketFactory deffactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
System.setProperty("javax.net.ssl.trustStore", "sttv_keystore");
System.setProperty("javax.net.ssl.trustStorePassword", "Smart.Table");

SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();

factory = deffactory;
// Twitter4J code...

But this code doesn't work:

System.setProperty("javax.net.ssl.trustStore", "sttv_keystore");
System.setProperty("javax.net.ssl.trustStorePassword", "Smart.Table");

SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
// Twitter4J code...

I can't do the same in my real project because breaks... almost everything ^^

What could be the problem? and the solution?


Solution

  • The problem with the code is that I was replacing the trust store instead of creating a new one for my app. The code that solves the problems is the next one:

    private SSLSocket getSSLSocket() throws TrackerSSLConnectionException {
        try {
            // Load properties.
            String keystore = properties.getProperty("keystore");
            String passphrase = properties.getProperty("keystorePassphrase");
            String host = properties.getProperty("host");
            int hostPort = Integer.parseInt(properties.getProperty("host_port"));
    
            // Create keystore.
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            keyStore.load(new FileInputStream(keystore), passphrase.toCharArray());
    
            // Get factory for the given keystore.
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init(keyStore);
            SSLContext ctx = SSLContext.getInstance("SSL");
            ctx.init(null, tmf.getTrustManagers(), null);
            SSLSocketFactory factory = ctx.getSocketFactory();
    
            return (SSLSocket) factory.createSocket(host, hostPort);
        } catch (Exception e) {
            throw new TrackerSSLConnectionException(e.getMessage(), e.getCause());
        }
    }
    
    private String publishFetcher() throws TrackerSSLConnectionException, IOException {
        // Get socket connection.
        SSLSocket socket = getSSLSocket();
    
        // Get input and output streams from socket.
        BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        PrintWriter output = new PrintWriter(socket.getOutputStream());
    
        // Request connection token to controller.
        String connectionToken = getConnectionToken(input, output);
    
        // Publish fetcher.
        final int rmiPort = Integer.parseInt(properties.getProperty("rmi_port"));
        TwitterFetcher fetcher = new TwitterFetcher(connector);
        Fetcher stub = (Fetcher) UnicastRemoteObject.exportObject(fetcher, 0);
        Registry registry = LocateRegistry.createRegistry(rmiPort);
        registry.rebind(connectionToken, stub);
    
        // Send RMI port.
        output.write(String.valueOf(rmiPort) + "\n");
        output.flush();
    
        input.close();
        output.close();
    
        return connectionToken;
    }