我找到了一些处理https服务器和https客户端的教程。我创建了一些密钥库,它工作正常。但是我有一些问题从教程中不清楚。
这是我的https-server
public class HTTPSServer {
private int port = 9999;
private boolean isServerDone = false;
public static void main(String[] args) {
HTTPSServer server = new HTTPSServer();
server.run();
}
HTTPSServer() {
}
HTTPSServer(int port) {
this.port = port;
}
// Create the and initialize the SSLContext
private SSLContext createSSLContext() {
try {
//Returns keystore object in definied type, here jks
KeyStore keyStore = KeyStore.getInstance("JKS");
//loads the keystore from givin input stream, and the password to unclock jks
keyStore.load(new FileInputStream("x509-ca.jks"), "password".toCharArray());
// Create key manager
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(keyStore, "password".toCharArray());
KeyManager[] km = keyManagerFactory.getKeyManagers();
// Create trust manager
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
trustManagerFactory.init(keyStore);
TrustManager[] tm = trustManagerFactory.getTrustManagers();
// opens a secure socket with definied protocol
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
//System.out.println(keyStore.getCertificate("root").getPublicKey());
//System.out.println(keyStore.isKeyEntry("root"));
sslContext.init(km, tm, null);
return sslContext;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
// Start to run the server
public void run() {
SSLContext sslContext = this.createSSLContext();
try {
// Create server socket factory
SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory();
// Create server socket
SSLServerSocket sslServerSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(this.port);
System.out.println("SSL server started");
while (!isServerDone) {
SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
// Start the server thread
new ServerThread(sslSocket).start();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
// Thread handling the socket from client
static class ServerThread extends Thread {
private SSLSocket sslSocket = null;
ServerThread(SSLSocket sslSocket) {
this.sslSocket = sslSocket;
}
public void run() {
sslSocket.setEnabledCipherSuites(sslSocket.getSupportedCipherSuites());
//System.out.println("HIER: " + sslSocket.getHandshakeSession());
//Klappt nicht, auch nicht, wenn der Client diese Zeile ebenfalls besitzt
//sslSocket.setEnabledCipherSuites(new String[]{"TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256"});
try {
// Start handshake
sslSocket.startHandshake();
// Get session after the connection is established
SSLSession sslSession = sslSocket.getSession();
System.out.println(sslSession.getPeerHost());
System.out.println(sslSession.getLocalCertificates());
System.out.println("\tProtocol : " + sslSession.getProtocol());
System.out.println("\tCipher suite : " + sslSession.getCipherSuite());
System.out.println("\tSession context : " + sslSession.getSessionContext());
//System.out.println("\tPeer pricipal of peer : " + sslSession.getPeerPrincipal());
// Start handling application content
InputStream inputStream = sslSocket.getInputStream();
OutputStream outputStream = sslSocket.getOutputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(outputStream));
String line = null;
String[] suites = sslSocket.getSupportedCipherSuites();
for (int i = 0; i < suites.length; i++) {
//System.out.println(suites[i]);
//System.out.println(sslSession.getCipherSuite());
}
while ((line = bufferedReader.readLine()) != null) {
System.out.println("Inut : " + line);
if (line.trim().isEmpty()) {
break;
}
}
// Write data
printWriter.print("HTTP/1.1 200\r\n");
printWriter.flush();
sslSocket.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
这是我的输出:
SSL server started
127.0.0.1
null
Protocol : TLSv1.2
Cipher suite : TLS_DH_anon_WITH_AES_128_GCM_SHA256
Session context : sun.security.ssl.SSLSessionContextImpl@781df1a4
我想知道,为什么这行
System.out.println(sslSession.getLocalCertificates());
打印出“null”?
非常感谢你,Mira
答案 0 :(得分:1)
Certificate[] getLocalCertificates()
返回在握手期间发送给对等方的证书。
注意:此方法仅在使用基于证书的密码套件时才有用。
当多个证书可用于握手时,实现会选择它认为可用的“最佳”证书链,并将其传输到另一方。此方法允许调用者知道实际使用的证书链。
<强>返回:强>
一个有序的证书数组,首先是本地证书,然后是任何证书颁发机构。如果没有发送证书,则返回
null
。
我们关心的部分是“返回在握手期间发送给对等方的证书。”和“此方法仅在使用基于证书的密码套件时才有用。”。
鉴于它正在返回null
,我们可以假设您没有向客户端发送任何证书。但它也是HTTPS,那么它给出了什么?好吧,看起来你正在使用TLS_DH_anon_WITH_AES_128_GCM_SHA256
,顾名思义,匿名。根据{{3}}:
Anonymous Diffie-Hellman 使用Diffie-Hellman,但没有身份验证。由于交换中使用的密钥未经过身份验证,因此协议容易受到中间人攻击。注意:如果您使用此方案,则对
SSL_get_peer_certificate
的调用将返回NULL
,因为您已选择匿名协议。这是SSL_get_peer_certificate
在正常情况下唯一允许返回NULL
的时间。
虽然这适用于OpenSSL,但它在Java中似乎是相同的 - 也就是说,您没有使用基于证书的密码。对TLS有更多了解的人需要加入,但看起来生成了AES密钥 ,并且它们被发送到客户端,但客户端无法保证这些密钥来自您,而通常你会生成密钥,然后使用RSA密钥对这些密钥进行签名/加密(不是100%肯定),以证明它们来自你。
要解决这个问题,我相信你需要选择一个不同的密码套件,例如: TLS_RSA_WITH_AES_128_GCM_SHA256
。我不是100%肯定如何你会这样做,但这似乎是解决方案。
答案 1 :(得分:0)
sslSocket.setEnabledCipherSuites(sslSocket.getSupportedCipherSuites());
您正在启用所有匿名和低级密码套件,因此您允许服务器不发送证书,因此它不会发送证书,因此它不会在getLocalCertificates()
中为您提供证书
删除此行。