Thread: JDBC SSL with postgresql

JDBC SSL with postgresql

From
Jeffrey Baker
Date:
I was interested in this[1] work on SSL client certs for JDBC, but I
see the author stopped working on your project.  I hope the list can
give me a quick clue, because i've been banging my head against this
all day.

1: http://github.com/ringerc/pkcs12provider

First of all, I should mention that my client cert authentication is
working fine with libpq/psql.  So I'm satisfied that the certs and
keys are in order.

The problem is when I try to use JDBC it doesn't pick up my client
cert.  I have two files, truststore and keystore.

$ file truststore keystore
truststore: Java KeyStore
keystore:   Java KeyStore

truststore has only my self-signed root CA cert.  keystore has the
root CA cert and my signed client certificate.  Using a trivial JDBC
test class and this command line:

java -cp /usr/share/java/postgresql.jar:.
-Djavax.net.ssl.keyStore=./keystore
-Djavax.net.ssl.trustStore=./truststore TestJdbc

I get this exception:

Exception in thread "main" org.postgresql.util.PSQLException: FATAL:
connection requires a valid client certificate

Which is half good, because I know that it's validating the trust
chain from the server to the root CA, but half bad because it's not
sending the client cert.  I know it's opening the keystore with my
client cert in it, because I verified it with strace.

Do I need to use a SSL socket factory class to make this work, or is
it supposed to work out of the box and if so how?

-jwb

Re: JDBC SSL with postgresql

From
Craig Ringer
Date:
On 3/06/2010 8:46 AM, Jeffrey Baker wrote:
> I was interested in this[1] work on SSL client certs for JDBC, but I
> see the author stopped working on your project.  I hope the list can
> give me a quick clue, because i've been banging my head against this
> all day.
>
> 1: http://github.com/ringerc/pkcs12provider

I stopped working on it because it's unnecessary if Java is configured
correctly using the standard, built-in SSLSocketFactory and the system
properties controlling it, as documented in the README in that directory.

Unfortunately, that's only true with current Pg server versions if the
same root signed the server and client certificates, or if you have only
one client cert installed in your KeyStore. Otherwise, Java doesn't know
which client cert to send.

> truststore has only my self-signed root CA cert.  keystore has the
> root CA cert and my signed client certificate.  Using a trivial JDBC
> test class and this command line:
>
> java -cp /usr/share/java/postgresql.jar:.
> -Djavax.net.ssl.keyStore=./keystore
> -Djavax.net.ssl.trustStore=./truststore TestJdbc
>
> I get this exception:
>
> Exception in thread "main" org.postgresql.util.PSQLException: FATAL:
> connection requires a valid client certificate

This code (attached) might help you out. I've been meaning to push it to
gitgub.

> Which is half good, because I know that it's validating the trust
> chain from the server to the root CA, but half bad because it's not
> sending the client cert.  I know it's opening the keystore with my
> client cert in it, because I verified it with strace.
>
> Do I need to use a SSL socket factory class to make this work, or is
> it supposed to work out of the box and if so how?

You'll need a custom SSLSocketFactory (like the one included in
PgClientCertDemo) if you want to target current 8.x versions reliably.
Make it configurable, though, because with 9.x Pg versions you won't
need it, and using a custom SSLSocketFactory makes it practically
impossible for the user to use PKCS#11 hardware keys and the like.

--
Craig Ringer

Attachment

Re: JDBC SSL with postgresql

From
Craig Ringer
Date:
On 3/06/2010 8:46 AM, Jeffrey Baker wrote:
> I was interested in this[1] work on SSL client certs for JDBC, but I
> see the author stopped working on your project.  I hope the list can
> give me a quick clue, because i've been banging my head against this
> all day.
>
> 1: http://github.com/ringerc/pkcs12provider

I stopped working on it because it's unnecessary if Java is configured
correctly using the standard, built-in SSLSocketFactory and the system
properties controlling it, as documented in the README in that directory.

Unfortunately, that's only true with current Pg server versions if the
same root signed the server and client certificates, or if you have only
one client cert installed in your KeyStore. Otherwise, Java doesn't know
which client cert to send.

