SSL - Providing client certificates

Lists: pgsql-jdbc
From: "Saleem EDAH-TALLY" <nmset(at)netcourrier(dot)com>
To: pgsql-jdbc(at)postgresql(dot)org
Subject: SSL - Providing client certificates
Date: 2009-02-20 09:30:32
Message-ID: 200902201030.33352.nmset@netcourrier.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Lists: pgsql-jdbc

Hello,

http://jdbc.postgresql.org/documentation/83/ssl-factory.html states :

"Specifically it would be nice to be able to provide client certificates to be
validated by the server."

http://jdbc.postgresql.org/documentation/83/ssl-client.html refers to
validating the server's identity only, using javax.net.ssl.trustStore JVM-wide
setting.

However, if we set javax.net.ssl.keyStore and javax.net.ssl.keyStorePassword,
the SSL connection is established and the client certificate is verified.

This seems contradictory with the documntation, or I am missing something.

I would be happy to read your comments.

Thank you.


From: Kris Jurka <books(at)ejurka(dot)com>
To: Saleem EDAH-TALLY <nmset(at)netcourrier(dot)com>
Cc: pgsql-jdbc(at)postgresql(dot)org
Subject: Re: SSL - Providing client certificates
Date: 2009-02-20 17:29:39
Message-ID: Pine.BSO.4.64.0902201226580.23634@leary.csoft.net
Views: Raw Message | Whole Thread | Download mbox | Resend email
Lists: pgsql-jdbc

On Fri, 20 Feb 2009, Saleem EDAH-TALLY wrote:

> However, if we set javax.net.ssl.keyStore and javax.net.ssl.keyStorePassword,
> the SSL connection is established and the client certificate is verified.
>

When the code was first written, this wasn't tested and it was just
assumed that it wouldn't work. Recently we got a report that it did work,
but the documentation was not updated.

What I don't understand is how it selects the certificate to send. If you
have multiple keys in your keystore, how do you indicate which one to use?

Kris Jurka


From: Guillaume Cottenceau <gc(at)mnc(dot)ch>
To: Kris Jurka <books(at)ejurka(dot)com>
Cc: Saleem EDAH-TALLY <nmset(at)netcourrier(dot)com>, pgsql-jdbc(at)postgresql(dot)org
Subject: Re: SSL - Providing client certificates
Date: 2009-02-23 11:25:40
Message-ID: 87iqn1v1hn.fsf@meuh.mnc.lan
Views: Raw Message | Whole Thread | Download mbox | Resend email
Lists: pgsql-jdbc

Kris Jurka <books 'at' ejurka.com> writes:

> On Fri, 20 Feb 2009, Saleem EDAH-TALLY wrote:
>
>> However, if we set javax.net.ssl.keyStore and javax.net.ssl.keyStorePassword,
>> the SSL connection is established and the client certificate is verified.
>>
>
> When the code was first written, this wasn't tested and it was just
> assumed that it wouldn't work. Recently we got a report that it did
> work, but the documentation was not updated.
>
> What I don't understand is how it selects the certificate to send. If
> you have multiple keys in your keystore, how do you indicate which one
> to use?

My quite limited understanding of the behaviour of SSL client
authentication may potentially help a little:

You initially send a certificate signing request to the
admin/owner of the server (signed with your private key); when
you receive the certificate reply (signed with their private
key), you can build a chain of trust between you and the server,
your keystore will look like:

Entry type: PrivateKeyEntry
Certificate chain length: 2

Certificate[1]:
Owner: <you>
Issuer: <server>

Certificate[2]:
Owner: <server>
Issuer: <server>

Then at the SSL handshake time, first the server presents his
certificate, second it asks for a client certificate, at that
time you are able to present the certificate belonging to the
chain of trust containing the server certificate on top.

--
Guillaume Cottenceau


From: "Saleem EDAH-TALLY" <nmset(at)netcourrier(dot)com>
To: pgsql-jdbc(at)postgresql(dot)org
Subject: Re: SSL - Providing client certificates
Date: 2009-02-23 14:53:02
Message-ID: 200902231519.29993.nmset@netcourrier.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Lists: pgsql-jdbc

Regarding Kris Jurka's question :

> > What I don't understand is how it selects the certificate to send. If
> > you have multiple keys in your keystore, how do you indicate which one
> > to use?

http://java.sun.com/javase/6/docs/api/javax/net/ssl/SSLContext.html#init(javax.net.ssl.KeyManager[],
javax.net.ssl.TrustManager[], java.security.SecureRandom)

states

<>
Only the first instance of a particular key and/or trust manager implementation
type in the array is used...
</>

Hence it's not necessary to overload a keystore or a truststore.

And who knows how the entries in the keystore are sorted an presented to
SSLContext.init. My guess they are sorted in ascending order of the aliases,
but it's just a guess.

As an add-on to this thread, the following class allows to build a
SSLSocketFactory that is not JVM wide but specific to a connection. It can be
passed as sslfactory, and a label passed as sslfactoryarg.
It may be adapted and included in the jdbc-driver if found useful.

