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?
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;
}
Related
I'm trying to create a secure connection in Java. For that I created following server and client
Server:
public static void main(String[] args){
try {
// relative keystorepath
String certificateChain = "keystore";
String password = "***";
System.setProperty("javax.net.ssl.keyStore", certificateChain);
System.setProperty("javax.net.ssl.keyStorePassword", password);
SSLServerSocketFactory sslserversocketfactory =
(SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
SSLServerSocket sslserversocket =
(SSLServerSocket) sslserversocketfactory.createServerSocket(9999);
SSLSocket sslsocket = (SSLSocket) sslserversocket.accept();
InputStream inputstream = sslsocket.getInputStream();
InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
BufferedReader bufferedreader = new BufferedReader(inputstreamreader);
String string = null;
while ((string = bufferedreader.readLine()) != null) {
System.out.println(string);
System.out.flush();
}
} catch (Exception exception) {
exception.printStackTrace();
}
}
Client:
public static void main(String[] arstring) {
try {
// Pfad zum Truststore
String certificateChain = "/usr/lib64/jvm/java-1.7.0-openjdk-1.7.0/jre/lib/security/cacerts";
String password = "***";
System.setProperty("javax.net.ssl.trustStore", certificateChain);
System.setProperty("javax.net.ssl.trustStorePassword", password);
SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket("localhost", 9999);
OutputStream outputstream = sslsocket.getOutputStream();
OutputStreamWriter outputstreamwriter = new OutputStreamWriter(outputstream);
BufferedWriter bufferedwriter = new BufferedWriter(outputstreamwriter);
String string = "testmessage";
bufferedwriter.write(string + '\n');
bufferedwriter.flush();
} catch (Exception exception) {
exception.printStackTrace();
}
}
After that I tried adding my certificate to the Truststore at the specific path.
The generated key was added to the keystore in the working directory.
If tried to follow all the tutorials and instructions on the first 5 pages of google using many different keywords, without luck. I'm always getting the handshake_failure Exception:
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.Alerts.getSSLException(Alerts.java:154)
at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1959)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1077)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1312)
at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:702)
at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:122)
at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)
at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291)
at sun.nio.cs.StreamEncoder.implFlush(StreamEncoder.java:295)
at sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:141)
at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:229)
at java.io.BufferedWriter.flush(BufferedWriter.java:254)
at client.SSLClientMain.main(SSLClientMain.java:151)
Since the code is just copy-pasted I think it's not a codeproblem, but a certificate problem. So my question is:
what am I doing wrong, what do I need to do more?
You may be using a certificate in Server side which may not be in Client side and vice versa.
Try it out with same certificate for server and client.
EDITTED: "The code below works fine, no errors, no exceptions"
I'm aware of the grand amount of questions in regards to this topic, as well as the many blogs that google conjures up. I have read through them and have managed to come up with what I'm about to explain. My doubt lies in "is my approach correct? Does it have any side-effects?" and another question that is better asked as I explain my method.
I based this approach following this Android.Developres tutorial.
System.setProperty("jsse.enableSNIExtension", "false");
//Java 7 introduced SNI (enabled by default). The server I use is
// misconfigured I suppose and
// it sends an "Unrecognized Name" warning in the SSL handshake
// which breaks my web service.
// Load CA from an InputStream (CA would be saved in Raw file,
// and loaded as a raw resource)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = new BufferedInputStream(new FileInputStream("PATH_TO_CERT.crt"));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
} finally {
caInput.close();
}
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
// Create all-trusting host name verifier
// to avoid the following :
// java.security.cert.CertificateException: No name matching
// This is because Java by default verifies that the certificate CN (Common Name) is
// the same as host name in the URL. If they are not, the web service client fails.
HostnameVerifier allHostsValid = new HostnameVerifier() {
#Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
};
//Install it
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
// Tell the URLConnection to use a SocketFactory from our SSLContext
URL url = new URL("https....");
urlConnection.setSSLSocketFactory(context.getSocketFactory());
try {
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.connect();
switch(urlConnection.getResponseCode()){
case 401:
BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getErrorStream()));
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line+"\n");
}
br.close();
System.out.println( sb.toString());
}
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
Here is my other question, in the following line:
InputStream caInput = new BufferedInputStream(new FileInputStream("PATH_TO_CERT.crt"));
You see that the method forces me to have the certificate.crt preloaded onto raw file inside res folder. Is there a way (I have looked but have found 0 answers) to connect to the server and download said certificate.crt and save it on a private folder not accessible by the user?
I want to open a secure listening socket in a Java server application. I know that the recommended way to do that is to just do this:
SSLServerSocketFactory ssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
ServerSocket ss = ssf.createServerSocket(443);
But this requires to pass the certificate of the server to the JVM when launching java. Because this would make some things in deployment more complicated for me, I would prefer to load the certificate at runtime.
So I have a key file and a password and I want a server socket. How do I get there? Well, I read the documentation and the only way I could find is this:
// these are my parameters for SSL encryption
char[] keyPassword = "P#ssw0rd!".toCharArray();
FileInputStream keyFile = new FileInputStream("ssl.key");
// init keystore
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(keyFile, keyPassword);
// init KeyManagerFactory
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, keyPassword);
// init KeyManager
KeyManager keyManagers[] = keyManagerFactory.getKeyManagers();
// init the SSL context
SSLContext sslContext = SSLContext.getDefault();
sslContext.init(keyManagers, null, new SecureRandom());
// get the socket factory
SSLServerSocketFactory socketFactory = sslContext.getServerSocketFactory();
// and finally, get the socket
ServerSocket serverSocket = socketFactory.createServerSocket(443);
And that doesn't even have any error handling. Is it really that complicated? Isn't there an easier way to do it?
But this requires to pass the certificate of the server to the JVM when launching java.
No it doesn't. Just set these system properties before you create the SSLServerSocket:
javax.net.ssl.keyStore ssl.key
javax.net.ssl.keyStorePassword P#ssw0rd!
You can do that with System.setProperties() or on the command line.
If you look at the code, you can see why it's necessarily complicated. This code decouples the implementation of the SSL protocol from:
the source of your key material (KeyStore)
certificate algorithm choice and key management (KeyManager)
management of peer trust rules (TrustManager) - not used here
secure random algorithm (SecureRandom)
NIO or socket implementation (SSLServerSocketFactory) - could use SSLEngine for NIO
Consider what your own implementation would look like if you were trying to reach the same goals!
Use this.
public class KeyMaster
{
public static SSLSocketFactory getSSLSocketFactory(KeyStore trustKey, String sslAlgorithm)
{
try
{
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustKey);
SSLContext context = SSLContext.getInstance(sslAlgorithm);//"SSL" "TLS"
context.init(null, tmf.getTrustManagers(), null);
return context.getSocketFactory();
}
catch(Exception e)
{
Assistance.log("Err: getSSLSocketFactory(), ");
}
return null;
}
public static SSLServerSocketFactory getSSLServerSocketFactory(KeyStore trustKey, String sslAlgorithm)
{
try
{
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustKey);
SSLContext context = SSLContext.getInstance(sslAlgorithm);//"SSL" "TLS"
context.init(null, tmf.getTrustManagers(), null);
return context.getServerSocketFactory();
}
catch(Exception e)
{
Assistance.log("Err: getSSLSocketFactory(), ");
}
return null;
}
public static SSLServerSocket getSSLServerSocket(SSLServerSocketFactory socketFactory, int port)
{
try
{
return (SSLServerSocket) socketFactory.createServerSocket(port);
}
catch(Exception e)
{Assistance.log("Err: getSSLSocket(), ");}
return null;
}
public static KeyStore getFromPath(String path, String algorithm, String filePassword)//PKSC12
{
try
{
File f = new File(path);
if(!f.exists())
throw new RuntimeException("Err: File not found.");
FileInputStream keyFile = new FileInputStream(f);
KeyStore keystore = KeyStore.getInstance(algorithm);
keystore.load(keyFile, filePassword.toCharArray());
keyFile.close();
return keystore;
}
catch(Exception e)
{
Assistance.log("Err: getFromPath(), " + e.toString());
}
return null;
}
and in main.
KeyStore key = KeyMaster.getFromPath(".\\cssl.pfx", "PKCS12", "123");
SSLServerSocketFactory fac = KeyMaster.getSSLServerSocketFactory(key, "TLS");
listener = KeyMaster.getSSLServerSocket(fac, 49015);
I am trying to read a video file from local storgae by chunks and upload to a server. I have this code working in another java platform so i thought it would be quite simple.
When i try to open the File using
File f = new File(filePath);
fileIn = new FileInputStream(f);
it opens and i can read whatever i need from the file, further down in my code though i call
SocketFactory socketFactory = SSLSocketFactory.getDefault();
Socket socket = socketFactory.createSocket(url, 443);
_in = new InputStreamReader(socket.getInputStream());
_out = (OutputStream)socket.getOutputStream();
The socket connects fine but when I then come to read the FileInputStream after this bit of code I get stream closed exception.
Any ideas? I don't see anything in the log to show anything is failing but i can't read from the fileinputstream once i have conencted to the server??
Let me know if you need to know anything else to help.
This example worked for me:
public class MainClass {
public static void main(String[] args) {
String host = args[0];
int port = Integer.parseInt(args[1]);
try {
System.out.println("Locating socket factory for SSL...");
SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
System.out.println("Creating secure socket to " + host + ":" + port);
SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
System.out.println("Enabling all available cipher suites...");
String[] suites = socket.getSupportedCipherSuites();
socket.setEnabledCipherSuites(suites);
System.out.println("Registering a handshake listener...");
socket.addHandshakeCompletedListener(new MyHandshakeListener());
System.out.println("Starting handshaking...");
socket.startHandshake();
System.out.println("Just connected to " + socket.getRemoteSocketAddress());
} catch (IOException e) {
e.printStackTrace();
}
}
}
class MyHandshakeListener implements HandshakeCompletedListener {
public void handshakeCompleted(HandshakeCompletedEvent e) {
System.out.println("Handshake succesful!");
System.out.println("Using cipher suite: " + e.getCipherSuite());
}
}
As snicolas said, socket.startHandshake() could fix your prob.
you didn't call socket.startHandshake();
I have 2 apps, one is a Servlet/Tomcat Server, and the other is an Android app.
I want to use HttpURLConnection to send and receive XML between both.
Code:
private String sendPostRequest(String requeststring) {
DataInputStream dis = null;
StringBuffer messagebuffer = new StringBuffer();
HttpURLConnection urlConnection = null;
try {
URL url = new URL(this.getServerURL());
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setDoOutput(true);
urlConnection.setRequestMethod("POST");
OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
out.write(requeststring.getBytes());
out.flush();
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
dis = new DataInputStream(in);
int ch;
long len = urlConnection.getContentLength();
if (len != -1) {
for (int i = 0; i < len; i++)
if ((ch = dis.read()) != -1) {
messagebuffer.append((char) ch);
}
} else {
while ((ch = dis.read()) != -1)
messagebuffer.append((char) ch);
}
dis.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
urlConnection.disconnect();
}
return messagebuffer.toString();
}
Now, I need to use SSL to send the XMLs for security.
First, I use Java Keytool to generate the .keystore file.
Keytool -keygen -alias tomcat -keyalg RSA
Then I put the XML Code on server.xml file of Tomcat to use SSL
<Connector
port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
keystoreFile="c:/Documents and Settings/MyUser/.keystore"
keystorePass="password"
clientAuth="false" sslProtocol="TLS"
/>
Then, I change it the HttpURLConnection for HttpsURLConnection
private String sendPostRequest(String requeststring) {
DataInputStream dis = null;
StringBuffer messagebuffer = new StringBuffer();
HttpURLConnection urlConnection = null;
//Conexion por HTTPS
HttpsURLConnection urlHttpsConnection = null;
try {
URL url = new URL(this.getServerURL());
//urlConnection = (HttpURLConnection) url.openConnection();
//Si necesito usar HTTPS
if (url.getProtocol().toLowerCase().equals("https")) {
trustAllHosts();
//Creo la Conexion
urlHttpsConnection = (HttpsURLConnection) url.openConnection();
//Seteo la verificacion para que NO verifique nada!!
urlHttpsConnection.setHostnameVerifier(DO_NOT_VERIFY);
//Asigno a la otra variable para usar simpre la mism
urlConnection = urlHttpsConnection;
} else {
urlConnection = (HttpURLConnection) url.openConnection();
}
//Do the same like up
and add a trustAllHosts method to Trust every server (dont check for any certificate)
private static void trustAllHosts() {
X509TrustManager easyTrustManager = new X509TrustManager() {
public void checkClientTrusted(
X509Certificate[] chain,
String authType) throws CertificateException {
// Oh, I am easy!
}
public void checkServerTrusted(
X509Certificate[] chain,
String authType) throws CertificateException {
// Oh, I am easy!
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] {easyTrustManager};
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
}
}
Those changes worked very good, but I don´t want to Trust every server. I want to use my keystore file to validate the connection and use SSL in the right way.
I read a lot on the internet and made a lot of tests, but I can´t understand what I have to do and how to do it.
Can somebody help me?
Thank you very much
Sorry for my poor english
-------------------------UPDATE 2011/08/24-------------------------------------------------
Well, I'm still working on this. I made a new method to set the KeyStore, InputStream, etc
The method looks like this:
private static void trustIFNetServer() {
try {
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance("BKS");
InputStream in = context.getResources().openRawResource(R.raw.mykeystore);
String keyPassword = "password";
ks.load(in, keyPassword.toCharArray());
in.close();
tmf.init(ks);
TrustManager[] tms = tmf.getTrustManagers();
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, tms, new java.security.SecureRandom());
} catch (Exception e) {
e.printStackTrace();
}
}
First I had a lot of problems with the Key and the Certificate, but now it is working (I think so)
My problem right now is a TimeOut Exception. I don´t know why it is generated. I'm think it's something with the data write, but I can't solve yet.
Any Idea?
You need to create a trust store file for your self-signed certificate as described here.
Use it on the client side to connect with your server. It doesn't really matter if you use JKS or another format, I'll assume JKS for now.
To accomplish what you have in mind you need a different TrustManager, obviously. You can use TrustManagerFactory and feed its trust settings with your newly created trust store.
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
KeyStore ks = KeyStore.getInstance("JKS");
FileInputStream in = new FileInputStream("<path to your key store>");
ks.load(in, "password".toCharArray());
in.close();
tmf.init(ks);
TrustManager[] tms = tmf.getTrustManagers();
Use tms to init your SSLContextinstead for the new trust settings to be used for your SSL/TLS connection.
Also you should make sure that the CN part of the server TLS certificate is equal to the FQDN (fully qualified domain name) of your server, e.g. if your server base URL is 'https://www.example.com', then the CN of the certificate should be 'www.example.com'. This is needed for host name verification, a feature that prevents man-in-the-middle-attacks. You could disable this, but only when using this your connection will be really secure.
Create your trust store, store at as an asset and use it initialize this SocketFactory. Then use the factory instead of your own 'trust everybody' one.
If you want to ignore all the certificate, ignore the handshake, then this works:
HttpsURLConnection and intermittent connections