> truststore has only my self-signed root CA cert.  keystore has the
> root CA cert and my signed client certificate.  Using a trivial JDBC
> test class and this command line:
>
> java -cp /usr/share/java/postgresql.jar:.
> -Djavax.net.ssl.keyStore=./keystore
> -Djavax.net.ssl.trustStore=./truststore TestJdbc
>
> I get this exception:
>
> Exception in thread "main" org.postgresql.util.PSQLException: FATAL:
> connection requires a valid client certificate

This code might help you out. I've been meaning to push it to gitgub.

Sources:
   http://www.postnewspapers.com.au/~craig/PgClientCertDemo.zip
Executable:
   http://www.postnewspapers.com.au/~craig/PgClientCertDemo.jar

> Which is half good, because I know that it's validating the trust
> chain from the server to the root CA, but half bad because it's not
> sending the client cert.  I know it's opening the keystore with my
> client cert in it, because I verified it with strace.
>
> Do I need to use a SSL socket factory class to make this work, or is
> it supposed to work out of the box and if so how?

You'll need a custom SSLSocketFactory (like the one included in
PgClientCertDemo) if you want to target current 8.x versions reliably.
Make it configurable, though, because with 9.x Pg versions you won't
need it, and using a custom SSLSocketFactory makes it practically
impossible for the user to use PKCS#11 hardware keys and the like.

--
Craig Ringer


Re: JDBC SSL with postgresql

From
Jeffrey Baker
Date:
On Wed, Jun 2, 2010 at 7:26 PM, Craig Ringer
<craig@postnewspapers.com.au> wrote:
> On 3/06/2010 8:46 AM, Jeffrey Baker wrote:
>>
>> I was interested in this[1] work on SSL client certs for JDBC, but I
>> see the author stopped working on your project.  I hope the list can
>> give me a quick clue, because i've been banging my head against this
>> all day.
>>
>> 1: http://github.com/ringerc/pkcs12provider
>
> I stopped working on it because it's unnecessary if Java is configured
> correctly using the standard, built-in SSLSocketFactory and the system
> properties controlling it, as documented in the README in that directory.
>
> Unfortunately, that's only true with current Pg server versions if the same
> root signed the server and client certificates, or if you have only one
> client cert installed in your KeyStore. Otherwise, Java doesn't know which
> client cert to send.

Thanks for the info.

I have complete control of both ends, and both the server and client
are signed by my self-signed root cert.  I think perhaps the problem
here is that I'm trying to tackle the entire Java SSL infrastructure
in one go; I knew nothing about it this morning.  I read somewhere
that the keystore has to include both the client cert and the root
cert that signed it.  Is that not true?  I've tried it both ways:
added root cert, then added client cert (this is the way it's
documented in the keytool manual page, the way I interpret it), and
only adding the client cert to the keystore.  It doesn't seem to work
either way.

> This code (attached) might help you out. I've been meaning to push it to
> gitgub.

Thanks again, these classes do seem like they would solve the problem,
if I can't figure out the standard way of doing things.

>> Which is half good, because I know that it's validating the trust
>> chain from the server to the root CA, but half bad because it's not
>> sending the client cert.  I know it's opening the keystore with my
>> client cert in it, because I verified it with strace.
>>
>> Do I need to use a SSL socket factory class to make this work, or is
>> it supposed to work out of the box and if so how?
>
> You'll need a custom SSLSocketFactory (like the one included in
> PgClientCertDemo) if you want to target current 8.x versions reliably. Make
> it configurable, though, because with 9.x Pg versions you won't need it, and
> using a custom SSLSocketFactory makes it practically impossible for the user
> to use PKCS#11 hardware keys and the like.

Not a requirement in this project.  I want to distribute a WAR file to
a third party "cloud" that can access my database, and I want to use
client certs that expire in one day rather than distributing
passwords.  Smart cards and whatnot would be slick for users but don't
really apply in virtuality.

-jwb

Re: JDBC SSL with postgresql

