diff -rcN postgresql-jdbc-9.1dev-900.src/build.xml postgresql-jdbc-new/build.xml
*** postgresql-jdbc-9.1dev-900.src/build.xml	2011-03-31 08:25:41.000000000 +0200
--- postgresql-jdbc-new/build.xml	2011-08-23 19:25:04.000000000 +0200
***************
*** 369,374 ****
--- 369,375 ----
    <property name="preparethreshold" value="5" />
    <property name="loglevel" value="0" />
    <property name="protocolVersion" value="0" />
+   <property name="ssltest.properties" value="ssltest.properties" />
  
    <!-- The tests now build to a separate directory and jarfile from the
         driver build, to ensure we're really testing against the jar we just
***************
*** 411,416 ****
--- 412,418 ----
        <sysproperty key="preparethreshold" value="${preparethreshold}" />
        <sysproperty key="loglevel" value="${loglevel}" />
        <sysproperty key="protocolVersion" value="${protocolVersion}" />
+       <sysproperty key="ssltest.properties" value="${ssltest.properties}" />
  
        <classpath>
          <pathelement location="${jardir}/postgresql.jar" />
***************
*** 422,427 ****
--- 424,430 ----
        <test name="org.postgresql.test.jdbc3.Jdbc3TestSuite" outfile="${testResultsDir}/jdbc3"/>
        <test name="org.postgresql.test.xa.XATestSuite" outfile="${testResultsDir}/xa"/>
        <test name="org.postgresql.test.jdbc4.Jdbc4TestSuite" if="jdbc4tests" outfile="${testResultsDir}/jdbc4"/>
+       <test name="org.postgresql.test.ssl.SslTestSuite" outfile="${testResultsDir}/ssl"/>
      </junit>
    </target>
    
