7

I'm working on a project that I want to add SSL to, so I created a simple client/server test implementation to see if it worked and I get a NoSuchAlgorithmException. The following is my server code which is throwing the exception:

import java.io.*;
import java.net.*;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

import javax.net.ssl.*;

public class SslServer
{
    private static final int PORT = 5555;

    public static void main(String[] args)
    {
        SecureRandom sr = new SecureRandom();
        sr.nextInt();

        try {
            //client.public is the keystore file that holds the client's public key (created with keytool)
            KeyStore clientKeyStore = KeyStore.getInstance("JKS");
            clientKeyStore.load(new FileInputStream("client.public"), "clientpublicpw".toCharArray());

            //server.private is the key pair for the server (created with keytool)
            KeyStore serverKeyStore = KeyStore.getInstance("JKS");
            clientKeyStore.load(new FileInputStream("server.private"), "serverprivatepw".toCharArray());

            TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
            tmf.init(clientKeyStore);

            //This next line is where the exception occurs
            KeyManagerFactory kmf = KeyManagerFactory.getInstance("TLS");
            kmf.init(serverKeyStore, "serverprivatepw".toCharArray());

            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), sr);

            SSLServerSocketFactory sf = sslContext.getServerSocketFactory();
            SSLServerSocket ss = (SSLServerSocket)sf.createServerSocket(SslServer.PORT);
            ss.setNeedClientAuth(true);

            BufferedReader in = new BufferedReader(new InputStreamReader(ss.accept().getInputStream()));

            String line = null;
            while((line = in.readLine()) != null)
            {
                System.out.println(line);
            }
            in.close();
            ss.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (UnrecoverableKeyException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
    }

}

The stacktrace I get is:

java.security.NoSuchAlgorithmException: TLS KeyManagerFactory not available
    at sun.security.jca.GetInstance.getInstance(Unknown Source)
    at javax.net.ssl.KeyManagerFactory.getInstance(Unknown Source)
    at SslServer.main(SslServer.java:32)

I tried replacing "TLS" with "SSL" and I still got the same exception. That Didn't make sense to me. How can TLS and SSL not be supported? This is my first time trying to implement SSL and it seems difficult to find good resources about this with code examples that are well explained. Can anyone tell me why I am getting this exception or point out something wrong with my code?

MattL922
  • 621
  • 2
  • 7
  • 12

3 Answers3

16

There are a number of problems:

  • It's called TLS (Transport Layer Security), not TSL (for the SSLContext).
  • I'd suggest using the default here: TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) (The default will be PKIX on the Oracle JRE`)
  • (EDIT:) The default KeyManagerFactory is SunX509 (TLS doesn't exist here). Again, use getDefaultAlgorithm().
  • You should close your FileInputStream once you've read them.
  • It's not clear why you have both a client and a server keystore at the same place. These should be two programs: one for the client and the server (and setNeedClientAuth(true) is only useful on the server side). It would be clearer to call it something else than "client store" if it's effectively your keystore. (In addition, since you seem to be learning how to make this work, I'd suggest trying without client-certificate authentication first, in which case, the server won't need a truststore: use null as a second parameter of SSLContext.init(...) to use the default value.)
  • DO NOT give the server keystore to the client. Only export its certificate into a new keystore which you will use as a trust store. Each entity (client and server) should keep their own private keys private.
  • It's not so much the public key (only) of the remote party you want in your trust-store: it's going to be its certificate. Make sure you haven't only imported its public key, but the entire certificate.
  • For clarify, keep the appropriate extensions to your files: use .jks for your JKS keystore, this will save you headaches later.
  • You can use null for the SecureRandom in SSLContext.init(...): this will use the default value according to the security provider.

Something like this should work better:

KeyStore trustStore = KeyStore.getInstance("JKS");
InputStream tsis = new FileInputStream("trustedcerts.jks");
trustStore.load(tsis, "clientpublicpw".toCharArray());
tsis.close();

KeyStore serverKeyStore = KeyStore.getInstance("JKS");
InputStream ksis = new FileInputStream("server.jks");
clientKeyStore.load(ksis.close(), "serverprivatepw".toCharArray());
ksis.close();

TrustManagerFactory tmf = 
    TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);

