Re: 8.1 Build 405 JDBC and certificate trasnfer - Mailing list pgsql-jdbc

From Neil Macneale
Subject Re: 8.1 Build 405 JDBC and certificate trasnfer
Date
Msg-id 43F85003.8080001@theory.org
Whole thread Raw
In response to 8.1 Build 405 JDBC and certificate trasnfer  (Andrew Madu <andrewmadu@gmail.com>)
Responses Re: 8.1 Build 405 JDBC and certificate trasnfer  (Andrew Madu <andrewmadu@gmail.com>)
List pgsql-jdbc
Andrew Madu wrote:
> Hi,
> can anyone confirm with me whether this driver supports certificate
> transfer via the inclusion of root.crt in the data folder yet?
>
> regards
>
> Andrew


I sure hope it doesn't, because I am putting together a patch to fix
this:-) Attached is a patch to support client certificate
authentication, to some degree. It has a limitation that your trust
store must be the same as your key store. It is also completely
uncommented, but it's not that complicated.

The patch contains two things. First a change to MakeSSL which allows
the user specified SSLSocketFactory to be constructed with the "info"
Properties Object. I don't know if the postgresql JDBC veterans condone
this, but there are simply to many configuration options to be contained
in a simple string. (key word being simple). The second is a new class
called ValidatingFactory, which is a SSLSocketFactory implementation
which does the work required.

Also included is a simple class file with a main method to show how to
use the class.

Like I said, I'm not done with the patch, but I figured since someone
asked about it I may was well pipe up. It would be nice to get comments
anyway!

Cheers,
Neil
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;

import org.postgresql.Driver;
import org.postgresql.ssl.NonValidatingFactory;
import org.postgresql.ssl.ValidatingFactory;


public class Client {

    public static void main(String[] a) throws Exception {

        // Load the drive class to register jdbc
        Class c = Driver.class;
        String url = "jdbc:postgresql://HOST/DB";
        Properties props = new Properties();
        props.setProperty("user","--ID--");
        props.setProperty("password","--PASSWORD--");
        props.setProperty("ssl", "true");

        // validation args. the options are all options. for defaults, read the code.
        props.setProperty("sslfactory", ValidatingFactory.class.getName());
        props.setProperty(ValidatingFactory.SSL_KEYSTORE_PASSWORD, "--PASSWORD--");
        props.setProperty(ValidatingFactory.SSL_PROTOCOL, "TLS"); // or SSLv3, SSLv2,SSlv1
        props.setProperty(ValidatingFactory.SSL_KEYSTORE_TYPE, "JKS"); // or PKCS12,JCEKS(?)
        props.setProperty(ValidatingFactory.SSL_FILE_KEYSTORE, "--FILE--");

        Connection conn = DriverManager.getConnection(url, props);

        Statement st = conn.createStatement();
        ResultSet rs = st.executeQuery("SELECT version();");
        while (rs.next()) {
            System.out.println(rs.getString(1));
        }
        rs.close();
        st.close();
        conn.close();
    }

}
### Eclipse Workspace Patch 1.0
#P pgjdbc
Index: org/postgresql/ssl/MakeSSL.java
===================================================================
RCS file: /usr/local/cvsroot/pgjdbc/pgjdbc/org/postgresql/ssl/MakeSSL.java,v
retrieving revision 1.5
diff -u -r1.5 MakeSSL.java
--- org/postgresql/ssl/MakeSSL.java    24 Nov 2005 02:29:22 -0000    1.5
+++ org/postgresql/ssl/MakeSSL.java    19 Feb 2006 10:34:18 -0000
@@ -50,8 +50,14 @@
                 }
                 catch (NoSuchMethodException nsme)
                 {
-                    ctor = factoryClass.getConstructor((Class[])null);
-                    args = null;
+                        try
+                        {
+                            ctor = factoryClass.getConstructor((Class[])null);
+                            args = null;
+                        } catch (NoSuchMethodException nsme2){
+                                ctor = factoryClass.getConstructor(new Class[]{Properties.class});
+                                args[0] = info;
+                        }
                 }
                 factory = (SSLSocketFactory)ctor.newInstance(args);
             }
