Обсуждение: New code: Easy PKCS#12 client certificate support for pgjdbc

Поиск
Список
Период
Сортировка

New code: Easy PKCS#12 client certificate support for pgjdbc

От
Craig Ringer
Дата:
Hi folks

I've been wrestling with certificate management in Java (as relates to
PgJDBC) for a while as part of an app I'm working on, and have settled
on a solution I'm happy with. It's general enough that it'll be very
useful to other people here, so I've split it out into a separate library.

It's intended for use with PgJDBC as an alternate sslfactory= in the
jdbc url/Properties to simplify use of client certificates. Certs may be
specified using the sslfactoryarg url parameter / Properties key.

This provider is not Pg or JDBC specific and doesn't depend on either
though. You can use it anywhere you use SSL/TLS with client certs and
you want PKCS#12 support.

It's really just an SSLSocketFactory that uses PKCS#12 files to provide
certificate trust as well as user public and private keys for client
certificate authentication. It verifies that the server being connected
to has a certificate trust path to one of the CA certificates in the
PKCS#12 file. If asked by the server, it will authenticate the client
end against the server using the PKCS#12 file's client cert details.

It is completely independent of the Java `cacerts' trust list, any .jks
files installed, etc. Feeding it your own certificate source requires
implementing a one-method interface that returns an InputStream.

You can grab it from github if you want to have a play. I'm interested
in improving the interface, and would be happy to accept changes to add
an additional trusted cert list (.der / .pem file).

There's a demo in the `demo' package.

BE WARNED: This code is NOT well tested, and I am fairly new to the Java
SSL APIs. This thing might trust 'goatse.cx' for all I know. Be very
careful.

Grab code here:

    git://github.com/ringerc/pkcs12provider.git

--
Craig Ringer

Re: New code: Easy PKCS#12 client certificate support for pgjdbc

От
Craig Ringer
Дата:
Just a follow-up on this:

The code I posted works, but is the wrong approach to take.  I've
removed it and replaced it with some suitable documentation. Given how
much trouble I had figuring out the right way to do this, I bet I'm not
alone, so I thought I'd write it up.


In summary: Applications should use the core SunPKIX trust and
certificate managers and the default SSLSocketFactory if at all
possible, because they're flexible enough to handle pluggable
authentication methods like hardware tokens with no app code changes. If
you override the SSLSocketFactory and provide your own trust and key
managers you lose a lot of the power of the system.

If, like mine, your app needs to offer a user a way to configure
additional keys and trusted certs, just create your own JKS or JECKS
KeyStores for the trust- and key- stores during startup (if they don't
already exist), and set the javax.net.ssl. properties to point to them.
The default Sun providers will then load those app-specific stores
instead of the global cert- and key- stores.

If the user attempts a connection to a service that requires a client
certificate, or a service whose certificate isn't trusted, you  can
catch the resulting exception, examine it to determine the root cause,
and prompt the user to supply a client certificate or to install a
trusted root cert as appropriate. Apache Commons Lang is useful here for
inspection of deep exception chains.

If you'd prefer to load a user PKCS#12 file directly for the keystore,
just specify it in your app's javax.net.ssl. system properties via
System.setProperty(...) before any SSL code is invoked, and the default
X509KeyStore will be happy to use it. Make sure to set
javax.net.ssl.keyStoreType to "pkcs12".

If you really do need to replace the X509TrustManager for some reason,
use the certificate verification built-in to the JVM rather than
re-implementing it. Create a Set<TrustAnchor> from your list of trusted
X509Certificate instances from your trust store, and use it with
CertPathValidator to validate a CertPath created from the
X509Certificate[] provided to the TrustManager. This does proper PKIX
validation a few lines of code.

Even better is to load the default "SunPKIX" X509TrustManager and
X509KeyManager and put them before your own in the appropriate array
when creating the SSL context in your SSLSocketFactory. The SSL context
will try them in order. That way the user can still use all the
pluggable features, but you can (eg) fall back to your X509TrustManager
if the cert isn't trusted so you can display a "add to trust store?" prompt.

About the only downside with this that I've found is that there doesn't
appear to be any way to force the SunPKIX code to re-load a KeyStore
(the trust store or the key store) after SSL has been inited, so if the
user adds a trusted cert or key they seem to need to re-start the app.
You can get around it by using non-public com.sun api, but that's never
a good idea.

If you're having problems in this area, you should read the JSSE
reference guide, as it'll help you understand how it all works:

http://java.sun.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html

--
Craig Ringer