我想使用我的Java应用程序在SSL / TLS握手期间检索Microsoft SQL Server(2012/2014)的已发送公共服务器证书。
我的环境首先:
要以编程方式实现 ,我正在使用自己的信任管理器实现。请在此处查看相关代码的摘录:
SSLSocket sslSocket = (SSLSocket) getFactory().createSocket(socket, host, port, true);
sslSocket.startHandshake();
getFactory():
private SSLSocketFactory getFactory() throws IOException
{
// irrelevant code removed here
return factory();
}
工厂():
private static SSLSocketFactory factory() throws NoSuchAlgorithmException, KeyManagementException
{
SSLSocketFactory factorySingleton;
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, getTrustManager(), null);
factorySingleton = ctx.getSocketFactory();
return factorySingleton;
}
getTrustManager():
private static TrustManager[] getTrustManager()
{
X509Certificate[] server = null;
X509Certificate[] client = null;
X509TrustManager tm = new X509TrustManager()
{
X509Certificate[] server1 = null;
X509Certificate[] client1 = null;
public X509Certificate[] getAcceptedIssuers()
{
return new X509Certificate[0];
}
public void checkServerTrusted(X509Certificate[] chain, String x)
{
server1 = chain;
Logger.println("X509 Certificate chain: " + chain);
}
public void checkClientTrusted(X509Certificate[] chain, String x)
{
client1 = chain;
Logger.println("X509 Certificate chain: " + chain);
}
};
return new X509TrustManager[]{tm};
}
我原以为对startHandshake()
的调用会在某些时候使我的应用程序从我的SQL服务器接收不同的证书,并试图验证它们是否调用我的自定义信任管理器。此时我将获得证书(X509Certificate []链)。但是我的信任管理器没有被调用,或者至少两个检查器方法中的断点都没有被调用。
这是我用于参考的MS文档之一:https://msdn.microsoft.com/en-us/library/bb879919(v=sql.110).aspx#Anchor_1
“在SSL握手期间,服务器将其公钥证书发送给客户端。” < ---正是我想要/需要的。
答案 0 :(得分:2)
经过一周的搜索,我发现了问题。在这里可以看到什么不起作用/只是一种解决方法:https://superuser.com/questions/1042525/retrieve-server-certificate-from-sql-server-2012-to-trust
问题/问题是Microsoft使用的TDS(表格数据流)协议,它是一个包装下面所有层和连接的应用层协议。这意味着驱动程序在连接到Microsoft SQL Server或Sybase(TDS最初是由Sybase创建)时必须实现此TDS协议。 FreeTDS就是这样一种实现,对于Java来说,有jTDS,遗憾的是它几乎已经死了。尽管仍然有一些修复已经完成,但没有包括在内并作为新的jTDS版本发布。可以在这里找到jTDS:https://sourceforge.net/projects/jtds/files/但是对于Java 1.8,数据类型发生了变化,导致jTDS向MSSQL发送256字节的无意义,从而无法实现SSL / TLS。这已在r1286(https://sourceforge.net/p/jtds/code/commit_browser)
中修复应用这些更改并至少使用连接字符串属性SSL=require
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)
{
// Dummy method
}
public void checkClientTrusted(X509Certificate[] chain, String x)
{
// Dummy method
}
};
return new X509TrustManager[]{tm};
}
将被调用。有了这个,OP中描述的方法可用于从服务器检索证书。这不是预期的用法,所以需要添加一些丑陋的getter / setter和trickery来实际获得证书,这样的方法有以下变化:
在net\sourceforge\jtds\jdbc\SharedSocket.java
中将enableEncryption()
更改为:
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()));
}
并使用其getter / setter添加以下字段:
private javax.security.cert.X509Certificate[] x509Certificates;
private void setX509Certificates(javax.security.cert.X509Certificate[] certs)
{
x509Certificates = certs;
}
public javax.security.cert.X509Certificate[] getX509Certificates()
{
return x509Certificates;
}
在net\sourceforge\jtds\jdbc\TdsCore.java
更改negotiateSSL()
,以便将其包括在内:
if (sslMode != SSL_NO_ENCRYPT)
{
socket.enableEncryption(ssl);
setX509Certificate(socket.getX509Certificates());
}
再次使用getter / setter完全相同的字段:
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;
net\sourceforge\jtds\jdbc\JtdsConnection.java
的构造函数JtdsConnection()
在构造函数内setX509Certificates(baseTds.getX509Certificate())
上调用negotiateSSL()
后调用baseTds.negotiateSSL()
。该类还必须包含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;
最后,可以创建自己的实用程序类来使用所有这些添加:
JtdsConnection jtdsConnection = new JtdsConnection(url, <properties to be inserted>);
X509Certificate[] certs = jtdsConnection.getX509Certificates()
对于属性(它们不是您通常为jdbc找到的所有标准属性),请使用提供的DefaultProperties.addDefaultProperties()
,然后在new Properties()
对象中更改用户,密码,主机等。