Postgresql JDBC SSL - java

We have a remote PostgreSQL DB server which requires SSL certs to be provided to connect to it.
I have the following certs with me.
root.pem
intermediate.pem
private-chain.pem
public-chain.pem
certificate.cer
certificate.key
And I am able to connect to the remote database using psql as
psql "port=5432 host=remote-host user=username sslcert=public-chain.pem sslkey=certificate.key dbname=database sslmode=require"
Now, I need to connect to the same database from a Java Spring based Rest API. So, ideally this would require building keystrokes from the certs and using that to connect to the the db.
The issue is, I'm not able to! I've tried all combinations of cert, public-chain, private-chain, root ca, etc. in the keystore. And I've tried passing the keystore as JVM arguments when calling the jar file (-Djavax.net.ssl.trustStore -Djavax.net.ssl.trustStorePassword -Djavax.net.ssl.keyStore -Djavax.net.ssl.keyStorePassword).
I've also tried pointing to the keystrokes from PostgreSQL JDBC connection string (jdbc:postgresql://remote-host:5432/database?ssl=true&sslcert='filename'&sslkey='key'). Not really sure if this is the right way.
But I keep getting this error.
Caused by: org.postgresql.util.PSQLException: FATAL: connection requires a valid client certificate
at org.postgresql.core.v3.ConnectionFactoryImpl.doAuthentication(ConnectionFactoryImpl.java:473) ~[postgresql-42.2.2.jar!/:42.2.2]
at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:205) ~[postgresql-42.2.2.jar!/:42.2.2]
at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:49) ~[postgresql-42.2.2.jar!/:42.2.2]
at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:195) ~[postgresql-42.2.2.jar!/:42.2.2]
at org.postgresql.Driver.makeConnection(Driver.java:452) ~[postgresql-42.2.2.jar!/:42.2.2]
at org.postgresql.Driver.connect(Driver.java:254) ~[postgresql-42.2.2.jar!/:42.2.2]
I am not able to figure out why it works with psql and not with jdbc.
EDIT 1:
Currently I am trying the following approach. This is the spring datasource configuration
spring.datasource.url=jdbc:postgresql://remote-host:5432/database?ssl=true&sslfactory=org.postgresql.ssl.NonValidatingFactory
spring.datasource.username=username
This is the cert config
export PGSSLCERT=/tmp/client.cer
export PGSSLKEY=/tmp/client.key
export PGSSLMODE=allow
I've also followed the steps described here to trust root ca. But I guess that is not necessary since I'm using org.postgresql.ssl.NonValidatingFactory
And I'm starting the application as
java -jar -Dspring.profiles.active=prod application.jar
Any insights into this are appreciated! Thanks.

What does your pb_hba.conf setting look like?
The JDBC driver only supports the trust, ident, password, md5, and crypt authentication methods.
So your java app will have to connect using password and certificate. You can specify that in your pb_hba.conf:
hostssl all all 0.0.0.0/0 md5 clientcert=1

Related

Provide SSL certificate to SQL Server via JDBC

The Microsoft JDBC SSL documentation details the use of a Java keystore in order to specify a certificate bundle to use when validating the TLS connection. Is there a way to provide a certificate bundle without needing to store it within a JKS when connecting to a SQL Server database? Postgres seems to have a sslrootcert option which appears to be lacking for SQL Server.
Since version 6.3.3 the Microsoft JDBC Driver for SQL allows, to specify a custom implementation of javax.net.ssl.TrustManager through connection properties. You can specify a class name and a constructor parameter (e.g. a file name or the certificate in PEM format).
See here, for more detailed instructions and a simple example of a custom TrustManager. The functionality of the sslrootcert option of the Postgres driver is implemented in a similar way here.

JDBC Certificate Requirements for connection in Google Apps Scripts