**********************************************************************
public class SSLBoth extends javax.net.ssl.SSLSocketFactory{
private String ksFile = null;
private String ksType = null;
private String ksPwd = null;
private String tsFile = null;
private String tsType = null;
private String tsPwd = null;
private javax.net.ssl.SSLSocketFactory ssf = null;

public SSLBoth(String label) {
setParams(label);
javax.net.ssl.KeyManagerFactory kmf = makeKMF();
javax.net.ssl.TrustManagerFactory tmf = makeTMF();
javax.net.ssl.SSLContext sslc = makeSSLContext(kmf.getKeyManagers(),
tmf.getTrustManagers());
ssf = sslc.getSocketFactory();
}

private void setParams(String label) {
ksFile = System.getProperty("KEYSTORE_FILE_" + label);
ksType = System.getProperty("KEYSTORE_TYPE_" + label);
ksPwd = System.getProperty("KEYSTORE_PWD_" + label);
tsFile = System.getProperty("TRUSTSTORE_FILE_" + label);
tsType = System.getProperty("TRUSTSTORE_TYPE_" + label);
tsPwd = System.getProperty("TRUSTSTORE_PWD_" + label);
}
private javax.net.ssl.KeyManagerFactory makeKMF() {
java.security.KeyStore ks = loadKeyStore(ksFile, ksType, ksPwd);
if (ks != null) {
try {
javax.net.ssl.KeyManagerFactory kmf =
javax.net.ssl.KeyManagerFactory.getInstance(javax.net.ssl.KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, ksPwd.toCharArray());
return kmf;
} catch (java.security.KeyStoreException ex) {
ex.printStackTrace();
} catch (java.security.UnrecoverableKeyException ex) {
ex.printStackTrace();
} catch (java.security.NoSuchAlgorithmException ex) {
ex.printStackTrace();
}
}
return null;
}
private javax.net.ssl.TrustManagerFactory makeTMF() {
java.security.KeyStore ts = loadKeyStore(tsFile, tsType, tsPwd);
if (ts != null) {
try {
javax.net.ssl.TrustManagerFactory tmf =
javax.net.ssl.TrustManagerFactory.getInstance(javax.net.ssl.TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ts);
return tmf;
} catch (java.security.KeyStoreException ex) {
ex.printStackTrace();
} catch (java.security.NoSuchAlgorithmException ex) {
ex.printStackTrace();
}
}
return null;
}
private javax.net.ssl.SSLContext makeSSLContext(javax.net.ssl.KeyManager[]
km, javax.net.ssl.TrustManager[] tm) {
try {
javax.net.ssl.SSLContext sslc =
javax.net.ssl.SSLContext.getInstance("TLS");
sslc.init(km, tm, new java.security.SecureRandom());
return sslc;
} catch (java.security.KeyManagementException ex) {
ex.printStackTrace();
} catch (java.security.NoSuchAlgorithmException ex) {
ex.printStackTrace();
}
return null;
}
public static java.security.KeyStore loadKeyStore(String ksPath, String
type, String pwd) {
try {
java.security.KeyStore ks =
java.security.KeyStore.getInstance(type);
java.io.FileInputStream fis = new java.io.FileInputStream(ksPath);
ks.load(fis, pwd.toCharArray());
fis.close();
return ks;
}

catch (java.security.KeyStoreException ex) {
ex.printStackTrace();
}
catch (java.io.FileNotFoundException ex) {
ex.printStackTrace();
}
catch (java.io.IOException ex) {
ex.printStackTrace();
}
catch (java.security.NoSuchAlgorithmException ex) {
ex.printStackTrace();
}
catch (java.security.cert.CertificateException ex) {
ex.printStackTrace();
}
return null;
}

@Override
public String[] getDefaultCipherSuites() {
return ssf.getDefaultCipherSuites();
}

@Override
public String[] getSupportedCipherSuites() {
return ssf.getSupportedCipherSuites();
}

@Override
public java.net.Socket createSocket(java.net.Socket arg0, String arg1, int
arg2, boolean arg3) throws java.io.IOException {
return ssf.createSocket(arg0, arg1, arg2, arg3);
}

@Override
public java.net.Socket createSocket(String arg0, int arg1) throws
java.io.IOException, java.net.UnknownHostException {
return ssf.createSocket(arg0, arg1);
}

@Override
public java.net.Socket createSocket(String arg0, int arg1,
java.net.InetAddress arg2, int arg3) throws java.io.IOException,
java.net.UnknownHostException {
return ssf.createSocket(arg0, arg1, arg2, arg3);
}

@Override
public java.net.Socket createSocket(java.net.InetAddress arg0, int arg1)
throws java.io.IOException {
return ssf.createSocket(arg0, arg1);
}

@Override
public java.net.Socket createSocket(java.net.InetAddress arg0, int arg1,
java.net.InetAddress arg2, int arg3) throws java.io.IOException {
return ssf.createSocket(arg0, arg1, arg2, arg3);
}

}

**********************************************************************