2

I'm using Java to develop a simple SSL server/client. Please ignore trustStore which is unused. When the server set serverSock.setNeedClientAuth(false), it works fine. However, when serverSock.setNeedClientAuth(true) is set, an error prompts that

*** ServerHello, TLSv1
....
***


   *** ECDH ServerKeyExchange
    Server key: Sun EC public key, 256 bits
      public x coord: 61670393751189389356366022463080915345182339021857366784148461923453434926203
      public y coord: 11927389709535675731950695034443898307097761611191306989959806723983291216258
      parameters: secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)
    **main, handling exception: java.lang.NullPointerException
    %% Invalidated:  [Session-1, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA]**
    main, SEND TLSv1 ALERT:  fatal, description = internal_error
    main, WRITE: TLSv1 Alert, length = 2
    main, called closeSocket()
Exception in thread "main" javax.net.ssl.SSLException: java.lang.NullPointerException
    at sun.security.ssl.Alerts.getSSLException(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.handleException(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.handleException(Unknown Source)
    at sun.security.ssl.AppInputStream.read(Unknown Source)
    at java.io.InputStream.read(Unknown Source)
    at cn.secure.CAServer.start(SecureServer.java:100)
    at cn.secure.SecureServer.main(SecureServer.java:33)
Caused by: java.lang.NullPointerException
    at sun.security.ssl.HandshakeMessage$CertificateRequest.<init>(Unknown Source)
    at sun.security.ssl.ServerHandshaker.clientHello(Unknown Source)
    at sun.security.ssl.ServerHandshaker.processMessage(Unknown Source)
    at sun.security.ssl.Handshaker.processLoop(Unknown Source)
    at sun.security.ssl.Handshaker.process_record(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.readDataRecord(Unknown Source)
    ... 4 more

It seems ServerHello is not done.

Following is my code. Please let me know how to solve it.

// Server code
class CAServer
{
    private SSLContext ctx;
    private KeyManagerFactory kmf;
    private TrustManagerFactory tmf;
    private SSLServerSocket serverSock;

    public void init() throws NoSuchAlgorithmException, KeyStoreException, CertificateException, FileNotFoundException, IOException, UnrecoverableKeyException, KeyManagementException
    {
        ctx=SSLContext.getInstance("TLS");
        kmf=KeyManagerFactory.getInstance("SunX509");
        tmf=TrustManagerFactory.getInstance("SunX509");
        char[] pwd="111".toCharArray();

        KeyStore ks=KeyStore.getInstance("JKS");
        KeyStore ts=KeyStore.getInstance("JKS");

        ks.load(new FileInputStream("C:/Users/Jim/ca.keystore"), pwd);        
        ts.load(new FileInputStream("C:/Users/Jim/ca.keystore"), pwd); // unused    

        kmf.init(ks,pwd);
        tmf.init(ts);

        TrustManager[] trustClientCerts = new TrustManager[] { new X509TrustManager() {

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }

            @Override
            public void checkClientTrusted(X509Certificate[] certs,String authType) {

            }

            @Override
            public void checkServerTrusted(X509Certificate[] certs,String authType) {
            }
        } 
        };


        ctx.init(kmf.getKeyManagers(),trustClientCerts, null);

        //init server
        serverSock=(SSLServerSocket)ctx.getServerSocketFactory().createServerSocket(13000);

        serverSock.setNeedClientAuth(true);
    }

    public void start() throws IOException
    {
        System.out.println("My Secure server start");
        while(true)
        {
            Socket s=serverSock.accept();

            InputStream input=s.getInputStream();   

            byte[] c=new byte[256];
            input.read(c);           **// error(NullPointer) occurs here** 
            System.out.println(new String(c));
        }
    }

}


// Client code
class MyClient
{
    private SSLContext ctx;
    KeyManagerFactory kmf;
    TrustManagerFactory tmf;
    private SSLSocket clientSock;

    public void init() throws NoSuchAlgorithmException, KeyStoreException, CertificateException, FileNotFoundException, IOException, KeyManagementException, UnrecoverableKeyException
    {
        ctx=SSLContext.getInstance("TLS");
        kmf=KeyManagerFactory.getInstance("SunX509");
        tmf=TrustManagerFactory.getInstance("SunX509");

        char[] pwd="111".toCharArray();

        KeyStore ks=KeyStore.getInstance("JKS");
        KeyStore ts=KeyStore.getInstance("JKS");

        ks.load(new FileInputStream("C:/Users/jim/alice.keystore"), pwd);         
        ts.load(new FileInputStream("C:/Users/jim/alice.keystore"), pwd);    //unused  

        TrustManager[] trustServerCerts = new TrustManager[]{new X509TrustManager() 
        {

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }

            @Override
            public void checkClientTrusted(X509Certificate[] certs, String authType) {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] certs, String authType) 
            {

                for(X509Certificate c :certs){
                   System.out.println(c.getSubjectDN().getName());
                }

             }
            }
        };

        kmf.init(ks, pwd);
        tmf.init(ts);

        ctx.init(kmf.getKeyManagers(), trustServerCerts, null);

        clientSock=(SSLSocket)ctx.getSocketFactory().createSocket("127.0.0.1", 13000);
        clientSock.setUseClientMode(true);

    }

    public void run() throws IOException
    {
        InputStream input = null;  
        OutputStream output = null;  

        output = clientSock.getOutputStream(); 
        BufferedOutputStream bufferedOutput = new BufferedOutputStream(output);
        bufferedOutput.write("Alice: is running".getBytes());  
        bufferedOutput.flush();

    }

}

