sce*_*ler 7 java sql-server ssl
我想使用我的Java应用程序在SSL/TLS握手期间检索Microsoft SQL Server(2012/2014)的已发送公共服务器证书.
我的环境第一:
为了以编程方式实现此目的,我使用自己的信任管理器实现.请在此处查看相关代码的摘录:
SSLSocket sslSocket = (SSLSocket) getFactory().createSocket(socket, host, port, true);
sslSocket.startHandshake();
Run Code Online (Sandbox Code Playgroud)
getFactory():
private SSLSocketFactory getFactory() throws IOException
{
// irrelevant code removed here
return factory();
}
Run Code Online (Sandbox Code Playgroud)
厂():
private static SSLSocketFactory factory() throws NoSuchAlgorithmException, KeyManagementException
{
SSLSocketFactory factorySingleton;
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, getTrustManager(), null);
factorySingleton = ctx.getSocketFactory();
return factorySingleton;
}
Run Code Online (Sandbox Code Playgroud)
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};
}
Run Code Online (Sandbox Code Playgroud)
我希望startHandshake()在某些时候调用会使我的应用程序从我的SQL服务器接收不同的证书,并试图验证它们调用我的自定义信任管理器.此时我将获得证书(X509Certificate []链).但是我的信任管理器没有被调用,或者至少两个checker方法中的断点都没有被调用.
这是我用于参考的MS文档之一:https://msdn.microsoft.com/en-us/library/bb879919(v = sql.110).aspx# Anchor_1
"在SSL握手期间,服务器将其公钥证书发送给客户端." <---正是我想要/需要的.
经过一周的搜索,我发现了问题。什么不起作用/只是一种解决方法可以在这里看到:https ://superuser.com/questions/1042525/retrieve-server-certificate-from-sql-server-2012-to-trust
问题/问题是微软使用的TDS(表格数据流)协议,它是一个应用层协议,包装了下面的所有层和连接。这意味着驱动程序在连接到 Microsoft SQL Server 或 Sybase 时必须实现此 TDS 协议(TDS 最初由 Sybase 创建)。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};
}
Run Code Online (Sandbox Code Playgroud)
将被调用。这样,OP 中描述的方式可用于从服务器检索证书。这不是预期的用途,因此需要添加一些丑陋的 getter/setter 和技巧来实际获取证书,其中一种方法是进行以下更改:
对此进行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()));
}
Run Code Online (Sandbox Code Playgroud)
并添加以下字段及其 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;
}
Run Code Online (Sandbox Code Playgroud)
进行net\sourceforge\jtds\jdbc\TdsCore.java更改,negotiateSSL()以便将其包括在内:
if (sslMode != SSL_NO_ENCRYPT)
{
socket.enableEncryption(ssl);
setX509Certificate(socket.getX509Certificates());
}
Run Code Online (Sandbox Code Playgroud)
再次拥有与 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;
Run Code Online (Sandbox Code Playgroud)
net\sourceforge\jtds\jdbc\JtdsConnection.javas 构造函数也必须做同样的事情JtdsConnection()
在构造函数内部调用setX509Certificates(baseTds.getX509Certificate())之后调用。该类还必须包含 getter/setter:negotiateSSL()baseTds.negotiateSSL()
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;
Run Code Online (Sandbox Code Playgroud)
最后,人们可以创建自己的实用程序类来利用所有这些添加,如下所示:
JtdsConnection jtdsConnection = new JtdsConnection(url, <properties to be inserted>);
X509Certificate[] certs = jtdsConnection.getX509Certificates()
Run Code Online (Sandbox Code Playgroud)
对于属性(它们不是您通常为 jdbc 找到的所有标准属性),请使用提供的属性DefaultProperties.addDefaultProperties(),然后更改new Properties()对象中的用户、密码、主机等。
PS.:有人可能想知道为什么所有这些繁琐的更改......例如,由于许可原因无法发布 Microsoft 的 jdbc 驱动程序或不想/无法使用它,这提供了另一种选择。
| 归档时间: |
|
| 查看次数: |
1252 次 |
| 最近记录: |