After my handshake is complete I call getPeerCertificates() on handshake (socket.getHandshakeSession()) I am using setNeedClientAuth(true) on the server socket so I know it must be getting the clients cert.
What could be going wrong? SSLPeerUnverifiedException is not thrown
ssf = (SSLServerSocketFactory)SSLServerSocketFactory.getDefault();
serverSocket = (SSLServerSocket)ssf.createServerSocket(PORT);
serverSocket.setNeedClientAuth(true);
//added to after accept list and picked up by a worker thread.
SSLSocket socket = (SSLSocket) serverSocket.accept();
//worker thread.
SSLSession handshake = socket.getHandshakeSession();
Certificate[] certificates = handshake.getPeerCertificates(); // NULL
When you call socket.getHandshakeSession(), you haven't started the handshake yet, and you certainly haven't reached the point where the client certificate is exchanged. Although calling getSession() is one way to start the handshake (see introduction of the SSLSocket documentation, no such thing happens with getHandshakeSession() (as the documentation says: "Unlike getSession(), this method does not initiate the initial handshake and does not block until handshaking is complete.").
In most cases, you'll want to get the client certificate once the handshake has completed anyway. Calling socket.getSession() instead of socket.getHandshakeSession() (an addition in Java 7) is likely to solve your problem.
Related
I have a database in which there are different hosts on which i want to connect to through SSL. Because the hosts sometimes are random and often invalid i get an error at the point where the socket is created. I don't know how i can handle this problem
for(int i=connect.getMinId();i<connect.getMaxId();i++){
System.out.println(connect.getHost(i));
SSLSocket socket = (SSLSocket) factory.createSocket(URL, 443);
socket.startHandshake();
SSLSession session = socket.getSession();
}
Create an unconnected Socket with new Socket().
Connect it with a timeout using Socket.connect(address, timeout). You can choose your own timeout up to the platform default. Note that the Javadoc incorrect says that 0 means an infinite timeout. It doesn't: it means the platform default, which is around a minute, dependent on the, err, platform. This method throws SocketTimeoutException if the timeout expires.
Wrap it in an SSLSocket with SSLSocketFactory.createSocket(Socket, host, port, true). Note that you have to re-specify the host and port: this is for SSL session sharing purposes; it doesn't create a new connection.
Get the session, or start the handshake. Note that getting the session starts the handshake, you don't have to do both. If you're not interested in the server certificate you don't really need to do this step at all.
Catch all IOExceptions and close the socket.
I have created a socket on port 443 as in the following line:
socket = (SSLSocket) factory.createSocket(hostName, port);
Then, I wanted to see the enabled ciphersuites in this socket, I used:
String[] enCiphersuite=socket.getEnabledCipherSuites();
System.out.println("Enabled ciphersuites are: "+Arrays.toString(enCiphersuite));
Then, I want to pick only one ciphersuite that I want my application to use when creating handshake with the remote server. I did the following:
String pickedCipher[] ={"TLS_RSA_WITH_AES_128_CBC_SHA"};
socket.setEnabledCipherSuites(pickedCipher);
System.out.println("ciphersuite set to: "+Arrays.toString(pickedCipher));
Then I made the handshake, and checked the session ciphersuite:
socket.startHandshake();
System.out.println("Session ciphersuite is"+socket.getSession().getCipherSuite() );
But I found that the name of the cipher printed in the previous printout statement after the handshake (as I understand, this is the actually used cipher in the session) is not what I set earlier using setEnabledCipherSuites()
Why am I still not see my chosen ciphersuite is the used one ? and also, I also tried to getEnabledCipherSuites() and print it out after I setEnabledCipherSuites and found the list has not changed to what I have set. I am not sure when I print the enabled ciphersuite, is this list of ciphersuites depends on Java and always the same list, or depends on the client or on the server? Can any body explain ?
EDIT:
Before the handshake I only have the following lines:
SSLSocketFactory factory = HttpsURLConnection.getDefaultSSLSocketFactory();
SSLSocket socket=null;
try {
socket = (SSLSocket) factory.createSocket(hostName, port);
socket.setSoTimeout(15000);
socket.startHandshake(); //handshake
.
.
I found out that I added socket.getsession() before the
setEnableCipherSuite() in order to print out the enabled cipheres
before setting them. When I removed it, the cipher has been set. why
is that ?
As documented in the SSLSocket JavaDoc:
The initial handshake on this connection can be initiated in one of
three ways:
calling startHandshake which explicitly begins handshakes, or
any attempt to read or write application data on this socket causes an implicit handshake, or
a call to getSession tries to set up a session if there is no currently valid session, and an implicit handshake is done.
If you call getSession() before calling setEnabledCipherSuite(), the handshake has already been done when you try to set the enabled cipher suites, so this session's cipher suite has already been selected.
I'm trying to connect an Android app to a SSL-enabled server, which uses a self-signed certificate. I've already read through dozens of tutorials and the app is now accepting the certificate & connecting to the server, but I never get any data back.
The original code i used to initialize the socket is this:
//passphrase for keystore
char[] keystorePass="password".toCharArray();
//load own keystore (MyApp just holds reference to application context)
KeyStore keyStore=KeyStore.getInstance("BKS");
keyStore.load(MyApp.getStaticApplicationContext().getResources().openRawResource(R.raw.keystore),keystorePass);
//create a factory
TrustManagerFactory trustManagerFactory=TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
//get context
SSLContext sslContext=SSLContext.getInstance("TLS");
//init context
sslContext.init(
null,
trustManagerFactory.getTrustManagers(),
new SecureRandom()
);
//create the socket
Socket socket=sslContext.getSocketFactory().createSocket("hostname",443);
socket.setKeepAlive(true);
Afterwards, the run loop of the receiver thread uses socket.getInputStream() to access the input stream.
As long as I use an unencrypted connection, this works without a problem. But the secure connection does not retrieve any data from the socket. I've verified this by adding log messages to the receive loop and even used OpenSSL's s_server to check. I retrieved data from the client, but the client never received anything I sent to it.
As a last test, I tried to open a connection to www.google.com:443 like this:
javax.net.SocketFactory fact=SSLSocketFactory.getDefault();
Socket socket=fact.createSocket(_config.getUri().getHost(), _config.getUri().getPort());
Still the same result, connection works but using the InputStream I receive nothing from the server.
Anybody got any ideas?
EDIT:
I'm currently not allowed to answer my own question, but here's the answer:
Well, turns out the problem WAS the receive loop. I relied on InputStream.available() to get the number of bytes to read, but didn't realize it was rather unreliable (always returns 0 for SSL socket). So I switched the receive loop to use the blocking read() instead.
As mentioned above: Turns out the problem WAS the receive loop. I relied on InputStream.available() to get the number of bytes to read, but didn't realize it was rather unreliable (always returns 0 for SSL socket). So I switched the receive loop to use the blocking read() instead.
How can I configure connect timeout for SSL Sockets in Java?
For plain sockets, I can simply create new socket instance without any target endpoint using new Socket(), and then call connect(SocketAddress endpoint, int timeout) method. With SSL sockets, I cannot create new SSLSocket() and SSLSocketFactory.getDefault().createSocket() method with no endpoint throws UnsupportedOperationException with Unconnected sockets not implemented message.
Is there a way to use connect timeouts for SSL Sockets in Java, using standard java libs only?
I believe you could use your current approach of creating the Socket and then connecting it. To establish SSL over the connection you could use SSLSocketFactory.createSocket
Returns a socket layered over an
existing socket connected to the named
host, at the given port.
This way you get full control over the connection and then you negociate setting up SSL on top of it. Please let me know if I misread your question.
With java 1.7 the following does not throw the exception stated in the question:
String host = "example.com";
int port = 12345;
int connectTimeout = 5000;
SSLSocket socket = (SSLSocket)SSLSocketFactory.getDefault().createSocket();
socket.connect(new InetSocketAddress(host, port), connectTimeout);
socket.startHandshake();
so it's business as usual.
Elaborating on #predi's answer, I found that I needed to use "setSoTimeout" too. Otherwise sometimes it gets stuck in the handshake (on very unstable connections):
final int connectTimeout = 30 * 1000;
SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket();
socket.setSoTimeout(connectTimeout);
socket.connect(new InetSocketAddress(hostAddress, port), connectTimeout);
socket.startHandshake();
socket.setSoTimeout(0);`
I am trying to create a client - server application, the client written in c++ and QT, and the server in java, but I am having a really hard time trying to get ssl encryption working.
The process fails at handshake level, I think. The reason why I am having such a difficult time trying to figure out why it is not working is because, even though the process fails, no errors are reported in either the client or the server. I use the fallowing an the client side, in QT :
this->_uCertificate.fromPath(_DC::DEFAULT_CERT_MAIN_PATH + _DC::DEFAULT_MAIN_CERT_FILE);
this->_socket->addCaCertificate(this->_uCertificate);
//begin connection
this->_socket->connectToHostEncrypted(this->_uServerAdress, this->_uServerPort);
//wait until connection has completed
if(!this->_socket->waitForConnected(_CM::TIMEOUT))
{
this->_lastError = this->_socket->errorString();
return false;
}
//wait for handshake
if ( !this->_socket->waitForEncrypted(_CM::TIMEOUT) ) {
this->_lastError = this->_socket->errorString(); //the error is "No Error"
//return false;
}
It fails when calling the "waitForEncrypted". The function return false, so the process failed, but the error string is "No Error". I have also added a slot for handling the error signal from the socket, but it is never called. On the server side I use :
SSLSocket _sock = (SSLSocket) this._ssocket.accept();
_sock.startHandshake();
........................................
if(this._inputBuffered.read(this._messageBuffer) < 0)
throw new Exception("Error while reading from client");
Again no exceptions are thrown, but it fails at the read command. But on the server side I am no sure if an exception is thrown if the connection / handshake fails, or I should check for the error manually somehow.
I used to have a problem , in the client, when I would receive an error that the common name doesn't match the host, so at least I know that the connection is somewhat working. After I fixed the certificate to include the right common name, I am receiving this none existing error. Does anyone have an idea why it would fail this way, or at least a better debugging method?
Edit I have tried to connect using openSSL and it works. The handshake succeeds, and I can send and receive packets from the server. So the problem seems to be in the client.
It seemed that I had a problem with the way I was loading certificates from file. The method "fromPath" apparently doesn't actually load a cert from file, but returnes a list of certificates. If I add this list to my socket, then it works as it should. I am a bit conscience-stricken that I didn't read the documentation properly.
Edit The reason why it was failing, but still no errors were thrown with signals is because my socket had no valid certificate. When I was calling this->_uCertificate.fromPath(..), the method was returning a list of certificates found at that path, but the object itself was not modified. It still remained a invalid, empty certificate. So when I added that empty certificate in my socket, the only one, when it reached the handshake, It had no valid certificate for the operation. At this point it fails, but no errors are thrown.
But when the objects returned by the .fromPath() methon are added to the socket, then the handshake continues as normal, because now it has valid and non empty cartificates.
The problem of failing to give an error when there's an empty certificate database is now Qt bug QTBUG-17550
Connect your SSL client socket's void QSslSocket::sslErrors ( const QList<QSslError> & errors ) signal to some slot and see if there are any SSL errors reported.