diff -rcN postgresql-jdbc-9.1dev-900.src/certdir/badclient.crt postgresql-jdbc-new/certdir/badclient.crt
*** postgresql-jdbc-9.1dev-900.src/certdir/badclient.crt	1970-01-01 01:00:00.000000000 +0100
--- postgresql-jdbc-new/certdir/badclient.crt	2011-08-20 22:37:37.000000000 +0200
***************
*** 0 ****
--- 1,13 ----
+ -----BEGIN CERTIFICATE-----
+ MIICDDCCAXWgAwIBAgIJANE9OHiE6fLeMA0GCSqGSIb3DQEBBQUAMA8xDTALBgNV
+ BAMTBHRlc3QwHhcNMTEwODIwMjAzNzM3WhcNMTEwOTE5MjAzNzM3WjAPMQ0wCwYD
+ VQQDEwR0ZXN0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDN7JFKgpkr+4XY
+ k7vrmkzP83IHrKGnHtmTPpJHnz2Axw21qF34oWRk1UPwjAmFRdLLsL28PgCIUwCR
+ tMJKY+fYXiygQ535wbeGrIii50Y8hEPWxB/KgFtytHaL4mNTtKktTZpxpfY2SLOL
+ yRr1Tvikj2fDygxJgeTmlr2GQJgkGQIDAQABo3AwbjAdBgNVHQ4EFgQUVzh18IGV
+ 6xys4+riIJSTHFARKn0wPwYDVR0jBDgwNoAUVzh18IGV6xys4+riIJSTHFARKn2h
+ E6QRMA8xDTALBgNVBAMTBHRlc3SCCQDRPTh4hOny3jAMBgNVHRMEBTADAQH/MA0G
+ CSqGSIb3DQEBBQUAA4GBADWs4iAsEC1UVL70Id5twkznbUsOVstFBW8w+TAA0uzm
+ DLtt7BTsqgvmTqi8QKDS2jB4rki1gEdJBJEfjMmxZvcQNyvuJQnUYUwThZn3QMRS
+ WmJoXCEmZTnZsDzSin2h9HNvgMzwphYOf3lE49fWiZuX/GuDDbWMTZkTX3K2Yhej
+ -----END CERTIFICATE-----
diff -rcN postgresql-jdbc-9.1dev-900.src/certdir/badclient.key postgresql-jdbc-new/certdir/badclient.key
*** postgresql-jdbc-9.1dev-900.src/certdir/badclient.key	1970-01-01 01:00:00.000000000 +0100
--- postgresql-jdbc-new/certdir/badclient.key	2011-08-21 13:58:38.000000000 +0200
***************
*** 0 ****
--- 1,15 ----
+ -----BEGIN RSA PRIVATE KEY-----
+ MIICXQIBAAKBgQDN7JFKgpkr+4XYk7vrmkzP83IHrKGnHtmTPpJHnz2Axw21qF34
+ oWRk1UPwjAmFRdLLsL28PgCIUwCRtMJKY+fYXiygQ535wbeGrIii50Y8hEPWxB/K
+ gFtytHaL4mNTtKktTZpxpfY2SLOLyRr1Tvikj2fDygxJgeTmlr2GQJgkGQIDAQAB
+ AoGAUDllsR2IBvaKgoGrqHIIkstJFWxP3gfKw+6Qlo6XK2BhGvBpC7/0mMPtH/2u
+ h9lX8jr88CVHl9SeWCWPu+OIIrQLt0XKxCoHW5zi4jVpnfW7MlFlzbSI6MTCSIPy
+ tUKBoGfu9rDqSu57CGjVhLQw1Glw2ZmqaeIRqBOt/8bgAMECQQDxoyGyRHc6CQIu
+ vGkmzBhhtP9saYhfsWeuTCIy02L2Yd50loKQscxD0GyUJPj6ZY/p9+I2DzRO9bcQ
+ Lb2UAmh9AkEA2ioB8RBBeCAK7RF/p5KcyC+1PwFOfz/T8OsxMYPldg7PDXsk8oZu
+ V8SIIHy6aw8VgX0qgbMrxiuGQz341VXYzQJBAKX8lJobs/b21zv5ixXun1UG+2qi
+ d96cRAeUHu3hDZsIEcLu3RO+Pv3af6uoVivTdm+XKxpNiXYwDieb9B1nJhUCQAY8
+ JxwNm+FkAQvvcReH8CUkf+iSD/1BBLyg7CH/JAKwv6gh+ddYkjS98CoFhopivvmZ
+ ayqTxU9/lPG9BqyKLI0CQQDL1/fDhz6gz0TaDgiYgRYVSdgqu9+CPKoyG/xHbQGg
+ 9AAH0t+CSuvR+/CezJF8zEGQ052gFhp2H2X32bj/vjEG
+ -----END RSA PRIVATE KEY-----
Binary files postgresql-jdbc-9.1dev-900.src/certdir/badclient.pk8 and postgresql-jdbc-new/certdir/badclient.pk8 differ
diff -rcN postgresql-jdbc-9.1dev-900.src/certdir/badroot.crt postgresql-jdbc-new/certdir/badroot.crt
*** postgresql-jdbc-9.1dev-900.src/certdir/badroot.crt	1970-01-01 01:00:00.000000000 +0100
--- postgresql-jdbc-new/certdir/badroot.crt	2011-08-20 22:39:09.000000000 +0200
***************
*** 0 ****
--- 1,18 ----
+ -----BEGIN CERTIFICATE-----
+ MIIC4TCCAkqgAwIBAgIJAMMVL4o9QQxnMA0GCSqGSIb3DQEBBQUAMFUxCzAJBgNV
+ BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
+ aWRnaXRzIFB0eSBMdGQxDjAMBgNVBAMTBWJhZGNhMB4XDTExMDgyMDIwMzkwOVoX
+ DTExMDkxOTIwMzkwOVowVTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3Rh
+ dGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEOMAwGA1UEAxMF
+ YmFkY2EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMA5oTgx+B+h7BtDd6rv
+ q9TuccBfVolZhTOPL5w/+hQjd0bWKGkO8CgmXcdSc9g+NvQB7TGNUa7BwUis405b
+ G+lRNC+AeX4mLY1M37QsekvI46HziZ8Gb6IvSmjcfg4gjOatEZz71v8TRy7ExlMM
+ bZ4jGOwXdc+E22lo6puuxzAzAgMBAAGjgbgwgbUwHQYDVR0OBBYEFM6SmCRnQvV6
+ 6pEggkQjvWw7/H1IMIGFBgNVHSMEfjB8gBTOkpgkZ0L1euqRIIJEI71sO/x9SKFZ
+ pFcwVTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoT
+ GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEOMAwGA1UEAxMFYmFkY2GCCQDDFS+K
+ PUEMZzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAJz1OURzHO0+4qyC
+ f0jNYTprDELM84nVMvP5Z0I8sROd5IqKHZQkI0F3kZtzvdgnhAhCXYy1hj/DOeDf
+ 0T47FLzv4YrrwBPDsfM0ufd2ScGu87r2PnTFTqv31oRrOJRmEEMQ9gooRG0R8bEX
+ dmirISoO/GJ+cRBTphDmwBa9JVtd
+ -----END CERTIFICATE-----
diff -rcN postgresql-jdbc-9.1dev-900.src/certdir/goodclient.crt postgresql-jdbc-new/certdir/goodclient.crt
*** postgresql-jdbc-9.1dev-900.src/certdir/goodclient.crt	1970-01-01 01:00:00.000000000 +0100
--- postgresql-jdbc-new/certdir/goodclient.crt	2011-08-20 22:34:23.000000000 +0200
***************
*** 0 ****
--- 1,13 ----
+ -----BEGIN CERTIFICATE-----
+ MIICDDCCAXWgAwIBAgIJANGTGuCgJYbrMA0GCSqGSIb3DQEBBQUAMA8xDTALBgNV
+ BAMTBHRlc3QwHhcNMTEwODIwMjAzNDIzWhcNMTEwOTE5MjAzNDIzWjAPMQ0wCwYD
+ VQQDEwR0ZXN0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCU+q7HRCdhVcz
+ X8eDQB54L7wX+oys9baMkcGta4UM6r13/Bbftc7RHv/m74b7hJQLWtkCMLWujA1B
+ e+VfoaW1j3czl4De3djyZkkJmDYTJh+zWT+5E8kFxcMbMHJGlL+ip3mb+QwF4KIa
+ qdmm2LON7DxRfOof1n+B8GOcXvhfCwIDAQABo3AwbjAdBgNVHQ4EFgQUGTXV7eYC
+ h93Fzuq6vIq/of5Mg54wPwYDVR0jBDgwNoAUGTXV7eYCh93Fzuq6vIq/of5Mg56h
+ E6QRMA8xDTALBgNVBAMTBHRlc3SCCQDRkxrgoCWG6zAMBgNVHRMEBTADAQH/MA0G
+ CSqGSIb3DQEBBQUAA4GBAA001m1BXDTYz7tNJRULMk2NNO99Ls9bAGJLFL/wfyCK
+ EDEPKDmcS1Y9mzulb6w5DiR8PCK2a7K6oFVOg+qD5ny8WKKFZDuJjvvwRHU1P+sk
+ hOux0CooLOo3U3S76wgrU4vDYI07Pmxty7JfI5GKt1yCaW4Yg3r7ZOhIdH0zlcO9
+ -----END CERTIFICATE-----
diff -rcN postgresql-jdbc-9.1dev-900.src/certdir/goodclient.key postgresql-jdbc-new/certdir/goodclient.key
*** postgresql-jdbc-9.1dev-900.src/certdir/goodclient.key	1970-01-01 01:00:00.000000000 +0100
--- postgresql-jdbc-new/certdir/goodclient.key	2011-08-21 13:43:37.000000000 +0200
***************
*** 0 ****
--- 1,15 ----
+ -----BEGIN RSA PRIVATE KEY-----
+ MIICXQIBAAKBgQDCU+q7HRCdhVczX8eDQB54L7wX+oys9baMkcGta4UM6r13/Bbf
+ tc7RHv/m74b7hJQLWtkCMLWujA1Be+VfoaW1j3czl4De3djyZkkJmDYTJh+zWT+5
+ E8kFxcMbMHJGlL+ip3mb+QwF4KIaqdmm2LON7DxRfOof1n+B8GOcXvhfCwIDAQAB
+ AoGAYCCo1eSbyoAiUayIghmeuO9MCUSMgCaQJdsYRpQQXgJktUzDd+h0pSfDiNZm
+ HFdJ8JABFz9HME/hYspJ/dN1p4xwOpJCDrUwIZ1Jt+zcgeqJF7W7pmz6/nbBxzrA
+ ujkZVFnRtdRlq7//LW+cKt+5DOal0aVfmvvwvaouPgoIepkCQQDt1ejLlFRuMaRq
+ FmGvvmj0El6PBhmBkEisK4eYUF8smE/Y9QnW9r9I51D2AXgVtA0HVfcxi7+Mxg6J
+ 6SujQAD3AkEA0StbN7k0iPKXqDsMXTUZJHhH53CPCeIqKXRXjVyfjpkJ9ZkAiugV
+ E2IU+YDn089+v6db84BMD3syc4xC4ighjQJBANqDv3hT1uLLf2jtGWFzy0/8FJ7J
+ luOgYfS1pcY+icMaSbpnIFw9pm/f/1WW8fLW02WbCRufbyVyzubMsYgzBO8CQAW3
+ WJvSPORNNOXND05wyAoeG2Dwy4fqnVpaqpXbZSThe8sxbjldlE3eTVJwOmWocv9L
+ 1JB3/hwiMsPDnoRNsnUCQQCcSqNtxDVItw1adE3LjvfY22nJltwRqEl9bX797sJt
+ h0RDpeKrgCQrHTZxToRLKbaI/HeKh0Ji0cwYmdjNwUio
+ -----END RSA PRIVATE KEY-----
Binary files postgresql-jdbc-9.1dev-900.src/certdir/goodclient.pk8 and postgresql-jdbc-new/certdir/goodclient.pk8 differ
diff -rcN postgresql-jdbc-9.1dev-900.src/certdir/goodroot.crt postgresql-jdbc-new/certdir/goodroot.crt
*** postgresql-jdbc-9.1dev-900.src/certdir/goodroot.crt	1970-01-01 01:00:00.000000000 +0100
--- postgresql-jdbc-new/certdir/goodroot.crt	2011-08-23 00:56:02.000000000 +0200
***************
*** 0 ****
--- 1,18 ----
+ -----BEGIN CERTIFICATE-----
+ MIIC7zCCAligAwIBAgIJAKUTCTev2VaiMA0GCSqGSIb3DQEBBQUAMFkxCzAJBgNV
+ BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
+ aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xMTA4MjAxOTIz
+ NDFaFw0xMTA5MTkxOTIzNDJaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21l
+ LVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV
+ BAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAyNFMnWLY
+ JSPgTHtAicWpU+fvw6tXqz9iVcGoL86+FMjP8aNUvLbGSHsSd9Oc9CguKdMMhJjH
+ v2/I+cAcsvMcAxAAo67iRcUw14vOiQftmog42VLON32WzTohziSh0TJ5iWeEm1Qr
+ cpf6XAr3ocsLmk7z/rIDKs3qqV3vV2ySQb0CAwEAAaOBvjCBuzAdBgNVHQ4EFgQU
+ cXtucxgDcerHn9DuGXAYSFv3eZ4wgYsGA1UdIwSBgzCBgIAUcXtucxgDcerHn9Du
+ GXAYSFv3eZ6hXaRbMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRl
+ MSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMTCWxv
+ Y2FsaG9zdIIJAKUTCTev2VaiMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
+ gYEAHKW3D32kGqi1jMgmGhSXf9cmgXpAp4R/y1kYDj4EKtLx7+C6LsFXEZ4gnHSd
+ VcmoVkHKpfUL8/geP6yq+wHARzY70oVg5OTfbbCX4hxMjAY6az5Z7+7IyzjyrnYK
+ clxZKitIfHoU5u9EclIj47uDigbH6nolK2hicLhk23HjEd4=
+ -----END CERTIFICATE-----
diff -rcN postgresql-jdbc-9.1dev-900.src/certdir/README postgresql-jdbc-new/certdir/README
*** postgresql-jdbc-9.1dev-900.src/certdir/README	1970-01-01 01:00:00.000000000 +0100
--- postgresql-jdbc-new/certdir/README	2011-08-23 20:39:42.000000000 +0200
***************
*** 0 ****
--- 1,24 ----
+ 
+ To run the SSL tests, the following properties are used:
+ 
+ certdir: directory where the certificates and keys are store
+ 
+ ssl<TYPE><gh|bh>: a connection string to the appropiate database
+ TYPE is the TYPE or METHOD field from pg_hba.conf that is: host, hostnossl,
+ hostssl and the special types hostsslcert, that corresponds
+ to a hostssl type with clientcert=1 and cert that corresponds
+ to a hostssl type with cert authentication. 'gh' means, the server certificate
+ matches the hostname (good hostname), 'bh' means it is not (bad
+ hostname). It can be simulated with a single database, if two names
+ can be uses i.e. localhost and 127.0.0.1. ssloff points to a database,
+ where ssl is off.
+ 
+ For each connection, the following files should be placed into certdir:
+ goodclient.crt, badclient.crt, goodclient.pk8, badclient.pk8, goodroot.crt, badroot.crt
+ optionally prefixed by the value of ssl<TYPE><gh|bh>prefix property, if
+ different files are necessary for different connect strings.
+ 
+ This directory contains example certificates. The subdirectory server
+ contains what shold be copied to the PGDATA directory.
+ 
+ The sslinfo module must be installed into every database.
diff -rcN postgresql-jdbc-9.1dev-900.src/certdir/server/pg_hba.conf postgresql-jdbc-new/certdir/server/pg_hba.conf
*** postgresql-jdbc-9.1dev-900.src/certdir/server/pg_hba.conf	1970-01-01 01:00:00.000000000 +0100
--- postgresql-jdbc-new/certdir/server/pg_hba.conf	2011-08-20 23:39:48.000000000 +0200
***************
*** 0 ****
--- 1,82 ----
+ # PostgreSQL Client Authentication Configuration File
+ # ===================================================
+ #
+ # Refer to the "Client Authentication" section in the
+ # PostgreSQL documentation for a complete description
+ # of this file.  A short synopsis follows.
+ #
+ # This file controls: which hosts are allowed to connect, how clients
+ # are authenticated, which PostgreSQL user names they can use, which
+ # databases they can access.  Records take one of these forms:
+ #
+ # local      DATABASE  USER  METHOD  [OPTIONS]
+ # host       DATABASE  USER  CIDR-ADDRESS  METHOD  [OPTIONS]
+ # hostssl    DATABASE  USER  CIDR-ADDRESS  METHOD  [OPTIONS]
+ # hostnossl  DATABASE  USER  CIDR-ADDRESS  METHOD  [OPTIONS]
+ #
+ # (The uppercase items must be replaced by actual values.)
+ #
+ # The first field is the connection type: "local" is a Unix-domain socket,
+ # "host" is either a plain or SSL-encrypted TCP/IP socket, "hostssl" is an
+ # SSL-encrypted TCP/IP socket, and "hostnossl" is a plain TCP/IP socket.
+ #
+ # DATABASE can be "all", "sameuser", "samerole", a database name, or
+ # a comma-separated list thereof.
+ #
+ # USER can be "all", a user name, a group name prefixed with "+", or
+ # a comma-separated list thereof.  In both the DATABASE and USER fields
+ # you can also write a file name prefixed with "@" to include names from
+ # a separate file.
+ #
+ # CIDR-ADDRESS specifies the set of hosts the record matches.
+ # It is made up of an IP address and a CIDR mask that is an integer
+ # (between 0 and 32 (IPv4) or 128 (IPv6) inclusive) that specifies
+ # the number of significant bits in the mask.  Alternatively, you can write
+ # an IP address and netmask in separate columns to specify the set of hosts.
+ #
+ # METHOD can be "trust", "reject", "md5", "password", "gss", "sspi", "krb5",
+ # "ident", "pam", "ldap" or "cert".  Note that "password" sends passwords
+ # in clear text; "md5" is preferred since it sends encrypted passwords.
+ #
+ # OPTIONS are a set of options for the authentication in the format
+ # NAME=VALUE. The available options depend on the different authentication
+ # methods - refer to the "Client Authentication" section in the documentation
+ # for a list of which options are available for which authentication methods.
+ #
+ # Database and user names containing spaces, commas, quotes and other special
+ # characters must be quoted. Quoting one of the keywords "all", "sameuser" or
+ # "samerole" makes the name lose its special character, and just match a
+ # database or username with that name.
+ #
+ # This file is read on server startup and when the postmaster receives
+ # a SIGHUP signal.  If you edit the file on a running system, you have
+ # to SIGHUP the postmaster for the changes to take effect.  You can use
+ # "pg_ctl reload" to do that.
+ 
+ # Put your actual configuration here
+ # ----------------------------------
+ #
+ # If you want to allow non-local connections, you need to add more
+ # "host" records. In that case you will also need to make PostgreSQL listen
+ # on a non-local interface via the listen_addresses configuration parameter,
+ # or via the -i or -h command line switches.
+ #
+ 
+ # CAUTION: Configuring the system for local "trust" authentication allows
+ # any local user to connect as any PostgreSQL user, including the database
+ # superuser. If you do not trust all your local users, use another
+ # authentication method.
+ 
+ 
+ # TYPE  DATABASE    USER        CIDR-ADDRESS          METHOD
+ 
+ # "local" is for Unix domain socket connections only
+ local   all         all                               ident
+ # IPv4 local connections:
+ host    hostdb         all         127.0.0.1/32          md5
+ hostnossl    hostnossldb         all         127.0.0.1/32          md5
+ hostssl    hostssldb         all         127.0.0.1/32          md5    clientcert=0
+ hostssl    hostsslcertdb         all         127.0.0.1/32          md5    clientcert=1
+ hostssl    certdb         all         127.0.0.1/32          cert
+ # IPv6 local connections:
+ host    all         all         ::1/128               md5
diff -rcN postgresql-jdbc-9.1dev-900.src/certdir/server/root.crt postgresql-jdbc-new/certdir/server/root.crt
*** postgresql-jdbc-9.1dev-900.src/certdir/server/root.crt	1970-01-01 01:00:00.000000000 +0100
--- postgresql-jdbc-new/certdir/server/root.crt	2011-08-20 22:34:23.000000000 +0200
***************
*** 0 ****
--- 1,13 ----
+ -----BEGIN CERTIFICATE-----
+ MIICDDCCAXWgAwIBAgIJANGTGuCgJYbrMA0GCSqGSIb3DQEBBQUAMA8xDTALBgNV
+ BAMTBHRlc3QwHhcNMTEwODIwMjAzNDIzWhcNMTEwOTE5MjAzNDIzWjAPMQ0wCwYD
+ VQQDEwR0ZXN0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCU+q7HRCdhVcz
+ X8eDQB54L7wX+oys9baMkcGta4UM6r13/Bbftc7RHv/m74b7hJQLWtkCMLWujA1B
+ e+VfoaW1j3czl4De3djyZkkJmDYTJh+zWT+5E8kFxcMbMHJGlL+ip3mb+QwF4KIa
+ qdmm2LON7DxRfOof1n+B8GOcXvhfCwIDAQABo3AwbjAdBgNVHQ4EFgQUGTXV7eYC
+ h93Fzuq6vIq/of5Mg54wPwYDVR0jBDgwNoAUGTXV7eYCh93Fzuq6vIq/of5Mg56h
+ E6QRMA8xDTALBgNVBAMTBHRlc3SCCQDRkxrgoCWG6zAMBgNVHRMEBTADAQH/MA0G
+ CSqGSIb3DQEBBQUAA4GBAA001m1BXDTYz7tNJRULMk2NNO99Ls9bAGJLFL/wfyCK
+ EDEPKDmcS1Y9mzulb6w5DiR8PCK2a7K6oFVOg+qD5ny8WKKFZDuJjvvwRHU1P+sk
+ hOux0CooLOo3U3S76wgrU4vDYI07Pmxty7JfI5GKt1yCaW4Yg3r7ZOhIdH0zlcO9
+ -----END CERTIFICATE-----
diff -rcN postgresql-jdbc-9.1dev-900.src/certdir/server/server.crt postgresql-jdbc-new/certdir/server/server.crt
*** postgresql-jdbc-9.1dev-900.src/certdir/server/server.crt	1970-01-01 01:00:00.000000000 +0100
--- postgresql-jdbc-new/certdir/server/server.crt	2011-08-20 21:23:42.000000000 +0200
***************
*** 0 ****
--- 1,62 ----
+ Certificate:
+     Data:
+         Version: 3 (0x2)
+         Serial Number:
+             a5:13:09:37:af:d9:56:a2
+         Signature Algorithm: sha1WithRSAEncryption
+         Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=localhost
+         Validity
+             Not Before: Aug 20 19:23:41 2011 GMT
+             Not After : Sep 19 19:23:42 2011 GMT
+         Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=localhost
+         Subject Public Key Info:
+             Public Key Algorithm: rsaEncryption
+             RSA Public Key: (1024 bit)
+                 Modulus (1024 bit):
+                     00:c8:d1:4c:9d:62:d8:25:23:e0:4c:7b:40:89:c5:
+                     a9:53:e7:ef:c3:ab:57:ab:3f:62:55:c1:a8:2f:ce:
+                     be:14:c8:cf:f1:a3:54:bc:b6:c6:48:7b:12:77:d3:
+                     9c:f4:28:2e:29:d3:0c:84:98:c7:bf:6f:c8:f9:c0:
+                     1c:b2:f3:1c:03:10:00:a3:ae:e2:45:c5:30:d7:8b:
+                     ce:89:07:ed:9a:88:38:d9:52:ce:37:7d:96:cd:3a:
+                     21:ce:24:a1:d1:32:79:89:67:84:9b:54:2b:72:97:
+                     fa:5c:0a:f7:a1:cb:0b:9a:4e:f3:fe:b2:03:2a:cd:
+                     ea:a9:5d:ef:57:6c:92:41:bd
+                 Exponent: 65537 (0x10001)
+         X509v3 extensions:
+             X509v3 Subject Key Identifier: 
+                 71:7B:6E:73:18:03:71:EA:C7:9F:D0:EE:19:70:18:48:5B:F7:79:9E
+             X509v3 Authority Key Identifier: 
+                 keyid:71:7B:6E:73:18:03:71:EA:C7:9F:D0:EE:19:70:18:48:5B:F7:79:9E
+                 DirName:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=localhost
+                 serial:A5:13:09:37:AF:D9:56:A2
+ 
+             X509v3 Basic Constraints: 
+                 CA:TRUE
+     Signature Algorithm: sha1WithRSAEncryption
+         1c:a5:b7:0f:7d:a4:1a:a8:b5:8c:c8:26:1a:14:97:7f:d7:26:
+         81:7a:40:a7:84:7f:cb:59:18:0e:3e:04:2a:d2:f1:ef:e0:ba:
+         2e:c1:57:11:9e:20:9c:74:9d:55:c9:a8:56:41:ca:a5:f5:0b:
+         f3:f8:1e:3f:ac:aa:fb:01:c0:47:36:3b:d2:85:60:e4:e4:df:
+         6d:b0:97:e2:1c:4c:8c:06:3a:6b:3e:59:ef:ee:c8:cb:38:f2:
+         ae:76:0a:72:5c:59:2a:2b:48:7c:7a:14:e6:ef:44:72:52:23:
+         e3:bb:83:8a:06:c7:ea:7a:25:2b:68:62:70:b8:64:db:71:e3:
+         11:de
+ -----BEGIN CERTIFICATE-----
+ MIIC7zCCAligAwIBAgIJAKUTCTev2VaiMA0GCSqGSIb3DQEBBQUAMFkxCzAJBgNV
+ BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
+ aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xMTA4MjAxOTIz
+ NDFaFw0xMTA5MTkxOTIzNDJaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21l
+ LVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV
+ BAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAyNFMnWLY
+ JSPgTHtAicWpU+fvw6tXqz9iVcGoL86+FMjP8aNUvLbGSHsSd9Oc9CguKdMMhJjH
+ v2/I+cAcsvMcAxAAo67iRcUw14vOiQftmog42VLON32WzTohziSh0TJ5iWeEm1Qr
+ cpf6XAr3ocsLmk7z/rIDKs3qqV3vV2ySQb0CAwEAAaOBvjCBuzAdBgNVHQ4EFgQU
+ cXtucxgDcerHn9DuGXAYSFv3eZ4wgYsGA1UdIwSBgzCBgIAUcXtucxgDcerHn9Du
+ GXAYSFv3eZ6hXaRbMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRl
+ MSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMTCWxv
+ Y2FsaG9zdIIJAKUTCTev2VaiMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
+ gYEAHKW3D32kGqi1jMgmGhSXf9cmgXpAp4R/y1kYDj4EKtLx7+C6LsFXEZ4gnHSd
+ VcmoVkHKpfUL8/geP6yq+wHARzY70oVg5OTfbbCX4hxMjAY6az5Z7+7IyzjyrnYK
+ clxZKitIfHoU5u9EclIj47uDigbH6nolK2hicLhk23HjEd4=
+ -----END CERTIFICATE-----
diff -rcN postgresql-jdbc-9.1dev-900.src/certdir/server/server.key postgresql-jdbc-new/certdir/server/server.key
*** postgresql-jdbc-9.1dev-900.src/certdir/server/server.key	1970-01-01 01:00:00.000000000 +0100
--- postgresql-jdbc-new/certdir/server/server.key	2011-08-20 21:23:23.000000000 +0200
***************
*** 0 ****
--- 1,15 ----
+ -----BEGIN RSA PRIVATE KEY-----
+ MIICXgIBAAKBgQDI0UydYtglI+BMe0CJxalT5+/Dq1erP2JVwagvzr4UyM/xo1S8
+ tsZIexJ305z0KC4p0wyEmMe/b8j5wByy8xwDEACjruJFxTDXi86JB+2aiDjZUs43
+ fZbNOiHOJKHRMnmJZ4SbVCtyl/pcCvehywuaTvP+sgMqzeqpXe9XbJJBvQIDAQAB
+ AoGBAKcEO74h+TZaci/pyjFLbZrVKboJBS7t1nEQUTsBrWFH6npDpzQUpzCc+y0h
+ mEYQLOSoHJ8jGe4DoJjmyHekbcQ9IV49dHX1vXHIEX2Qk9PgFXQGxaAoodV8WfTw
+ 3xE3YNFS8gjozBRhY9P/Y9NBRh6KOjRiJtnt9epXWI4aU3HZAkEA47gmxsBps6i8
+ ofPeEIAhbB2QF9qoujGHoBI8NwbtLKBWlBrxlmcGn4FrVGraO/qDYI/9E72LWnth
+ sAGgBBFzLwJBAOHB3SEoG+o2o0yoc732FubNRdj6rtTSDDAm9CRs3MJ1pxllpLFt
+ 08oklJq/ANqTB7sghVTpod7guTGG2epoTtMCQQCxApFpx2Gi3NQcU9NXIjJYtzYt
+ xLOJYDzbqbES2Rh9qvkB0VvfnKAiVtiWWyTeoKGE6wg/UwRCxkuuI+U9pE7TAkBo
+ JsM418NBI6Hk7NYw7kaUhmFSAB0GbD1ZPa9m2p5iTFsh+WWowa1y1buFRNdE/44S
+ g6dfjEW0iGKH+dmxLhwnAkEAwJ6Hr9JiWddoHpqepGFwxlDjCU/a7mFWu6OOZtsp
+ e0kTBP4QdEqcKFRt6HI+ND7Rffum7AgS6pIKry3907RGYw==
+ -----END RSA PRIVATE KEY-----
diff -rcN postgresql-jdbc-9.1dev-900.src/org/postgresql/core/v2/ConnectionFactoryImpl.java postgresql-jdbc-new/org/postgresql/core/v2/ConnectionFactoryImpl.java
*** postgresql-jdbc-9.1dev-900.src/org/postgresql/core/v2/ConnectionFactoryImpl.java	2011-04-19 03:15:21.000000000 +0200
--- postgresql-jdbc-new/org/postgresql/core/v2/ConnectionFactoryImpl.java	2011-08-23 17:48:29.000000000 +0200
***************
*** 42,49 ****
      public ProtocolConnection openConnectionImpl(String host, int port, String user, String database, Properties info, Logger logger) throws SQLException {
          // Extract interesting values from the info properties:
          //  - the SSL setting
!         boolean requireSSL = (info.getProperty("ssl") != null);
!         boolean trySSL = requireSSL; // XXX temporary until we revisit the ssl property values
  
          //  - the TCP keep alive setting
          boolean requireTCPKeepAlive = (Boolean.valueOf(info.getProperty("tcpKeepAlive")).booleanValue());
--- 42,69 ----
      public ProtocolConnection openConnectionImpl(String host, int port, String user, String database, Properties info, Logger logger) throws SQLException {
          // Extract interesting values from the info properties:
          //  - the SSL setting
!         boolean requireSSL;
!         boolean trySSL;
!         String sslmode = info.getProperty("sslmode");
!         if (sslmode==null)
!         { //Fall back to the ssl property
!           requireSSL = trySSL  = (info.getProperty("ssl") != null);
!         } else {
!           if ("disable".equals(sslmode))
!           {
!             requireSSL = trySSL = false;
!           } else if ("allow".equals(sslmode) || "prefer".equals(sslmode))
!           {  
!             //XXX Allow and prefer are treated the same way
!             requireSSL = false;
!             trySSL = true;
!           } else if ("require".equals(sslmode) || "verify-ca".equals(sslmode) || "verify-full".equals(sslmode))
!           {
!             requireSSL = trySSL = true;
!           } else {
!             throw new PSQLException (GT.tr("Invalid sslmode value: {0}", sslmode), PSQLState.CONNECTION_UNABLE_TO_CONNECT);
!           }
!         }
  
          //  - the TCP keep alive setting
          boolean requireTCPKeepAlive = (Boolean.valueOf(info.getProperty("tcpKeepAlive")).booleanValue());
diff -rcN postgresql-jdbc-9.1dev-900.src/org/postgresql/core/v3/ConnectionFactoryImpl.java postgresql-jdbc-new/org/postgresql/core/v3/ConnectionFactoryImpl.java
*** postgresql-jdbc-9.1dev-900.src/org/postgresql/core/v3/ConnectionFactoryImpl.java	2011-04-19 03:15:21.000000000 +0200
--- postgresql-jdbc-new/org/postgresql/core/v3/ConnectionFactoryImpl.java	2011-08-17 22:21:01.000000000 +0200
***************
*** 50,57 ****
      public ProtocolConnection openConnectionImpl(String host, int port, String user, String database, Properties info, Logger logger) throws SQLException {
          // Extract interesting values from the info properties:
          //  - the SSL setting
!         boolean requireSSL = (info.getProperty("ssl") != null);
!         boolean trySSL = requireSSL; // XXX temporary until we revisit the ssl property values
  
          //  - the TCP keep alive setting
          boolean requireTCPKeepAlive = (Boolean.valueOf(info.getProperty("tcpKeepAlive")).booleanValue());
--- 50,77 ----
      public ProtocolConnection openConnectionImpl(String host, int port, String user, String database, Properties info, Logger logger) throws SQLException {
          // Extract interesting values from the info properties:
          //  - the SSL setting
!         boolean requireSSL;
!         boolean trySSL;
!         String sslmode = info.getProperty("sslmode");
!         if (sslmode==null)
!         { //Fall back to the ssl property
!           requireSSL = trySSL  = (info.getProperty("ssl") != null);
!         } else {
!           if ("disable".equals(sslmode))
!           {
!             requireSSL = trySSL = false;
!           } else if ("allow".equals(sslmode) || "prefer".equals(sslmode))
!           {  
!             //XXX Allow and prefer are treated the same way
!             requireSSL = false;
!             trySSL = true;
!           } else if ("require".equals(sslmode) || "verify-ca".equals(sslmode) || "verify-full".equals(sslmode))
!           {
!             requireSSL = trySSL = true;
!           } else {
!             throw new PSQLException (GT.tr("Invalid sslmode value: {0}", sslmode), PSQLState.CONNECTION_UNABLE_TO_CONNECT);
!           }
!         }
  
          //  - the TCP keep alive setting
          boolean requireTCPKeepAlive = (Boolean.valueOf(info.getProperty("tcpKeepAlive")).booleanValue());
diff -rcN postgresql-jdbc-9.1dev-900.src/org/postgresql/ssl/LazyKeyManager.java postgresql-jdbc-new/org/postgresql/ssl/LazyKeyManager.java
*** postgresql-jdbc-9.1dev-900.src/org/postgresql/ssl/LazyKeyManager.java	1970-01-01 01:00:00.000000000 +0100
--- postgresql-jdbc-new/org/postgresql/ssl/LazyKeyManager.java	2011-08-23 20:10:53.000000000 +0200
***************
*** 0 ****
--- 1,253 ----
+ package org.postgresql.ssl;
+ 
+ import java.io.File;
+ import java.io.FileInputStream;
+ import java.io.FileNotFoundException;
+ import java.io.IOException;
+ import java.net.Socket;
+ import java.security.AlgorithmParameters;
+ import java.security.GeneralSecurityException;
+ import java.security.Key;
+ import java.security.KeyFactory;
+ import java.security.NoSuchAlgorithmException;
+ import java.security.Principal;
+ import java.security.PrivateKey;
+ import java.security.cert.CertificateException;
+ import java.security.cert.CertificateFactory;
+ import java.security.cert.X509Certificate;
+ import java.security.spec.InvalidKeySpecException;
+ import java.security.spec.KeySpec;
+ import java.security.spec.PKCS8EncodedKeySpec;
+ import java.util.Collection;
+ 
+ import javax.crypto.Cipher;
+ import javax.crypto.EncryptedPrivateKeyInfo;
+ import javax.crypto.NoSuchPaddingException;
+ import javax.crypto.SecretKeyFactory;
+ import javax.crypto.spec.PBEKeySpec;
+ import javax.net.ssl.X509KeyManager;
+ import javax.security.auth.callback.Callback;
+ import javax.security.auth.callback.CallbackHandler;
+ import javax.security.auth.callback.PasswordCallback;
+ import javax.security.auth.callback.UnsupportedCallbackException;
+ import javax.security.auth.x500.X500Principal;
+ 
+ import org.postgresql.util.GT;
+ import org.postgresql.util.PSQLException;
+ import org.postgresql.util.PSQLState;
+ 
+ /**
+  * A Key manager that only loads the keys, if necessary.
+  *
+  */
+ public class LazyKeyManager implements X509KeyManager {
+   private X509Certificate[] cert = null;
+   private PrivateKey key = null;
+   private String certfile;
+   private String keyfile;
+   private CallbackHandler cbh;
+   private boolean defaultfile;
+   private PSQLException error = null;
+   
+   /**
+    * Costructor. certfile and keyfile can be null, in that case no
+    * certificat is presented to the server.
+    * @param certfile
+    * @param keyfile
+    * @param cbh
+    * @param defaultfile
+    */
+   public LazyKeyManager(String certfile, String keyfile, CallbackHandler cbh, boolean defaultfile) {
+     this.certfile = certfile;
+     this.keyfile = keyfile;
+     this.cbh = cbh;
+     this.defaultfile = defaultfile;
+   }
+   
+   /**
+    * getCertificateChain and getPrivateKey cannot throw exeptions,
+    * therefore any exception is stored in this.error and can be raised
+    * by this method
+    * @throws PSQLException
+    */
+   public void throwKeyManagerException() throws PSQLException
+   {
+     if (error!=null) throw error;
+   }
+   
+   @Override
+   public String chooseClientAlias(String[] keyType, Principal[] issuers,
+       Socket socket) {
+     if (certfile==null)
+     {
+       return null;
+     } else {
+       if (issuers==null || issuers.length==0)
+       { //Postgres 8.4 and earlier do not send the list of accepted certificate authorities
+         //to the client. See BUG #5468. We only hope, that our certificate will be accepted.
+         return "user";
+       } else { //Sending a wrong certificate makes the connection rejected, even, if clientcert=0 in pg_hba.conf.
+         //therefore we only send our certificate, if the issuer is listed in issuers
+         X509Certificate[] certchain = getCertificateChain("user");
+         if (certchain==null)
+         {
+           return null;
+         } else {
+           X500Principal ourissuer = certchain[certchain.length-1].getIssuerX500Principal();
+           boolean found = false;
+           for (int i=0; i<issuers.length; i++)
+           {
+             if (ourissuer.equals(issuers[i])) { found = true;}
+           }
+           return (found ? "user" : null);
+         }
+       }
+     }
+   }
+ 
+   @Override
+   public String chooseServerAlias(String keyType, Principal[] issuers,
+       Socket socket) {
+     return null; //We are not a server
+   }
+ 
+   @Override
+   public X509Certificate[] getCertificateChain(String alias) {
+     if (cert==null && certfile!=null) //If certfile is null, we do not load the certificate
+     { //The certificate must be loaded
+       CertificateFactory cf;
+       try
+       {
+         cf = CertificateFactory.getInstance("X.509");
+       } catch (CertificateException ex)
+       { //For some strange reason it throws CertificateException instead of NoSuchAlgorithmException...
+         error = new PSQLException(GT.tr("Could not find a java cryptographic algorithm: X.509 CertificateFactory not available.", null), PSQLState.CONNECTION_FAILURE, ex);
+         return null;
+       }
+       Collection certs;
+       try
+       {
+         certs = cf.generateCertificates(new FileInputStream(certfile));
+       } catch (FileNotFoundException ioex)
+       {
+         if (!defaultfile)
+         { //It is not an error if there is no file at the default location
+           error = new PSQLException(GT.tr("Could not open SSL certificate file {0}.", new Object[]{certfile}), PSQLState.CONNECTION_FAILURE, ioex);
+         }
+         return null;
+       }catch (CertificateException gsex)
+       {
+         error = new PSQLException(GT.tr("Loading the SSL certificate {0} into a KeyManager failed.", new Object[]{certfile}), PSQLState.CONNECTION_FAILURE, gsex);
+         return null;
+       } 
+       cert = (X509Certificate[]) certs.toArray(new X509Certificate[certs.size()]);
+     }    
+     return cert;
+   }
+ 
+   @Override
+   public String[] getClientAliases(String keyType, Principal[] issuers) {
+     String alias = chooseClientAlias(new String[]{keyType}, issuers, (Socket)null);
+     return (alias==null ? new String[]{} : new String[]{alias});
+   }
+ 
+   @Override
+   public PrivateKey getPrivateKey(String alias) {
+     try
+     {
+       if (key==null && keyfile!=null) //If keyfile is null, we do not load the key
+       { //The private key must be loaded
+         if (cert==null)
+         { //We need the certificate for the algorithm
+           if(getCertificateChain("user")==null)
+             return null; //getCertificateChain failed...
+         }
+         File keyf = new File(keyfile);
+         FileInputStream fl;
+         try
+         {
+           fl = new FileInputStream(keyfile);
+         }
+         catch (FileNotFoundException ex)
+         {
+           if (!defaultfile)
+           { //It is not an error if there is no file at the default location
+             throw ex;
+           }
+           return null;      
+         }
+         byte[] keydata = new byte[(int)keyf.length()];
+         fl.read ( keydata, 0, (int)keyf.length() );
+         fl.close();
+         KeyFactory kf = KeyFactory.getInstance(cert[0].getPublicKey().getAlgorithm());
+         try {
+           KeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec (keydata);
+           key = kf.generatePrivate (pkcs8KeySpec);
+         }
+         catch (InvalidKeySpecException ex) //The key might be password protected
+         {
+           EncryptedPrivateKeyInfo ePKInfo = new EncryptedPrivateKeyInfo(keydata);
+           Cipher cipher;
+           try
+           {
+             cipher = Cipher.getInstance(ePKInfo.getAlgName());
+           } catch (NoSuchPaddingException npex)
+           { //Why is it not a subclass of NoSuchAlgorithmException?
+             throw new NoSuchAlgorithmException(npex.getMessage(),npex);
+           }
+           //We call back for the password
+           PasswordCallback pwdcb = new PasswordCallback(GT.tr("Enter SSL password: "), false);
+           try
+           {
+             cbh.handle(new Callback[]{pwdcb});
+           } catch (UnsupportedCallbackException ucex)
+           {
+             if ((cbh instanceof LibPQFactory.ConsoleCallbackHandler) && ("Console is not available".equals(ucex.getMessage())))
+             {
+               error = new PSQLException(GT.tr("Could not read password for SSL key file, console is not available.", null), PSQLState.CONNECTION_FAILURE, ucex);
+             } else {
+               error = new PSQLException(GT.tr("Could not read password for SSL key file by callbackhandler {0}.", new Object[]{cbh.getClass().getName()}), PSQLState.CONNECTION_FAILURE, ucex); 
+             }
+             return null;
+           }
+           try
+           {
+             PBEKeySpec pbeKeySpec = new PBEKeySpec(pwdcb.getPassword());
+             // Now create the Key from the PBEKeySpec
+             SecretKeyFactory skFac = SecretKeyFactory.getInstance(ePKInfo.getAlgName());
+             Key pbeKey = skFac.generateSecret(pbeKeySpec);
+             // Extract the iteration count and the salt
+             AlgorithmParameters algParams = ePKInfo.getAlgParameters();
+             cipher.init(Cipher.DECRYPT_MODE, pbeKey, algParams);
+             // Decrypt the encryped private key into a PKCS8EncodedKeySpec
+             KeySpec pkcs8KeySpec = ePKInfo.getKeySpec(cipher);
+             key = kf.generatePrivate (pkcs8KeySpec);
+           }
+           catch (GeneralSecurityException ikex)
+           {
+             error = new PSQLException(GT.tr("Could not decrypt SSL key file {0}.", new Object[]{keyfile}), PSQLState.CONNECTION_FAILURE, ikex);
+             return null;      
+           }         
+         }
+       }
+     }
+     catch (IOException ioex)
+     {
+       error = new PSQLException(GT.tr("Could not read SSL key file {0}.", new Object[]{keyfile}), PSQLState.CONNECTION_FAILURE, ioex);
+     }
+     catch(NoSuchAlgorithmException ex)
+     {
+       error = new PSQLException(GT.tr("Could not find a java cryptographic algorithm: {0}.", new Object[]{ex.getMessage()}), PSQLState.CONNECTION_FAILURE, ex);
+       return null;      
+     }
+ 
+     return key;
+   }
+ 
+   @Override
+   public String[] getServerAliases(String keyType, Principal[] issuers) {
+     return new String[]{};
+   }
+   
+ 
+ }
diff -rcN postgresql-jdbc-9.1dev-900.src/org/postgresql/ssl/LibPQFactory.java postgresql-jdbc-new/org/postgresql/ssl/LibPQFactory.java
*** postgresql-jdbc-9.1dev-900.src/org/postgresql/ssl/LibPQFactory.java	1970-01-01 01:00:00.000000000 +0100
--- postgresql-jdbc-new/org/postgresql/ssl/LibPQFactory.java	2011-08-23 20:47:47.000000000 +0200
***************
*** 0 ****
--- 1,303 ----
+ package org.postgresql.ssl;
+ 
+ import java.io.Console;
+ import java.io.File;
+ import java.io.FileInputStream;
+ import java.io.FileNotFoundException;
+ import java.io.IOException;
+ import java.net.Socket;
+ import java.security.AlgorithmParameters;
+ import java.security.GeneralSecurityException;
+ import java.security.Key;
+ import java.security.KeyFactory;
+ import java.security.KeyManagementException;
+ import java.security.KeyStore;
+ import java.security.KeyStoreException;
+ import java.security.NoSuchAlgorithmException;
+ import java.security.PrivateKey;
+ import java.security.cert.Certificate;
+ import java.security.cert.CertificateException;
+ import java.security.cert.CertificateFactory;
+ import java.security.cert.X509Certificate;
+ import java.security.spec.KeySpec;
+ import java.security.spec.PKCS8EncodedKeySpec;
+ import java.util.Collection;
+ import java.util.Properties;
+ 
+ import javax.crypto.Cipher;
+ import javax.crypto.EncryptedPrivateKeyInfo;
+ import javax.crypto.SecretKeyFactory;
+ import javax.crypto.spec.PBEKeySpec;
+ import javax.naming.InvalidNameException;
+ import javax.naming.ldap.LdapName;
+ import javax.naming.ldap.Rdn;
+ import javax.net.ssl.HostnameVerifier;
+ import javax.net.ssl.KeyManager;
+ import javax.net.ssl.SSLContext;
+ import javax.net.ssl.SSLPeerUnverifiedException;
+ import javax.net.ssl.SSLSession;
+ import javax.net.ssl.SSLSocket;
+ import javax.net.ssl.SSLSocketFactory;
+ import javax.net.ssl.TrustManager;
+ import javax.net.ssl.TrustManagerFactory;
+ import javax.security.auth.callback.Callback;
+ import javax.security.auth.callback.CallbackHandler;
+ import javax.security.auth.callback.PasswordCallback;
+ import javax.security.auth.callback.UnsupportedCallbackException;
+ import javax.security.auth.x500.X500Principal;
+ 
+ import org.postgresql.ssl.NonValidatingFactory.NonValidatingTM;
+ import org.postgresql.util.GT;
+ import org.postgresql.util.PSQLException;
+ import org.postgresql.util.PSQLState;
+ 
+ /**
+  * Provide an SSLSocketFactory that is compatible with the libpq
+  * behaviour.
+  */
+ public class LibPQFactory extends WrappedFactory implements HostnameVerifier {
+ 
+     LazyKeyManager km = null;
+     String sslmode; 
+     /**
+      * 
+      * @param info the connection parameters
+      * The following parameters are used:
+      * sslmode,sslcert,sslkey,sslrootcert,sslhostnameverifier,sslpasswordcallback,sslpassword
+      * @throws GeneralSecurityException
+      * @throws IOException 
+      * @throws UnsupportedCallbackException 
+      */
+     public LibPQFactory(Properties info) throws PSQLException {
+       try
+       {
+         sslmode = info.getProperty("sslmode");
+         SSLContext ctx = SSLContext.getInstance("TLS"); // or "SSL" ?
+         
+         //Determinig the default file location
+         String pathsep = System.getProperty("path.separator");
+         String defaultdir;
+         boolean defaultfile = false;
+         if (System.getProperty("os.name").toLowerCase().indexOf("windows") > -1)
+         { //It is Windows
+           defaultdir = System.getenv("APPDATA")+pathsep+"postgresql"+pathsep;
+         } else {
+           defaultdir = System.getProperty("user.home")+pathsep+".postgresql"+pathsep;
+         }
+         
+         //Load the client's certificate and key
+         String sslcertfile = info.getProperty("sslcert");
+         if (sslcertfile == null)
+         { //Fall back to default
+           defaultfile = true;
+           sslcertfile = defaultdir + "postgresql.crt";
+         }
+         String sslkeyfile = info.getProperty("sslkey");
+         if (sslkeyfile == null)
+         { //Fall back to default
+           defaultfile = true;
+           sslkeyfile = defaultdir + "postgresql.pk8";
+         }
+         
+         //Determine the callback handler
+         CallbackHandler cbh;
+         String sslpasswordcallback = info.getProperty("sslpasswordcallback");
+         if (sslpasswordcallback != null)
+         {
+           try
+           {
+             cbh = (CallbackHandler)MakeSSL.instantiate(sslpasswordcallback, info, false, null);
+           }
+           catch (Exception e)
+           {
+               throw new PSQLException(GT.tr("The password callback class provided {0} could not be instantiated.", sslpasswordcallback), PSQLState.CONNECTION_FAILURE, e);
+           }
+         } else {
+           cbh = new ConsoleCallbackHandler(info.getProperty("sslpassword"));
+         }
+          
+         //If the properies are empty, give null to prevent client key selection
+         km = new LazyKeyManager(("".equals(sslcertfile) ? null : sslcertfile), ("".equals(sslkeyfile) ? null : sslkeyfile), cbh, defaultfile);
+         
+         TrustManager[] tm;    
+         if("verify-ca".equals(sslmode) || "verify-full".equals(sslmode))
+         {
+           //Load the server certificate
+           
+           TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
+           KeyStore ks;
+           try
+           {
+             ks = KeyStore.getInstance("jks");
+           } catch (KeyStoreException e)
+           {
+             throw new NoSuchAlgorithmException("jks KeyStore not available"); //this should never happen
+           }
+           String sslrootcertfile = info.getProperty("sslrootcert");
+           if (sslrootcertfile == null)
+           { //Fall back to default
+             sslrootcertfile = defaultdir + "root.crt";
+           }
+           FileInputStream fis;
+           try
+           {
+             fis = new FileInputStream(sslrootcertfile);
+           } catch (FileNotFoundException ex)
+           {
+             throw new PSQLException(GT.tr("Could not open SSL root certificate file {0}.", new Object[]{sslrootcertfile}), PSQLState.CONNECTION_FAILURE, ex);
+           }
+           try
+           {
+             CertificateFactory cf = CertificateFactory.getInstance("X.509");
+             Certificate[] certs = cf.generateCertificates(fis).toArray(new Certificate[]{});
+             fis.close();
+             ks.load(null, null);
+             for(int i=0; i<certs.length; i++)
+             {
+               ks.setCertificateEntry("cert"+i, certs[i]);
+             }
+             tmf.init(ks);
+           }
+           catch (IOException ioex)
+           {
+             throw new PSQLException(GT.tr("Could not read SSL root certificate file {0}.", new Object[]{sslrootcertfile}), PSQLState.CONNECTION_FAILURE, ioex);
+           } catch (GeneralSecurityException gsex)
+           {
+             throw new PSQLException(GT.tr("Loading the SSL root certificate {0} into a TrustManager failed.", new Object[]{sslrootcertfile}), PSQLState.CONNECTION_FAILURE, gsex);
+           } 
+           tm = tmf.getTrustManagers();
+         } else { //server validation is not required
+           tm = new TrustManager[] { new NonValidatingTM() };
+         }
+ 
+         //finally we can initialize the context
+         try
+         {
+           ctx.init(new KeyManager[]{km}, tm, null);
+         } catch (KeyManagementException ex)
+         {
+           throw new PSQLException(GT.tr("Could not initialize SSL context.", null), PSQLState.CONNECTION_FAILURE, ex);
+         }
+ 
+         _factory = ctx.getSocketFactory();
+       }
+       catch(NoSuchAlgorithmException ex)
+       {
+         throw new PSQLException(GT.tr("Could not find a java cryptographic algorithm: {0}.", new Object[]{ex.getMessage()}), PSQLState.CONNECTION_FAILURE, ex);
+       }
+     }
+ 
+     /**
+      * Propagates any exception from LazyKeyManager
+      * @throws PSQLException
+      */
+     public void throwKeyManagerException() throws PSQLException
+     {
+       if (km!=null) km.throwKeyManagerException();
+     }
+     
+     /**
+      * A CallbackHandler that reads the password from the console
+      * or returns the password given to it's constructor.
+      *
+      */
+     class ConsoleCallbackHandler implements CallbackHandler {
+ 
+       private char[] password = null;
+       
+       public ConsoleCallbackHandler(String password)
+       {
+         if (password != null) this.password = password.toCharArray();
+       }
+       
+       /**
+        * Handles the callbacks.
+        * @param callbacks The callbacks to handle
+        * @throws UnsupportedCallbackException If the console is not available
+        * or other than PasswordCallback is supplied
+        */
+       @Override
+       public void handle(Callback[] callbacks) throws IOException,
+           UnsupportedCallbackException {
+         Console cons = System.console();
+         if (cons==null && password==null) {throw new UnsupportedCallbackException(callbacks[0], "Console is not available");}
+         for (int i=0; i<callbacks.length; i++)
+         {
+           if (callbacks[i] instanceof PasswordCallback)
+           {
+             if (password==null)
+             {
+               //It is used instead of cons.readPassword(prompt), because the prompt may contain '%' characters
+               ((PasswordCallback)callbacks[i]).setPassword(cons.readPassword("%s", ((PasswordCallback)callbacks[i]).getPrompt()));
+             } else {
+               ((PasswordCallback)callbacks[i]).setPassword(password);
+             }
+           } else {
+             throw new UnsupportedCallbackException(callbacks[i]);
+           }
+       }
+       
+     }
+   }
+ 
+     /**
+      * Verifies the server certificate according to the libpq rules.
+      * The cn attribute of the certificate is matched against the hostname.
+      * If the cn  attribute starts with an asterisk (*), it will be treated as a wildcard,
+      * and will match all characters except a dot (.). This means the certificate
+      * will not match subdomains. If the connection is made using an IP address instead of a
+      * hostname, the IP address will be matched (without doing any DNS lookups).
+      * @param hostname Hostname or IP address of the server.
+      * @param session The  SSL session.
+      * @returns true if the certificate belongs to the server, false otherwise.
+      */
+     @Override
+     public boolean verify(String hostname, SSLSession session) {
+       X509Certificate[] peerCerts;
+       try
+       {
+         peerCerts = (X509Certificate[])session.getPeerCertificates();
+       } catch (SSLPeerUnverifiedException e)
+       {
+         return false;
+       }
+       if (peerCerts == null || peerCerts.length == 0) {
+         return false;
+       }
+       //Extract the common name
+       X509Certificate serverCert = peerCerts[0];
+       LdapName DN;
+       try
+       {
+         DN = new LdapName(serverCert.getSubjectX500Principal().getName(X500Principal.RFC2253));
+       } catch (InvalidNameException e)
+       {
+         return false; 
+       }
+       String CN = null;
+       for(Rdn rdn : DN.getRdns())
+       {
+         if ("CN".equals(rdn.getType())) //Multiple AVAs are not treated
+         {
+           CN = (String)rdn.getValue();
+           break;
+         }
+       }
+       if (CN == null)
+       {
+         return false;
+       } else if (CN.startsWith("*"))
+       { //We have a wildcard
+         if (hostname.endsWith(CN.substring(1)))
+         { //Avoid IndexOutOfBoundsException because hostname already ends with CN
+           return !(hostname.substring(0, hostname.length()-CN.length()+1).contains("."));
+         } else {
+           return false;
+         }
+       } else {
+         return CN.equals(hostname);
+       }
+     }
+ 
+ }
+ 
diff -rcN postgresql-jdbc-9.1dev-900.src/org/postgresql/ssl/MakeSSL.java postgresql-jdbc-new/org/postgresql/ssl/MakeSSL.java
*** postgresql-jdbc-9.1dev-900.src/org/postgresql/ssl/MakeSSL.java	2008-01-08 07:56:30.000000000 +0100
--- postgresql-jdbc-new/org/postgresql/ssl/MakeSSL.java	2011-08-23 20:19:03.000000000 +0200
***************
*** 9,58 ****
  */
  package org.postgresql.ssl;
  
- import java.util.Properties;
  import java.io.IOException;
- import java.net.Socket;
  import java.lang.reflect.Constructor;
  import javax.net.ssl.SSLSocketFactory;
  
- import org.postgresql.core.PGStream;
  import org.postgresql.core.Logger;
  import org.postgresql.util.GT;
- import org.postgresql.util.PSQLState;
  import org.postgresql.util.PSQLException;
  
  public class MakeSSL {
!     public static void convert(PGStream stream, Properties info, Logger logger) throws IOException, PSQLException {
          logger.debug("converting regular socket connection to ssl");
  
          SSLSocketFactory factory;
  
          // Use the default factory if no specific factory is requested
!         //
          String classname = info.getProperty("sslfactory");
          if (classname == null)
          {
              factory = (SSLSocketFactory)SSLSocketFactory.getDefault();
          }
          else
          {
-             Object[] args = {info.getProperty("sslfactoryarg")};
-             Constructor ctor;
-             Class factoryClass;
- 
              try
              {
!                 factoryClass = Class.forName(classname);
!                 try
!                 {
!                     ctor = factoryClass.getConstructor(new Class[]{String.class});
!                 }
!                 catch (NoSuchMethodException nsme)
!                 {
!                     ctor = factoryClass.getConstructor((Class[])null);
!                     args = null;
!                 }
!                 factory = (SSLSocketFactory)ctor.newInstance(args);
              }
              catch (Exception e)
              {
--- 9,112 ----
  */
  package org.postgresql.ssl;
  
  import java.io.IOException;
  import java.lang.reflect.Constructor;
+ import java.lang.reflect.InvocationTargetException;
+ import java.util.Properties;
+ 
+ import javax.net.ssl.HostnameVerifier;
+ import javax.net.ssl.SSLSession;
+ import javax.net.ssl.SSLSocket;
  import javax.net.ssl.SSLSocketFactory;
  
  import org.postgresql.core.Logger;
+ import org.postgresql.core.PGStream;
  import org.postgresql.util.GT;
  import org.postgresql.util.PSQLException;
+ import org.postgresql.util.PSQLState;
  
  public class MakeSSL {
!   
!     /**
!      * Instantiates a class using the appropriate constructor.
!      * If a constructor with a single Propertiesparameter exists, it is
!      * used. Otherwise, if tryString is true a constructor with
!      * a single String argument is searched if it fails, or tryString is true
!      * a no argument constructor is tried.
!      * @param classname Nam of the class to instantiate
!      * @param info parameter to pass as Properties
!      * @param tryString weather to look for a single String argument constructor
!      * @param stringarg parameter to pass as String
!      * @return the instantiated class
!      * @throws ClassNotFoundException
!      * @throws SecurityException
!      * @throws NoSuchMethodException
!      * @throws IllegalArgumentException
!      * @throws InstantiationException
!      * @throws IllegalAccessException
!      * @throws InvocationTargetException
!      */
!     public static Object instantiate(String classname, Properties info, boolean tryString, String stringarg) 
!         throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException,
!         InstantiationException, IllegalAccessException, InvocationTargetException
!     {
!       Object[] args = {info};
!       Constructor ctor = null;
!       Class cls;
!       cls = Class.forName(classname);
!       try
!       {         
!           ctor = cls.getConstructor(new Class[]{Properties.class});
!       }
!       catch (NoSuchMethodException nsme)
!       {
!         if (tryString)
!         {
!           try
!           {
!               ctor = cls.getConstructor(new Class[]{String.class});
!               args = new String[]{stringarg};
!           }
!           catch (NoSuchMethodException nsme2)
!           {
!             tryString = false;
!           }
!         }
!         if (!tryString)
!         {
!           ctor = cls.getConstructor((Class[])null);
!           args = null;          
!         }
!       }
!       return ctor.newInstance(args);
!     }
!     
!     public static void convert(PGStream stream, Properties info, Logger logger) throws PSQLException, IOException {
          logger.debug("converting regular socket connection to ssl");
  
          SSLSocketFactory factory;
  
+         String sslmode = info.getProperty("sslmode");
          // Use the default factory if no specific factory is requested
!         // unless sslmode is set
          String classname = info.getProperty("sslfactory");
          if (classname == null)
          {
+           //If sslmode is set, use the libpg compatible factory
+           if (sslmode!=null)
+           {
+             factory = new LibPQFactory(info);
+           }
+           else
+           {
              factory = (SSLSocketFactory)SSLSocketFactory.getDefault();
+           }
          }
          else
          {
              try
              {
!                 factory = (SSLSocketFactory)instantiate(classname, info, true, info.getProperty("sslfactoryarg"));
              }
              catch (Exception e)
              {
***************
*** 60,66 ****
              }
          }
  
!         Socket newConnection = factory.createSocket(stream.getSocket(), stream.getHost(), stream.getPort(), true);
          stream.changeSocket(newConnection);
      }
  
--- 114,159 ----
              }
          }
  
!         SSLSocket newConnection;
!         try
!         {
!           newConnection = (SSLSocket)factory.createSocket(stream.getSocket(), stream.getHost(), stream.getPort(), true);
!           newConnection.startHandshake(); //We must invoke manually, otherwise the exceptions are hidden
!         }
!         catch (IOException ex) {
!           if (factory instanceof LibPQFactory)
!           { //throw any KeyManager exception
!             ((LibPQFactory)factory).throwKeyManagerException();
!           }
!           throw new PSQLException(GT.tr("SSL error: {0}", ex.getMessage()), PSQLState.CONNECTION_FAILURE, ex);
!         }
!         
!         String sslhostnameverifier = info.getProperty("sslhostnameverifier");
!         if (sslhostnameverifier!=null)
!         {
!           HostnameVerifier hvn;
!           try
!           {
!             hvn = (HostnameVerifier)instantiate(sslhostnameverifier, info, false, null);
!           }
!           catch (Exception e)
!           {
!               throw new PSQLException(GT.tr("The HostnameVerifier class provided {0} could not be instantiated.", sslhostnameverifier), PSQLState.CONNECTION_FAILURE, e);
!           }
!           if (!hvn.verify(stream.getHost(), newConnection.getSession()))
!           {
!             throw new PSQLException(GT.tr("The hostname {0} could not be verified by hostnameverifier {1}.", new Object[]{stream.getHost(), sslhostnameverifier}), PSQLState.CONNECTION_FAILURE);
!           }
!         } else {
!           if ("verify-full".equals(sslmode) && factory instanceof LibPQFactory)
!           {
!             if (!(((LibPQFactory)factory).verify(stream.getHost(), newConnection.getSession())))
!             {
!               throw new PSQLException(GT.tr("The hostname {0} could not be verified.", stream.getHost()), PSQLState.CONNECTION_FAILURE);
!             }
!           }
! 
!         }
          stream.changeSocket(newConnection);
      }
  
diff -rcN postgresql-jdbc-9.1dev-900.src/org/postgresql/test/ssl/SslTest.java postgresql-jdbc-new/org/postgresql/test/ssl/SslTest.java
*** postgresql-jdbc-9.1dev-900.src/org/postgresql/test/ssl/SslTest.java	1970-01-01 01:00:00.000000000 +0100
--- postgresql-jdbc-new/org/postgresql/test/ssl/SslTest.java	2011-08-23 19:36:21.000000000 +0200
***************
*** 0 ****
--- 1,325 ----
+ package org.postgresql.test.ssl;
+ 
+ import java.sql.Connection;
+ import java.sql.DriverManager;
+ import java.sql.ResultSet;
+ import java.sql.SQLException;
+ import java.util.Map;
+ import java.util.Properties;
+ import java.util.TreeMap;
+ 
+ import junit.framework.TestCase;
+ import junit.framework.TestSuite;
+ 
+ import org.postgresql.test.TestUtil;
+ 
+ public class SslTest extends TestCase {
+ 
+   /**
+    * Tries to connect to the database.
+    * @param connstr Connection string for the database
+    * @param expected Expected values. the first element is a String holding the expected message of PSQLException
+    * or null, if no exception is expected, the second indicates weather ssl is to be used (Boolean)
+    * @throws SQLException
+    */
+   protected void driver(String connstr, Object[] expected) throws SQLException
+   {
+     Connection conn = null;
+     String exmsg = (String)expected[0];
+     try
+     {
+       conn = DriverManager.getConnection(connstr, TestUtil.getUser(), TestUtil.getPassword());
+       if (exmsg!=null)
+       {
+         fail("Exception did not occur: "+exmsg);
+       }
+       //
+       ResultSet rs = conn.createStatement().executeQuery("select ssl_is_used()");
+       assertTrue(rs.next());
+       assertEquals("ssl_is_used: ",((Boolean)expected[1]).booleanValue(), rs.getBoolean(1));
+       conn.close();
+     }
+     catch (SQLException ex) {
+       if (conn!=null) { conn.close(); }
+       if (exmsg==null)
+       { //no exception is excepted
+         fail("Exception thrown: "+ex.getMessage());
+       } else {
+         assertTrue("expected: "+exmsg+" actual: "+ex.getMessage(), ex.getMessage().matches(exmsg));
+         return;
+       }
+     }
+   }
+ 
+   protected String certdir;
+   protected String connstr;
+   protected String sslmode;
+   protected int protocol;
+   protected boolean goodclient;
+   protected boolean goodserver;
+   protected String prefix;
+   protected Object[] expected;
+   
+   private String makeConnStr(String sslmode, boolean goodclient, boolean goodserver, int protocol)
+   {
+     return connstr+"&protocolVersion="+protocol+"&sslmode="+sslmode+
+         "&sslcert="+certdir+"/"+prefix+(goodclient?"goodclient.crt":"badclient.crt")+
+         "&sslkey="+certdir+"/"+prefix+(goodclient?"goodclient.pk8":"badclient.pk8")+
+         "&sslrootcert="+certdir+"/"+prefix+(goodserver?"goodroot.crt":"badroot.crt")+
+         "&loglevel="+TestUtil.getLogLevel();
+   }
+ 
+   public SslTest(String name, String certdir, String connstr, String sslmode, int protocol,  boolean goodclient, boolean goodserver, String prefix, Object[] expected)
+   {
+     super(name);
+     this.certdir = certdir;
+     this.connstr = connstr;
+     this.sslmode = sslmode;
+     this.protocol = protocol;
+     this.goodclient = goodclient;
+     this.goodserver = goodserver;
+     this.prefix = prefix;
+     this.expected = expected;
+   }
+ 
+   static TestSuite getSuite(Properties prop, String param)
+   {
+     String certdir = prop.getProperty("certdir");
+     String sconnstr = prop.getProperty(param);
+     String sprefix = prop.getProperty(param + "prefix");
+     String[] csslmode = {"disable","allow","prefer","require","verify-ca","verify-full"};
+     
+     TestSuite suite = new TestSuite();
+     Map expected = (Map)expectedmap.get(param);
+     if (expected == null) {;expected = defaultexpected;}
+     int j=0;
+     for (int i=0; i<csslmode.length; i++)
+     {
+       suite.addTest(new SslTest(param + "-"+csslmode[i]+"GG2",certdir,sconnstr,csslmode[i],2,true, true,sprefix,(Object[])expected.get(csslmode[i]+"GG")));
+       suite.addTest(new SslTest(param + "-"+csslmode[i]+"GG3",certdir,sconnstr,csslmode[i],3,true, true,sprefix,(Object[])expected.get(csslmode[i]+"GG")));
+       suite.addTest(new SslTest(param + "-"+csslmode[i]+"GB2",certdir,sconnstr,csslmode[i],2,true,false,sprefix,(Object[])expected.get(csslmode[i]+"GB")));
+       suite.addTest(new SslTest(param + "-"+csslmode[i]+"GB3",certdir,sconnstr,csslmode[i],3,true,false,sprefix,(Object[])expected.get(csslmode[i]+"GB")));
+       suite.addTest(new SslTest(param + "-"+csslmode[i]+"BG2",certdir,sconnstr,csslmode[i],2,false,true,sprefix,(Object[])expected.get(csslmode[i]+"BG")));
+       suite.addTest(new SslTest(param + "-"+csslmode[i]+"BG3",certdir,sconnstr,csslmode[i],3,false,true,sprefix,(Object[])expected.get(csslmode[i]+"BG")));
+     }
+     return suite;
+   }
+   
+   @Override
+   protected void runTest() throws Throwable {
+     driver(makeConnStr(sslmode, goodclient, goodserver, protocol), expected);
+   }
+ 
+   static Map expectedmap;
+   static TreeMap defaultexpected;
+   
+   //For some strange reason, the v2 driver begins these error messages by "Connection rejected: " but the v3 does not.
+   //Also, for v2 there are to spaces after FATAL:, and the message ends with "\n.".
+   static String PG_HBA_ON = "(Connection rejected: )?FATAL:  ?no pg_hba.conf entry for host .*, user .*, database .*, SSL on(?s-d:.*)";
+   static String PG_HBA_OFF = "(Connection rejected: )?FATAL:  ?no pg_hba.conf entry for host .*, user .*, database .*, SSL off(?s-d:.*)";
+   static String FAILED = "The connection attempt failed.";
+   static String BROKEN = "SSL error: Broken pipe";
+   static String ANY = "ilyen nincs";
+   static String VALIDATOR = "SSL error: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target";
+   static String HOSTNAME = "The hostname .* could not be verified.";
+   static
+   {
+     defaultexpected = new TreeMap();
+     defaultexpected.put("disableGG", new Object[]{null,Boolean.FALSE});
+     defaultexpected.put("disableGB", new Object[]{null,Boolean.FALSE});
+     defaultexpected.put("disableBG", new Object[]{null,Boolean.FALSE});
+     defaultexpected.put("allowGG", new Object[]{null,Boolean.TRUE});
+     defaultexpected.put("allowGB", new Object[]{null,Boolean.TRUE});
+     defaultexpected.put("allowBG", new Object[]{null,Boolean.TRUE});
+     defaultexpected.put("preferGG", new Object[]{null,Boolean.TRUE});
+     defaultexpected.put("preferGB", new Object[]{null,Boolean.TRUE});
+     defaultexpected.put("preferBG", new Object[]{null,Boolean.TRUE});
+     defaultexpected.put("requireGG", new Object[]{null,Boolean.TRUE});
+     defaultexpected.put("requireGB", new Object[]{null,Boolean.TRUE});
+     defaultexpected.put("requireBG", new Object[]{null,Boolean.TRUE});
+     defaultexpected.put("verify-caGG", new Object[]{null,Boolean.TRUE});
+     defaultexpected.put("verify-caGB", new Object[]{ANY,Boolean.TRUE});
+     defaultexpected.put("verify-caBG", new Object[]{null,Boolean.TRUE});
+     defaultexpected.put("verify-fullGG", new Object[]{null,Boolean.TRUE});
+     defaultexpected.put("verify-fullGB", new Object[]{ANY,Boolean.TRUE});
+     defaultexpected.put("verify-fullBG", new Object[]{null,Boolean.TRUE});
+     
+     expectedmap = new TreeMap();
+     TreeMap work;
+     
+     work=(TreeMap)defaultexpected.clone();
+     work.put("disableGG", new Object[]{null,Boolean.FALSE});
+     work.put("disableGB", new Object[]{null,Boolean.FALSE});
+     work.put("disableBG", new Object[]{null,Boolean.FALSE});
+     work.put("allowGG", new Object[]{null,Boolean.FALSE});
+     work.put("allowGB", new Object[]{null,Boolean.FALSE});
+     work.put("allowBG", new Object[]{null,Boolean.FALSE});
+     work.put("preferGG", new Object[]{null,Boolean.FALSE});
+     work.put("preferGB", new Object[]{null,Boolean.FALSE});
+     work.put("preferBG", new Object[]{null,Boolean.FALSE});
+     work.put("requireGG", new Object[]{ANY,Boolean.TRUE});
+     work.put("requireGB", new Object[]{ANY,Boolean.TRUE});
+     work.put("requireBG", new Object[]{ANY,Boolean.TRUE});
+     work.put("verify-caGG", new Object[]{ANY,Boolean.TRUE});
+     work.put("verify-caGB", new Object[]{ANY,Boolean.TRUE});
+     work.put("verify-caBG", new Object[]{ANY,Boolean.TRUE});
+     work.put("verify-fullGG", new Object[]{ANY,Boolean.TRUE});
+     work.put("verify-fullGB", new Object[]{ANY,Boolean.TRUE});
+     work.put("verify-fullBG", new Object[]{ANY,Boolean.TRUE});    
+     expectedmap.put("ssloff",work);
+     
+     work=(TreeMap)defaultexpected.clone();
+     work.put("disableGG", new Object[]{null,Boolean.FALSE});
+     work.put("disableGB", new Object[]{null,Boolean.FALSE});
+     work.put("disableBG", new Object[]{null,Boolean.FALSE});
+     work.put("allowGG", new Object[]{null,Boolean.FALSE});
+     work.put("allowGB", new Object[]{null,Boolean.FALSE});
+     work.put("allowBG", new Object[]{null,Boolean.FALSE});
+     work.put("preferGG", new Object[]{null,Boolean.FALSE});
+     work.put("preferGB", new Object[]{null,Boolean.FALSE});
+     work.put("preferBG", new Object[]{null,Boolean.FALSE});
+     work.put("requireGG", new Object[]{PG_HBA_ON,Boolean.TRUE});
+     work.put("requireGB", new Object[]{PG_HBA_ON,Boolean.TRUE});
+     work.put("requireBG", new Object[]{BROKEN,Boolean.TRUE});
+     work.put("verify-caGG", new Object[]{PG_HBA_ON,Boolean.TRUE});
+     work.put("verify-caGB", new Object[]{VALIDATOR,Boolean.TRUE});
+     work.put("verify-caBG", new Object[]{BROKEN,Boolean.TRUE});
+     work.put("verify-fullGG", new Object[]{PG_HBA_ON,Boolean.TRUE});
+     work.put("verify-fullGB", new Object[]{VALIDATOR,Boolean.TRUE});
+     work.put("verify-fullBG", new Object[]{BROKEN,Boolean.TRUE});    
+     expectedmap.put("sslhostnossl",work);
+     
+     work=(TreeMap)defaultexpected.clone();
+     work.put("disableGG", new Object[]{null,Boolean.FALSE});
+     work.put("disableGB", new Object[]{null,Boolean.FALSE});
+     work.put("disableBG", new Object[]{null,Boolean.FALSE});
+     work.put("allowGG", new Object[]{null,Boolean.FALSE});
+     work.put("allowGB", new Object[]{null,Boolean.FALSE});
+     work.put("allowBG", new Object[]{null,Boolean.FALSE});
+     work.put("preferGG", new Object[]{null,Boolean.TRUE});
+     work.put("preferGB", new Object[]{null,Boolean.TRUE});
+     work.put("preferBG", new Object[]{null,Boolean.FALSE});
+     work.put("requireGG", new Object[]{null,Boolean.TRUE});
+     work.put("requireGB", new Object[]{null,Boolean.TRUE});
+     work.put("requireBG", new Object[]{BROKEN,Boolean.TRUE});
+     work.put("verify-caGG", new Object[]{null,Boolean.TRUE});
+     work.put("verify-caGB", new Object[]{VALIDATOR,Boolean.TRUE});
+     work.put("verify-caBG", new Object[]{BROKEN,Boolean.TRUE});
+     work.put("verify-fullGG", new Object[]{null,Boolean.TRUE});
+     work.put("verify-fullGB", new Object[]{VALIDATOR,Boolean.TRUE});
+     work.put("verify-fullBG", new Object[]{BROKEN,Boolean.TRUE});    
+     expectedmap.put("sslhostgh",work);
+     
+     work=(TreeMap)work.clone();
+     work.put("disableGG", new Object[]{PG_HBA_OFF,Boolean.FALSE});
+     work.put("disableGB", new Object[]{PG_HBA_OFF,Boolean.FALSE});
+     work.put("disableBG", new Object[]{PG_HBA_OFF,Boolean.FALSE});
+     work.put("allowGG", new Object[]{null,Boolean.TRUE});
+     work.put("allowGB", new Object[]{null,Boolean.TRUE});
+     work.put("allowBG", new Object[]{BROKEN,Boolean.TRUE});
+     work.put("preferBG", new Object[]{BROKEN,Boolean.FALSE});
+     expectedmap.put("sslhostsslgh",work);
+     
+     work=(TreeMap)defaultexpected.clone();
+     work.put("disableGG", new Object[]{null,Boolean.FALSE});
+     work.put("disableGB", new Object[]{null,Boolean.FALSE});
+     work.put("disableBG", new Object[]{null,Boolean.FALSE});
+     work.put("allowGG", new Object[]{null,Boolean.FALSE});
+     work.put("allowGB", new Object[]{null,Boolean.FALSE});
+     work.put("allowBG", new Object[]{null,Boolean.FALSE});
+     work.put("preferGG", new Object[]{null,Boolean.TRUE});
+     work.put("preferGB", new Object[]{null,Boolean.TRUE});
+     work.put("preferBG", new Object[]{null,Boolean.FALSE});
+     work.put("requireGG", new Object[]{null,Boolean.TRUE});
+     work.put("requireGB", new Object[]{null,Boolean.TRUE});
+     work.put("requireBG", new Object[]{BROKEN,Boolean.TRUE});
+     work.put("verify-caGG", new Object[]{null,Boolean.TRUE});
+     work.put("verify-caGB", new Object[]{VALIDATOR,Boolean.TRUE});
+     work.put("verify-caBG", new Object[]{BROKEN,Boolean.TRUE});
+     work.put("verify-fullGG", new Object[]{HOSTNAME,Boolean.TRUE});
+     work.put("verify-fullGB", new Object[]{VALIDATOR,Boolean.TRUE});
+     work.put("verify-fullBG", new Object[]{BROKEN,Boolean.TRUE});    
+     expectedmap.put("sslhostbh",work);
+     
+     work=(TreeMap)work.clone();
+     work.put("disableGG", new Object[]{PG_HBA_OFF,Boolean.FALSE});
+     work.put("disableGB", new Object[]{PG_HBA_OFF,Boolean.FALSE});
+     work.put("disableBG", new Object[]{PG_HBA_OFF,Boolean.FALSE});
+     work.put("allowGG", new Object[]{null,Boolean.TRUE});
+     work.put("allowGB", new Object[]{null,Boolean.TRUE});
+     work.put("allowBG", new Object[]{BROKEN,Boolean.TRUE});    
+     work.put("preferBG", new Object[]{BROKEN,Boolean.FALSE});
+     expectedmap.put("sslhostsslbh",work);
+     
+     work=(TreeMap)defaultexpected.clone();
+     work.put("disableGG", new Object[]{PG_HBA_OFF,Boolean.FALSE});
+     work.put("disableGB", new Object[]{PG_HBA_OFF,Boolean.FALSE});
+     work.put("disableBG", new Object[]{PG_HBA_OFF,Boolean.FALSE});
+     work.put("allowGG", new Object[]{null,Boolean.TRUE});
+     work.put("allowGB", new Object[]{null,Boolean.TRUE});
+     work.put("allowBG", new Object[]{BROKEN,Boolean.TRUE});
+     work.put("preferGG", new Object[]{null,Boolean.TRUE});
+     work.put("preferGB", new Object[]{null,Boolean.TRUE});
+     work.put("preferBG", new Object[]{BROKEN,Boolean.TRUE});
+     work.put("requireGG", new Object[]{null,Boolean.TRUE});
+     work.put("requireGB", new Object[]{null,Boolean.TRUE});
+     work.put("requireBG", new Object[]{BROKEN,Boolean.TRUE});
+     work.put("verify-caGG", new Object[]{null,Boolean.TRUE});
+     work.put("verify-caGB", new Object[]{VALIDATOR,Boolean.TRUE});
+     work.put("verify-caBG", new Object[]{BROKEN,Boolean.TRUE});
+     work.put("verify-fullGG", new Object[]{null,Boolean.TRUE});
+     work.put("verify-fullGB", new Object[]{VALIDATOR,Boolean.TRUE});
+     work.put("verify-fullBG", new Object[]{BROKEN,Boolean.TRUE});    
+     expectedmap.put("sslhostsslcertgh",work);
+     
+     work=(TreeMap)defaultexpected.clone();
+     work.put("disableGG", new Object[]{PG_HBA_OFF,Boolean.FALSE});
+     work.put("disableGB", new Object[]{PG_HBA_OFF,Boolean.FALSE});
+     work.put("disableBG", new Object[]{PG_HBA_OFF,Boolean.FALSE});
+     work.put("allowGG", new Object[]{null,Boolean.TRUE});
+     work.put("allowGB", new Object[]{null,Boolean.TRUE});
+     work.put("allowBG", new Object[]{BROKEN,Boolean.TRUE});
+     work.put("preferGG", new Object[]{null,Boolean.TRUE});
+     work.put("preferGB", new Object[]{null,Boolean.TRUE});
+     work.put("preferBG", new Object[]{BROKEN,Boolean.TRUE});
+     work.put("requireGG", new Object[]{null,Boolean.TRUE});
+     work.put("requireGB", new Object[]{null,Boolean.TRUE});
+     work.put("requireBG", new Object[]{BROKEN,Boolean.TRUE});
+     work.put("verify-caGG", new Object[]{null,Boolean.TRUE});
+     work.put("verify-caGB", new Object[]{VALIDATOR,Boolean.TRUE});
+     work.put("verify-caBG", new Object[]{BROKEN,Boolean.TRUE});
+     work.put("verify-fullGG", new Object[]{HOSTNAME,Boolean.TRUE});
+     work.put("verify-fullGB", new Object[]{VALIDATOR,Boolean.TRUE});
+     work.put("verify-fullBG", new Object[]{BROKEN,Boolean.TRUE});    
+     expectedmap.put("sslhostsslcertbh",work);
+     
+     work=(TreeMap)defaultexpected.clone();
+     work.put("disableGG", new Object[]{PG_HBA_OFF,Boolean.FALSE});
+     work.put("disableGB", new Object[]{PG_HBA_OFF,Boolean.FALSE});
+     work.put("disableBG", new Object[]{PG_HBA_OFF,Boolean.FALSE});
+     work.put("allowGG", new Object[]{null,Boolean.TRUE});
+     work.put("allowGB", new Object[]{null,Boolean.TRUE});
+     work.put("allowBG", new Object[]{BROKEN,Boolean.TRUE});
+     work.put("preferGG", new Object[]{null,Boolean.TRUE});
+     work.put("preferGB", new Object[]{null,Boolean.TRUE});
+     work.put("preferBG", new Object[]{BROKEN,Boolean.TRUE});
+     work.put("requireGG", new Object[]{null,Boolean.TRUE});
+     work.put("requireGB", new Object[]{null,Boolean.TRUE});
+     work.put("requireBG", new Object[]{BROKEN,Boolean.TRUE});
+     work.put("verify-caGG", new Object[]{null,Boolean.TRUE});
+     work.put("verify-caGB", new Object[]{VALIDATOR,Boolean.TRUE});
+     work.put("verify-caBG", new Object[]{BROKEN,Boolean.TRUE});
+     work.put("verify-fullGG", new Object[]{null,Boolean.TRUE});
+     work.put("verify-fullGB", new Object[]{VALIDATOR,Boolean.TRUE});
+     work.put("verify-fullBG", new Object[]{BROKEN,Boolean.TRUE});    
+     expectedmap.put("sslcertgh",work);
+     
+     work=(TreeMap)work.clone();
+     work.put("verify-fullGG", new Object[]{HOSTNAME,Boolean.TRUE});
+     expectedmap.put("sslcertbh",work);
+ 
+   }
+ 
+ 
+ 
+ }
diff -rcN postgresql-jdbc-9.1dev-900.src/org/postgresql/test/ssl/SslTestSuite.java postgresql-jdbc-new/org/postgresql/test/ssl/SslTestSuite.java
*** postgresql-jdbc-9.1dev-900.src/org/postgresql/test/ssl/SslTestSuite.java	1970-01-01 01:00:00.000000000 +0100
--- postgresql-jdbc-new/org/postgresql/test/ssl/SslTestSuite.java	2011-08-23 19:49:57.000000000 +0200
***************
*** 0 ****
--- 1,52 ----
+ package org.postgresql.test.ssl;
+ 
+ import java.io.FileInputStream;
+ import java.util.Properties;
+ 
+ import junit.framework.Test;
+ import junit.framework.TestCase;
+ import junit.framework.TestResult;
+ import junit.framework.TestSuite;
+ 
+ import org.postgresql.test.TestUtil;
+ 
+ public class SslTestSuite  extends TestSuite {
+ 
+   private static void add(TestSuite suite, String param)
+   {
+     if (prop.getProperty(param,"").equals(""))
+     {
+       System.out.println("Skipping "+param+".");
+     } else {
+       suite.addTest(SslTest.getSuite(prop,param));
+     }              
+   }
+   
+   static Properties prop;
+   /*
+    * The main entry point for JUnit
+    */
+   public static TestSuite suite() throws Exception
+   {
+       TestSuite suite = new TestSuite();
+       prop = new Properties();
+       prop.load(new FileInputStream(System.getProperty("ssltest.properties")));
+       add(suite,"ssloff");
+       add(suite,"sslhostnossl");
+ 
+       String[] hostmode = {"sslhost","sslhostssl","sslhostsslcert","sslcert"};
+       String[] certmode = {"gh","bh"};
+       
+       for (int i=0; i<hostmode.length; i++)
+       {
+         for (int j=0; j<certmode.length; j++)
+         {
+           add(suite,hostmode[i]+certmode[j]);
+         }
+       }
+       
+       TestUtil.initDriver();
+       
+       return suite;
+   }
+ }
diff -rcN postgresql-jdbc-9.1dev-900.src/ssltest.properties postgresql-jdbc-new/ssltest.properties
*** postgresql-jdbc-9.1dev-900.src/ssltest.properties	1970-01-01 01:00:00.000000000 +0100
--- postgresql-jdbc-new/ssltest.properties	2011-08-23 19:55:10.000000000 +0200
***************
*** 0 ****
--- 1,28 ----
+ 
+ certdir=certdir
+ 
+ ssloff=
+ ssloffprefix=
+ 
+ sslhostnossl=jdbc:postgresql://localhost:5433/hostnossldb?sslpassword=sslpwd
+ sslhostnosslprefix=
+ 
+ sslhostgh=jdbc:postgresql://localhost:5433/hostdb?sslpassword=sslpwd
+ sslhostghprefix=
+ sslhostbh=jdbc:postgresql://127.0.0.1:5433/hostdb?sslpassword=sslpwd
+ sslhostbhprefix=
+ 
+ sslhostsslgh=jdbc:postgresql://localhost:5433/hostssldb?sslpassword=sslpwd
+ sslhostsslghprefix=
+ sslhostsslbh=jdbc:postgresql://127.0.0.1:5433/hostssldb?sslpassword=sslpwd
+ sslhostsslbhprefix=
+ 
+ sslhostsslcertgh=jdbc:postgresql://localhost:5433/hostsslcertdb?sslpassword=sslpwd
+ sslhostsslcertghprefix=
+ sslhostsslcertbh=jdbc:postgresql://127.0.0.1:5433/hostsslcertdb?sslpassword=sslpwd
+ sslhostsslcertbhprefix=
+ 
+ sslcertgh=jdbc:postgresql://localhost:5433/certdb?sslpassword=sslpwd
+ sslcertghprefix=
+ sslcertbh=jdbc:postgresql://127.0.0.1:5433/certdb?sslpassword=sslpwd
+ sslcertbhprefix=