By the way, Windows only supports TLSv1 according to log which is surprising.

7
  • what if getAcceptedIssuers() returns an empty array, instead of null? Commented May 30, 2015 at 0:25
  • also, instead of use a trust-all approach, you could use A's keystore as B's truststore, and vice versa. Commented May 30, 2015 at 0:27
  • thanks, I just tried to add an empty array in getAcceptedIssuers() in client code: public X509Certificate[] getAcceptedIssuers() { return newX509Certificate[]{}; } But it still prompts the same error. Using TrustManager[] trustClientCerts can dynamically check certificates if certificates are added/removed at runtime. I have added error stack trace in my original message. Commented May 30, 2015 at 0:31
  • what about getAcceptedIssuers in server code? Commented May 30, 2015 at 0:46
  • Wow! adding new cert array in server works! X509Certificate[] getAcceptedIssuers() Return an array of certificate authority certificates which are trusted for authenticating peers. I don't figure out the use of getAcceptedIssuers. Can you explain a little bit? thk. Commented May 30, 2015 at 0:50

2 Answers 2

2

From the X509TrustManager.getAcceptedIssuers() documentation:

Returns: a non-null (possibly empty) array of acceptable CA issuer certificates.

It should be no surprise if returning a null value there (like you do) causes a NullPointerException somewhere subsequently.

Indeed, the constructor of sun.security.ssl.HandshakeMessage$CertificateRequest makes use of this array of CA certificates assuming it's not null.

In addition, if you want a more realistic attempt at two-way authentication, you could create your own test CA and have a proper truststore. The behaviour you're using (an empty list) works, but it's only specified as acceptable in the TLS 1.1 specifications. (Bypassing all trust checks is rarely a good idea anyway.)


From one of your comments:

I don't figure out the use of getAcceptedIssuers. Can you explain a little bit?

getAcceptedIssuers() is only used to build the list of acceptable CA certificates sent in the Certificate Request TLS message. Although it's an array of certificates, only the subject DNs of these certificates are really used.

This is very similar to the SSLCADNRequestFile and SSLCADNRequestPath directives in Apache Httpd. It is extremely rarely useful to make this differ from the list in your truststore.

What is actually used for the verification is checkServerTrusted().


Truststore contains certificates of trusted parties, which is loaded at program startup. If we are going to add/remove entries in Truststore at runtime, is there a way to do that?

If needed you can load your truststore programmatically, instead of using the default or system properties, as described in this answer. Then, create your server socket from the SSLContext you initialised with it.

If you can close and re-open the server socket every time you need to change your truststoreYou could also have something a bit more complicated where you implement your own TrustManager that delegates its calls to another trustmanager initialised from a KeyStore (your truststore) and TrustManagerFactory. You would change the delegated TrustManager whenever your truststore changes (you might need to take into account possible concurrency issues).

Sign up to request clarification or add additional context in comments.

7 Comments

Truststore contains certificates of trusted parties, which is loaded at program startup. If we are going to add/remove entries in Truststore at runtime, is there a way to do that? Appreciate.
the certs are all self-signed in my code. getAcceptedIssuers() returns a non-null (possibly empty) array of acceptable CA issuer certificates. How to understand "acceptable"?
See the TLS specification, certificate_authorities in Certificate Request. Essentially, it's what the server advertises it may accept (although it may still refuse if it doesn't like the cert for whatever reason when the client presents it). In practice, an empty list makes most clients send the first one they find. It's then up to checkServerTrusted to verify them one way or another (e.g. against a fixed list of known certs if they are all self-signed in your case).
thanks for detailed explanations. Since truststore is a group of .crt certificate files, is it functionally okay to use checkServerTrusted/checkClientTrusted to check certificates(.crt files) at runtime? By doing that, we can avoid reloading truststore and re-opening socket.
If you don't have too many of them, it might be worth putting them in a concurrent collection you keep in memory (e.g. CopyOnWriteArrayList or ConcurrentHashMap using the DN as key for efficiency) and use that to compare against the first cert in the chain you get in checkClientTrusted. Otherwise, you could also indeed access the file system every time (even though it's not necessarily efficient).
|
0

The getAcceptedIssuers() method may not return null. See the Javadoc.

But don't use this insecure trust manager code, especially as you have your own truststore.

Err, no you don't. Your keystore should be distinct from your truststore. They have completely different functions.

2 Comments

Thanks. I see that truststore and keystore are quite different. Java has pretty nice crypto stuff but security concepts(like keystore, truststore) are somewhat confusing, compared with Openssl things.
Mainly because they decided to use the same format for both.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.