@@ -60,7 +66,7 @@
                 throw new PSQLException(GT.tr("The SSLSocketFactory class provided {0} could not be instantiated.",
classname),PSQLState.CONNECTION_FAILURE, e); 
             }
         }
-
+
         Socket newConnection = factory.createSocket(stream.getSocket(), stream.getHost(), stream.getPort(), true);
         stream.changeSocket(newConnection);
     }
Index: org/postgresql/ssl/ValidatingFactory.java
===================================================================
RCS file: org/postgresql/ssl/ValidatingFactory.java
diff -N org/postgresql/ssl/ValidatingFactory.java
--- /dev/null    1 Jan 1970 00:00:00 -0000
+++ org/postgresql/ssl/ValidatingFactory.java    1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,97 @@
+package org.postgresql.ssl;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.Properties;
+
+public class ValidatingFactory extends WrappedFactory {
+
+    public static final String SSL_FILE_KEYSTORE = "ssl.keystore";
+
+    public static final String SSL_KEYSTORE_TYPE = "ssl.keystore.type";
+
+    public static final String SSL_KEYSTORE_PASSWORD = "ssl.keystore.password";
+
+    public static final String SSL_PROTOCOL = "ssl.protocol";
+
+    public ValidatingFactory(Properties props) throws Exception {
+
+        File keystoreFile = getKeyStoreFile(props);
+
+        String keyStoreType = getKeyStoreType(props, keystoreFile);
+        KeyStore trustStore = KeyStore.getInstance(keyStoreType);
+        KeyStore clientKeyStore = KeyStore.getInstance(keyStoreType);
+
+        char[] password = getKeyStorePWD(props);
+
+        trustStore.load(new FileInputStream(keystoreFile), password);
+        clientKeyStore.load(new FileInputStream(keystoreFile), password);
+
+        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+        tmf.init(trustStore);
+
+        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+        kmf.init( clientKeyStore, password );
+
+        Arrays.fill(password, '\0');
+
+        SSLContext sslContext = SSLContext.getInstance(getProtocol(props));
+
+        sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+
+        _factory = sslContext.getSocketFactory();
+    }
+
+    private static File getKeyStoreFile(Properties props){
+        String ks = props.getProperty(SSL_FILE_KEYSTORE);
+        if ( ks != null ){
+            return new File(ks);
+        }
+
+        File ans = new File(System.getProperty("user.home"));
+        ans = new File(ans, ".postgresql");
+        ans = new File(ans, "postgresql.jks");
+        return ans;
+    }
+
+    private static String getKeyStoreType(Properties props, File keyStore){
+        String kst = props.getProperty(SSL_KEYSTORE_TYPE);
+        if ( kst != null ){
+            return kst;
+        }
+
+        String fileName = keyStore.getName();
+        if (fileName.lastIndexOf('.') > 0){
+            return fileName.substring(fileName.lastIndexOf('.') + 1 ).toUpperCase();
+        }
+
+        return "JKS";
+    }
+
+    private static char[] getKeyStorePWD(Properties props){
+        String pwd = props.getProperty(SSL_KEYSTORE_PASSWORD, "");
+        return pwd.toCharArray();
+    }
+
+    private static String getProtocol(Properties props){
+        if ( props.containsKey(SSL_PROTOCOL)){
+            return props.getProperty(SSL_PROTOCOL);
+        }
+        return "SSLv3";
+    }
+}

pgsql-jdbc by date:

Previous
From: Konstantinos Agouros
Date:
Subject: ResultSet.getDate throws too long
Next
From: Andrew Madu
Date:
Subject: Re: 8.1 Build 405 JDBC and certificate trasnfer