I'm trying to connect to an on-premises sql server (Microsoft Sql Server Express), but I keep getting an error that has to do with the validation of the certificate.
Connections through locally-run java (with JDBC) work when I pass encrypt=false or trustServerCertificate=true, otherwise I get an error message :
"Error: "PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target".
I know that Google Apps Scripts allows for using self-signed certificates (as documented here), but I'm not sure what they're actually looking for in the _serverSslCertificate, _clientSslCertificate, or _clientSslKey fields.
Unfortunately, I'm not able to share more details about the network I'm working in. I've tried applying self-signed certificates generated with PowerShell and Windows IIS to the server, but it simply doesn't restart after I do this.
Any help is appreciated.
These parameters require SSL certificates and key of the server and client. As documented here, you can see the expected values they need on those parameters. See snippet below:
var conn = Jdbc.getConnection('jdbc:mysql://<ip address>/<db name>?useSSL=true', {
user: '<user>',
password: '<pass>',
_serverSslCertificate: '-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----',
_clientSslCertificate: '-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----',
_clientSslKey: '-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----'
});
You can try the above snippet but this is for MySQL originally and is reported to have not worked for Microsoft SQL Server. The latter has its own issue posted here but still doesn't have any updates.
Guides that might help you are linked below.
References:
https://developers.google.com/apps-script/guides/jdbc
https://trang-nguyen-ngoc.medium.com/import-data-from-ms-sql-server-to-google-sheets-using-google-apps-script-d92098ea16ee (establish sql connection)

Why can JDBC connect to MySQL when SSL has been configured, without having the cert

My issue is I seem to have secured MySQL from the command line, yet when I connect through JDBC that same restriction falls away. I'm a beginner to using SSL for my database connection, this is the first time I've tried to set it up.
I setup SSL for MySQL locally on my Linux machine. I generated the certificates, editing my.cnf, etc.
Here is what I added to my my.cnf:
[mysqld]
ssl-ca=/var/lib/mysql/cert/ca-cert.pem
ssl-cert=/var/lib/mysql/cert/server-cert.pem
ssl-key=/var/lib/mysql/cert/server-key.pem
Right there are valid paths to my server cert/key, and my CA cert. It's self-signed. I'll get to having a more official one when I need it.
In the MySQL database, I set my database user account to require SSL:
GRANT ALL PRIVILEGES ON *.* TO ‘orgapi’#’localhost’
IDENTIFIED BY ‘password’ REQUIRE SSL;
So far so good. Once that user requires SSL, MySQL seems to require a client cert to be provided when making the connection. I generated that cert as well, signing it with the same self-signed CA cert.
Now, for the first test: I tried logging in from the command line WITHOUT the client cert:
mysql -u orgapi -p -h localhost
Voila, it fails to let me log in. However, when I include the SSL cert in the login command, like this:
mysql --ssl-ca=ca-cert.pem --ssl-cert=client-cert.pem --ssl-key=client-key.pem -u orgapi -p -h localhost
It works like a charm. I'm able to connect when I specify the client cert, and if I do a "status" command, it says I'm connected using SSL.
The problem
To ensure that everything was all set to go, I threw together some basic test code using JDBC. Currently, I'm not providing the client certs to this JDBC code. Because of that, I would expect the connection to fail.
public static void main(String[] args) throws Exception{
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306", "orgapi", "orgapi");
conn.setCatalog("org_app");
Statement stmt = conn.createStatement();
ResultSet resultSet = stmt.executeQuery("select * from users");
while(resultSet.next()){
System.out.println(resultSet.getString(1) + " " + resultSet.getString(2));
}
}
What worries me is that it doesn't fail. In fact, it executes the query without any issue. Now, if I append to the end of the database url "?useSSL=true", then it will fail to connect because it doesn't have the cert. But it still concerns me that JDBC code would be able to connect to MySQL and bypass the restrictions that exist elsewhere.
So I'm just wondering what I may be doing wrong, or what I don't understand about this process. I just want to be sure that I'm not leaving my database less secure somehow.
Edit
I do get this interesting log message when I'm using JDBC:
Sun Oct 23 08:58:28 EDT 2016 WARN: Establishing SSL connection without
server's identity verification is not recommended. According to MySQL
5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance
with existing applications not using SSL the verifyServerCertificate
property is set to 'false'. You need either to explicitly disable SSL
by setting useSSL=false, or set useSSL=true and provide truststore for
server certificate verification.

Apache directory API refuses to add user to Active Directory "(UNWILLING_TO_PERFORM)"

