Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java SSL code throwing NoSuchAlgorithException

Tags:

java

ssl

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?

like image 962
MattL922 Avatar asked Jan 20 '12 19:01

MattL922


2 Answers

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);
like image 158
Bruno Avatar answered Sep 20 '22 12:09

Bruno


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.

like image 32
JB Nizet Avatar answered Sep 19 '22 12:09

JB Nizet