KeyManagerFactory kmf = 
    KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(serverKeyStore, "serverprivatepw".toCharArray());

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

SSLServerSocketFactory sf = sslContext.getServerSocketFactory();
SSLServerSocket ss = (SSLServerSocket)sf.createServerSocket(SslServer.PORT);
ss.setNeedClientAuth(true);
Bruno
  • 119,590
  • 31
  • 270
  • 376
  • k i edited my post to add that change. I'm still getting the same exception even when I use "TLS" – MattL922 Jan 20 '12 at 20:05
  • If you're really worried about passwords, you should use a `char[]` directly or as password callback, and never hard-code your password anyway in a real application (see http://stackoverflow.com/a/8889285/372643). – Bruno Jan 20 '12 at 20:25
  • As a side note, waiting for `readLine` to return `null` is not a good way to know when to stop reading (from any sort of socket), see discussion here: http://stackoverflow.com/a/6425509/372643 – Bruno Jan 20 '12 at 20:29
  • 1
    I changed my code to use TrustManagerFactory.getDefaultAlgorithm() and KeyManagerFactory.getDefaultAlgorithm() and that got it working. The reason I have both keystores here is because the server has: server.private and client.public, while th client will have client.private and server.public. BTW, I used this site as a reference for my code, not sure how reliable it is but its the best I could find: www.ibm.com/developerworks/java/tutorials/j-jsse/index.html – MattL922 Jan 20 '12 at 20:34
  • It did say `SunX509` for the KMF, not `TLS`. It's also using a rather old version of Java, the new default of the TMF is `PKIX`. (I'm still not convinced about the keystore file naming convention in this article.) – Bruno Jan 20 '12 at 21:00
  • Instead of using .private and .public should I just make them .jks? Also, in reference to the article on properly closing sslsocket, how would I detect the close_notify on the server when the client calls `socket.close()`? – MattL922 Jan 20 '12 at 21:46
  • About the files, you might as well just add `.jks`, yes. About closing the sockets, that's a more general networking problem: you can't generally distinguish silent and abnormally closed sockets. That's why protocols use an indicator to say how much data they're going to send (and you'd generally need to set some timeout value): for example, HTTP has `Content-Length` and chunked transfer encoding to tell the recipient when the expected message is finished. Other protocols have other delimiters. – Bruno Jan 20 '12 at 21:52
  • Thats the problem with my program. Multiple clients will connect to the server, they will send numerous messages back and forth, until the client disconnects. I guess you can think of it kind of like a multiuser chat room. So since I don't know before hand how much data or how many messages will be sent, I just check for the end of the BufferedReader. Would using that method still be considered bad practice or is it acceptable? – MattL922 Jan 20 '12 at 22:02
  • Don't check for the end of the reader. It might work most of the time so you won't see the problem, but study other protocols. HTTP, POP, IMAP, SMTP all have some form of delimiters to indicate to the remote party when they're done (and waiting for another message). Whenever you send a new message in your chat (which may or may not be represented by a "message" at your application protocol level), use some indicator to say when it's going to end. – Bruno Jan 20 '12 at 22:30
3

See http://docs.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html#SupportClasses for examples, and for the names of the supported algorithm. It seems that "SunX509" and "NewSunX509" are the algorithms supported by KeyManagerFactory. And the protocol is named TLS, not TSL.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • Again, this was a mistype and was fixed. I still get the exception with "TLS" – MattL922 Jan 20 '12 at 20:27
  • Have you read my answer? *It seems that "SunX509" and "NewSunX509" are the algorithms supported by KeyManagerFactory*. – JB Nizet Jan 20 '12 at 20:28
  • +1'ed (as I had made a typo in my initial answer `X509` -> `SunX509`). @MattL922: `getDefaultAlgorithm()` is often the safest bet. – Bruno Jan 20 '12 at 20:31
1

The correct SSLContext name is "TLS". A list of standard algorithm names can be found here.

Joachim Isaksson
  • 176,943
  • 25
  • 281
  • 294