After a week of searching, I found a problem. What does not work / only a workaround can be seen here: https://superuser.com/questions/1042525/retrieve-server-certificate-from-sql-server-2012-to-trust
The problem / problem is the TDS (tabular data stream) protocol used by Microsoft, which is an application layer protocol that wraps all the layers and connections below it. This means that the driver must implement this TDS protocol when connecting to a Microsoft SQL or Sybase server (originally TDS was created by Sybase). FreeTDS is such an implementation, and for Java there is jTDS, which, unfortunately, is mostly dead. Despite the fact that they are still fixed, but not included and released as a new version of jTDS. jTDS can be found here: https://sourceforge.net/projects/jtds/files/ , but with Java 1.8 there was a change in the data type, which is why jTDS sent 256 bytes of nonsense to MSSQL, which makes SSL / TLS impossible. This has been fixed in r1286 ( https://sourceforge.net/p/jtds/code/commit_browser )
After applying these changes and using at least the properties of the SSL=require connection string, the user trust manager in net\sourceforge\jtds\ssl\SocketFactories.java :
private static TrustManager[] trustManagers() { X509TrustManager tm = new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } public void checkServerTrusted(X509Certificate[] chain, String x) {
. Moreover, the described method in the OP can be used to extract the certificate from the server. This is not an intended use, so you need to add some ugly getter / setter and trickery to actually get the certificate. One such approach is the following changes:
In net\sourceforge\jtds\jdbc\SharedSocket.java change enableEncryption() to this:
void enableEncryption(String ssl) throws IOException { Logger.println("Enabling TLS encryption"); SocketFactory sf = SocketFactories.getSocketFactory(ssl, socket); sslSocket = sf.createSocket(getHost(), getPort()); SSLSocket s = (SSLSocket) sslSocket; s.startHandshake(); setX509Certificates(s.getSession().getPeerCertificateChain()); setOut(new DataOutputStream(sslSocket.getOutputStream())); setIn(new DataInputStream(sslSocket.getInputStream())); }
and add the following field with its recipient / installer:
private javax.security.cert.X509Certificate[] x509Certificates; private void setX509Certificates(javax.security.cert.X509Certificate[] certs) { x509Certificates = certs; } public javax.security.cert.X509Certificate[] getX509Certificates() { return x509Certificates; }
In net\sourceforge\jtds\jdbc\TdsCore.java change negotiateSSL() so that it is turned on:
if (sslMode != SSL_NO_ENCRYPT) { socket.enableEncryption(ssl); setX509Certificate(socket.getX509Certificates()); }
And again we get the same getter / setter field:
public javax.security.cert.X509Certificate[] getX509Certificate() { return x509Certificate; } public void setX509Certificate(javax.security.cert.X509Certificate[] x509Certificate) { this.x509Certificate = x509Certificate; } private javax.security.cert.X509Certificate[] x509Certificate;
The same should be done for the constructor net\sourceforge\jtds\jdbc\JtdsConnection.java JtdsConnection()
to call setX509Certificates(baseTds.getX509Certificate()) after negotiateSSL() been called in baseTds.negotiateSSL() inside the constructor. This class should also contain a getter / setter:
public javax.security.cert.X509Certificate[] getX509Certificates() { return x509Certificates; } public void setX509Certificates(javax.security.cert.X509Certificate[] x509Certificates) { this.x509Certificates = x509Certificates; } private javax.security.cert.X509Certificate[] x509Certificates;
Finally, you can create your own utility class to use this entire add-on as follows:
JtdsConnection jtdsConnection = new JtdsConnection(url, <properties to be inserted>); X509Certificate[] certs = jtdsConnection.getX509Certificates()
For properties (they are not all standard ones that you usually find for jdbc), use the provided DefaultProperties.addDefaultProperties() and subsequently change the user, password, host, etc. in the new Properties() object.
PS: Is it possible to ask why all these cumbersome changes ... for example, due to licensing reasons, you cannot send the Microsoft jdbc driver or do not want / cannot use it, this also provides an alternative.