Im trying to add a user using Java 6 and Apache Directory API to Active Directory running on windows server 2008, but the result i get is (UNWILLING_TO_PERFORM), now i understand that it needs a secure connection and to use unicodePwd attribute to add the password, which i did, or at least tried to do.
The server admin created a certificate for the account i use when adding users, i exported it and added it to cacert in /jre/lib/security/.
I tried to see if i can just connect securely to the AD server without performing any operations, i get 2 scenarios that fail on the binding operation .bind():
1- if i connect using port 636 and use startTLS() available in ldapNetworkConnection : then i either get PROTOCOL_ERROR server will disconnect
2- if i connect using port 389 and use startTLS() available in LdapConnectionConfig i get "Failed to initialize the SSL context" and "IllegalArgumentException :TLSv1.1"
I added the trust manager but still no luck, below is the code so far.
LdapConnectionConfig config = new LdapConnectionConfig();
config.setLdapHost(IP);
config.setLdapPort(389);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init((KeyStore)null);
config.setTrustManagers(tmf.getTrustManagers());
config.setName("CN=TestAdmin,CN=Users,DC=bmrk,DC=com");
config.setCredentials("P#ssw0rd");
LdapNetworkConnection ldapNetworkConnection = new LdapNetworkConnection(config);
ldapNetworkConnection.connect();
ldapNetworkConnection.startTls();
ldapNetworkConnection.bind();
// EDIT
i switched to JNDI LDAP APIs it gave me a more reasonable error, apparently the issue comes from the SSL handshake, my app isn't able to find the valid certificate, any recommendations?
Any help is really appreciated.
Thanks,
In Case anyone is interested, my only issue was in the adding the server that generated the certificate as a CA authority in the cacerts file so that the JVM can trust certificates issued by the server although it worked smoothly and perfect with JNDI ldap APIs it doesn't work with Apache Directory not sure why.
so my recommendation is to use JNDI instead of Apache Directory and make sure you export the certificate for the account used to login to Active Directory as PKCS12 and add it to the keystore.
so it's 2 certificates, 1 for the login/bind account and 1 for the server to act as a CA (Certificate Authority).
Also Java 6 support TLSv1 as a maximum security protocol which isn't the standard case with Apache LDAP, so either upgrade you Java or use JNDI.
it works perfectly with Java 7.

CF8 SSL Connection to Postgres fails

I'm attempting an SSL connection from a ColdFusion 8 Enterprise client to a Redhat 5 Postgres server. Another party set up Postgres and sent me the certificates.
I imported root.crt into E:\JRun4\jre\lib\security\cacerts successfully and restarted the service.
The Postgres pg_hba.conf file has the client IP, and connects fine without SSL
Postgresql.conf has ssl=on
The server-side certs were created according to http://www.postgresql.org/docs/8.2/static/ssl-tcp.html and presumingly done correct.
With SSL enabled, I get org.postgresql.util.PSQLException: The connection attempt failed. which produces little helpful information from the searches I've done.
JDBC URL: jdbc:postgresql://x.x.x.x/main?ssl=true
Class: org.postgresql.Driver
What can be done to see/test if the error source is on my end or the server end?
Obvious questions to start with in troubleshooting something like this are:
Can you connect with SSL enabled using psql?
Is there anything of interest in the postgresql logs?
If that doesn't work, is there a way you can test the certificate against the root ca's signature?
If that fails, the only thing I can think of is tracing the client side and seeing exactly where in the SSL libraries it fails.
For certificate problems add &sslfactory=org.postgresql.ssl.NonValidatingFactory to your jdbc url. See https://jdbc.postgresql.org/documentation/91/ssl-client.html for more information.
I found the notes I took for solving this. Note that the drive and folder paths may be different for your setup.
Copy server.crt.der your destination server
Open a command window in your destination server and go to E:\Jrun4\jre\bin\
Type in the following command: keytool -importcert -alias postgres -keystore e:\jrun4\jre\lib\security\cacerts -file c:\location\of\server.crt -storepass changeit
It will prompt you whether you should trust this certificate; answer yes
To verify it successfully installed, type: keytool -list -alias postgres -keystore e:\jrun4\jre\lib\security\cacerts -storepass changeit. If successful you'll see a single listing return by that alias, otherwise it will say none found
After the certificate has been stored you may delete the actual certificate file from the server
Restart ColdFusion services on the client machine

Categories