Reputation: 61
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.
Upvotes: 2
Views: 2380
Reputation: 122599
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.
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).
Upvotes: 2
Reputation: 310840
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.
Upvotes: 0