From
Jeffrey Baker
Date:
On Wed, Jun 2, 2010 at 5:46 PM, Jeffrey Baker <jwbaker@gmail.com> wrote:
> I was interested in this[1] work on SSL client certs for JDBC, but I
> see the author stopped working on your project.  I hope the list can
> give me a quick clue, because i've been banging my head against this
> all day.

Just to update the list, I did figure this out.  Turns out I hadn't
imported my private key into the keystore file.  Which, in turn, is a
ridiculous pain in the butt because keytool can only deal with private
keys it generated, or those in PKCS#12 files, and in fact only in
PKCS#12 files protected with passwords.  Furthermore once the key is
in the keystore it must also have a password there (keystore password
as well as key password) or the implementation will refuse to use it!

# openssl pkcs12 -export -out client.pkcs12 -in client.cert -inkey client.key
# keytool -importkeystore -deststorepass changeit -destkeystore
client.jks -srckeystore client.pkcs12 -srcstorepass changeit
-srcstoretype PKCS12 -alias 1 -destkeypass changeit

and

$ java -Djavax.net.ssl.keyStore=./client.jks
-Djavax.net.ssl.keyStorePassword=changeit
-Djavax.net.ssl.trustStore=./truststore

Given all that, it works!

-jwb

Re: JDBC SSL with postgresql

From
Craig Ringer
Date:
On 3/06/2010 11:06 AM, Jeffrey Baker wrote:

> Thanks for the info.
>
> I have complete control of both ends, and both the server and client
> are signed by my self-signed root cert.  I think perhaps the problem
> here is that I'm trying to tackle the entire Java SSL infrastructure
> in one go; I knew nothing about it this morning.  I read somewhere
> that the keystore has to include both the client cert and the root
> cert that signed it.  Is that not true?

No, it's not strictly true, but it's a good idea. However, "include" is
pretty broad.

When you get a reply from your certificate authority containing the
signed certificate, it should include the full certificate chain in that
reply. If it doesn't, you should append the CA cert and any other
intermediate certs to the reply before importing it with keytool.

So, when you use keytool to list your keystore, you should only see one
entry (alias). The certificate part of that entry should preferably
include the full certificate chain up to the CA certificate, though.

Your truststore should contain only trustedCertificate aliases for the
CA certs you trust to identify peers.

>  I've tried it both ways:
> added root cert, then added client cert (this is the way it's
> documented in the keytool manual page, the way I interpret it), and
> only adding the client cert to the keystore.  It doesn't seem to work
> either way.

Concatenate all certs in the chain into one file, and import that.

--
Craig Ringer

Re: JDBC SSL with postgresql

From
Craig Ringer
Date:
On 3/06/2010 12:08 PM, Jeffrey Baker wrote:
> On Wed, Jun 2, 2010 at 5:46 PM, Jeffrey Baker<jwbaker@gmail.com>  wrote:
>> I was interested in this[1] work on SSL client certs for JDBC, but I
>> see the author stopped working on your project.  I hope the list can
>> give me a quick clue, because i've been banging my head against this
>> all day.
>
> Just to update the list, I did figure this out.  Turns out I hadn't
> imported my private key into the keystore file.  Which, in turn, is a
> ridiculous pain in the butt because keytool can only deal with private
> keys it generated, or those in PKCS#12 files, and in fact only in
> PKCS#12 files protected with passwords.

It's often easier to just point the keyStore directly at a PKCS#12 file
using the javax.net.ssl.trustStoreType=pkcs12 system property.

> Furthermore once the key is
> in the keystore it must also have a password there (keystore password
> as well as key password) or the implementation will refuse to use it!

And both passwords must be the SAME.

> # openssl pkcs12 -export -out client.pkcs12 -in client.cert -inkey client.key
> # keytool -importkeystore -deststorepass changeit -destkeystore
> client.jks -srckeystore client.pkcs12 -srcstorepass changeit
> -srcstoretype PKCS12 -alias 1 -destkeypass changeit

Generally, you are better off using keytool to generate the key and a
certificate request, getting that certificate request signed by the CA,
and importing the reply into your keystore.

--
Craig Ringer