在阅读了关于这些主题的众多答案之后,我发现自己完全无法将拼图的各个部分拼凑在一起,我希望你能原谅我。
我正在尝试在Java中更改我的简单套接字连接以使用SSL。我希望服务器和客户端都尽可能自我验证,但只有服务器身份验证才是良好的开端。
目前,这是服务器端非常简单的代码:
ServerSocket serverSocket = new ServerSocket(port);
Socket socket = serverSocket.accept();
这是客户端的代码:
Socket socket = null;
while (true) {
try {
socket = new Socket(ipAddress, port);
break;
} catch (Exception e) {}
}
这很好,但没有SSL。
我使用OpenSSL为服务器和客户端生成了SSL证书,最后是:
由此我使用OpenSSL为客户端和服务器创建PKCS12(.p12)密钥库,如下所示
然后,我使用keytool将这些转换为JKS密钥库(例如,对于服务器,命令是 keytool -importkeystore -srckeystore server.p12 -srcstoretype PKCS12 -destkeystore server.jks -deststoretype JKS ),产生两个名为 server.jks 和 client.jks 的文件。
然后我使用以下代码替换以前的服务器代码段:
char[] keyStorePassword = "JKSPassword".toCharArray();
FileInputStream keyStoreFile = new FileInputStream("somepath/server.jks");
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(keyStoreFile, keyStorePassword);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, "PKCS12Password".toCharArray());
SSLContext sslContext = SSLContext.getDefault();
ServerSocket serverSocket = sslContext.getServerSocketFactory().createServerSocket(port);
Socket socket = serverSocket.accept();
以下代码替换客户端代码段:
Socket socket = null;
while (true) {
try {
char[] keyStorePassword = "JKSPassword".toCharArray();
FileInputStream keyStoreFile = new FileInputStream("somepath/client.jks");
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(keyStoreFile, keyStorePassword);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, "PKCS12Password".toCharArray());
SSLContext sslContext = SSLContext.getDefault();
socket = sslContext.getSocketFactory().createSocket(ipAddress, port);
break;
} catch (Exception e) {}
}
我现在仍然得到 javax.net.ssl.SSLHandshakeException:服务器上没有共同的密码套件 javax.net.ssl.SSLHandshakeException:收到致命警报:handshake_failure 我>在客户端上。)
可能出现什么问题?
答案 0 :(得分:1)
我发现了谜题的缺失部分(我相信)。
而不是客户端上的行SSLContext sslContext = SSLContext.getDefault();
,我把:
BufferedInputStream serverCertificateFile = new BufferedInputStream(new FileInputStream("somepath/server-cert.der"));
X509Certificate serverCertificate = (X509Certificate)
CertificateFactory.getInstance("X.509").generateCertificate(serverCertificateFile);
sslContext.init(keyManagerFactory.getKeyManagers(), new TrustManager[] {
new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] arg0,
String arg1) throws CertificateException {
throw new CertificateException();
}
@Override
public void checkServerTrusted(X509Certificate[] arg0,
String arg1) throws CertificateException {
boolean valid = false;
for (X509Certificate certificate : arg0) {
try {
certificate.verify(serverCertificate.getPublicKey());
valid = true;
break;
} catch (SignatureException e) {}
}
if (!valid) {
throw new CertificateException();
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
}, new SecureRandom());
server-cert.der是一个文件,使用 openssl509 -outform der -in server-cert.pem -out server-cert.der 创建。
我无法在StackOverflow上找到关于此问题的好教程或问题,所以这是我通过尝试理解参考指南而创建的。
基本上,我正在创建一个TrustManager,对于服务器,当其中一个提供的证书属于您自己的服务器时,它会信任它。
它似乎有用,请告诉我这种方法是否有任何重大错误(我很遗憾地认为存在)。谢谢你读了这么多!