Java SSLHandshakeException:没有共同的密码套件

我正在尝试使用Java SSLSockets将安全性应用于简单的聊天应用程序。

我创建了一个自签名CA,并在其中签署了两个证书(所有使用的RSA密钥),一个用于服务器,一个用于客户端。 之后,我将证书导入服务器的密钥库,并导入客户端的另一个密钥库。

CA : openssl genrsa -out ca.key 1024 -rsa openssl req -new -key ca.key -out ca.csr openssl x509 -req -days 365 -in ca.csr -out ca.crt -signkey ca.key SERVER CERTIFICATE: openssl genrsa -out server.key 1024 -rsa openssl req -new -key server.key -out server.csr openssl ca -in server.csr -cert ca.crt -keyfile ca.key -out server.crt CLIENT CERTIFICATE : openssl genrsa -out client.key 1024 -rsa openssl req -new -key client.key -out client.csr openssl ca -in client.csr -cert ca.crt -keyfile ca.key -out client.crt KEYSTORES: keytool -import -keystore serverkeystore -file ca.crt -alias theCARoot keytool -import -keystore serverkeystore -file server.crt -alias servercrt keytool -import -keystore serverkeystore -file client.crt -alias clientcrt keytool -import -keystore clientkeystore -file ca.crt -alias theCARoot keytool -import -keystore clientkeystore -file server.crt -alias servercrt keytool -import -keystore clientkeystore -file client.crt -alias clientcrt 

我想使用特定的密码,但显然没有任何支持的密码可以工作。

我的客户代码:

  import java.net.*; import java.io.*; import java.security.*; import java.security.cert.CertificateException; import javax.net.ssl.*; public class ChatClient implements Runnable { private SSLSocket socket = null; private Thread thread = null; private DataInputStream console = null; private DataOutputStream streamOut = null; private ChatClientThread client = null; final String[] enabledCipherSuites = {"TLS_RSA_WITH_AES_256_CBC_SHA256"}; final char[] passphrase = "123456".toCharArray(); public ChatClient(String serverName, int serverPort) { System.out.println("Establishing connection to server..."); try { SSLSocketFactory factory = null; SSLContext ctx = SSLContext.getInstance("TLS"); KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); KeyStore ks= KeyStore.getInstance("JKS"); ks.load(new FileInputStream("clientkeystore"), passphrase); kmf.init(ks, passphrase); KeyStore serverKey = KeyStore.getInstance("JKS"); serverKey.load(new FileInputStream("serverkeystore"),passphrase); TrustManagerFactory trustManager = TrustManagerFactory.getInstance("SunX509"); trustManager.init(serverKey); ctx.init(kmf.getKeyManagers(), trustManager.getTrustManagers(), null); factory = ctx.getSocketFactory(); socket = (SSLSocket)factory.createSocket(serverName, serverPort); socket.setEnabledCipherSuites(enabledCipherSuites); start(); } catch(UnknownHostException uhe) { // Host unkwnown System.out.println("Error establishing connection - host unknown: " + uhe.getMessage()); } catch(IOException ioexception) { // Other error establishing connection System.out.println("Error establishing connection - unexpected exception: " + ioexception.getMessage()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (KeyStoreException e) { e.printStackTrace(); } catch (CertificateException e) { e.printStackTrace(); } catch (UnrecoverableKeyException e) { e.printStackTrace(); } catch (KeyManagementException e) { e.printStackTrace(); } } public void run() { while (thread != null) { try { // Sends message from console to server streamOut.writeUTF(console.readLine()); streamOut.flush(); } catch(IOException ioexception) { System.out.println("Error sending string to server: " + ioexception.getMessage()); stop(); } } } public void handle(String msg) { // Receives message from server if (msg.equals(".quit")) { // Leaving, quit command System.out.println("Exiting...Please press RETURN to exit ..."); stop(); } else // else, writes message received from server to console System.out.println(msg); } // Inits new client thread public void start() throws IOException { console = new DataInputStream(System.in); streamOut = new DataOutputStream(socket.getOutputStream()); if (thread == null) { client = new ChatClientThread(this, socket); thread = new Thread(this); thread.start(); } } // Stops client thread public void stop() { if (thread != null) { thread.stop(); thread = null; } try { if (console != null) console.close(); if (streamOut != null) streamOut.close(); if (socket != null) socket.close(); } catch(IOException ioe) { System.out.println("Error closing thread..."); } client.close(); client.stop(); } public static void main(String args[]) { ChatClient client = null; if (args.length != 2) // Displays correct usage syntax on stdout System.out.println("Usage: java ChatClient host port"); else // Calls new client client = new ChatClient(args[0], Integer.parseInt(args[1])); } } class ChatClientThread extends Thread { private SSLSocket socket = null; private ChatClient client = null; private DataInputStream streamIn = null; public ChatClientThread(ChatClient _client, SSLSocket _socket) { client = _client; socket = _socket; open(); start(); } public void open() { try { streamIn = new DataInputStream(socket.getInputStream()); } catch(IOException ioe) { System.out.println("Error getting input stream: " + ioe); client.stop(); } } public void close() { try { if (streamIn != null) streamIn.close(); } catch(IOException ioe) { System.out.println("Error closing input stream: " + ioe); } } public void run() { while (true) { try { client.handle(streamIn.readUTF()); } catch(IOException ioe) { System.out.println("Listening error: " + ioe.getMessage()); client.stop(); } } } } 

而对于服务器:

 import java.net.*; import java.io.*; import java.security.*; import java.security.cert.CertificateException; import java.util.Arrays; import javax.net.ServerSocketFactory; import javax.net.ssl.*; public class ChatServer implements Runnable { private ChatServerThread clients[] = new ChatServerThread[20]; private SSLServerSocket server_socket = null; private Thread thread = null; private int clientCount = 0; final String[] enabledCipherSuites = {"TLS_RSA_WITH_AES_256_CBC_SHA256"}; final char[] passphrase = "123456".toCharArray(); public ChatServer(int port) { try { // Binds to port and starts server System.out.println("Binding to port " + port); SSLContext ctx = SSLContext.getInstance("TLS");; KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); KeyStore ks = KeyStore.getInstance("JKS"); ks.load(new FileInputStream("serverkeystore"), passphrase); kmf.init(ks, passphrase); KeyStore serverKey = KeyStore.getInstance("JKS"); serverKey.load(new FileInputStream("clientkeystore"),passphrase); TrustManagerFactory trustManager = TrustManagerFactory.getInstance("SunX509"); trustManager.init(serverKey); ctx.init(kmf.getKeyManagers(), trustManager.getTrustManagers(), null); SSLServerSocketFactory ssf = ctx.getServerSocketFactory(); server_socket = (SSLServerSocket) ssf.createServerSocket(port); server_socket.setEnabledCipherSuites(enabledCipherSuites); server_socket.setNeedClientAuth(true); System.out.println("Server started: " + server_socket); start(); } catch(IOException ioexception) { // Error binding to port System.out.println("Binding error (port=" + port + "): " + ioexception.getMessage()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (KeyStoreException e) { e.printStackTrace(); } catch (CertificateException e) { e.printStackTrace(); } catch (UnrecoverableKeyException e) { e.printStackTrace(); } catch (KeyManagementException e) { e.printStackTrace(); } } public void run() { while (thread != null) { try { // Adds new thread for new client System.out.println("Waiting for a client ..."); addThread((SSLSocket)server_socket.accept()); } catch(IOException ioexception) { System.out.println("Accept error: " + ioexception); stop(); } } } public void start() { if (thread == null) { // Starts new thread for client thread = new Thread(this); thread.start(); } } public void stop() { if (thread != null) { // Stops running thread for client thread.stop(); thread = null; } } private int findClient(int ID) { // Returns client from id for (int i = 0; i < clientCount; i++) if (clients[i].getID() == ID) return i; return -1; } public synchronized void handle(int ID, String input) { if (input.equals(".quit")) { int leaving_id = findClient(ID); // Client exits clients[leaving_id].send(".quit"); // Notify remaing users for (int i = 0; i < clientCount; i++) if (i!=leaving_id) clients[i].send("Client " +ID + " exits.."); remove(ID); } else // Brodcast message for every other client online for (int i = 0; i = 0) { // Removes thread for exiting client ChatServerThread toTerminate = clients[pos]; System.out.println("Removing client thread " + ID + " at " + pos); if (pos < clientCount-1) for (int i = pos+1; i < clientCount; i++) clients[i-1] = clients[i]; clientCount--; try { toTerminate.close(); } catch(IOException ioe) { System.out.println("Error closing thread: " + ioe); } toTerminate.stop(); } } private void addThread(SSLSocket socket) { if (clientCount < clients.length) { // Adds thread for new accepted client System.out.println("Client accepted: " + socket); clients[clientCount] = new ChatServerThread(this, socket); try { clients[clientCount].open(); clients[clientCount].start(); clientCount++; } catch(IOException ioe) { System.out.println("Error opening thread: " + ioe); } } else System.out.println("Client refused: maximum " + clients.length + " reached."); } public static void main(String args[]) { ChatServer server = null; if (args.length != 1) // Displays correct usage for server System.out.println("Usage: java ChatServer port"); else // Calls new server server = new ChatServer(Integer.parseInt(args[0])); } } class ChatServerThread extends Thread { private ChatServer server = null; private SSLSocket socket = null; private int ID = -1; private DataInputStream streamIn = null; private DataOutputStream streamOut = null; public ChatServerThread(ChatServer _server, SSLSocket _socket) { super(); server = _server; socket = _socket; ID = socket.getPort(); } // Sends message to client public void send(String msg) { try { streamOut.writeUTF(msg); streamOut.flush(); } catch(IOException ioexception) { System.out.println(ID + " ERROR sending message: " + ioexception.getMessage()); server.remove(ID); stop(); } } // Gets id for client public int getID() { return ID; } // Runs thread public void run() { System.out.println("Server Thread " + ID + " running."); while (true) { try { server.handle(ID, streamIn.readUTF()); } catch(IOException ioe) { System.out.println(ID + " ERROR reading: " + ioe.getMessage()); server.remove(ID); stop(); } } } // Opens thread public void open() throws IOException { streamIn = new DataInputStream(new BufferedInputStream(socket.getInputStream())); streamOut = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream())); } // Closes thread public void close() throws IOException { if (socket != null) socket.close(); if (streamIn != null) streamIn.close(); if (streamOut != null) streamOut.close(); } } 

对不起,如果我的英语有点生疏了。

我的操作系统是OS X El Capitan,Java版本是1.8。

 Here is the server's stack trace: /Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/bin/java -Djavax.net.debug=all -Didea.launcher.port=7536 "-Didea.launcher.bin.path=/Applications/IntelliJ IDEA 15.app/Contents/bin" -Dfile.encoding=UTF-8 -classpath "/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/lib/ant-javafx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/lib/javafx-mx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/lib/packager.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/lib/tools.jar:/Users/joaogoncalves/Dropbox/STI/out/production/STI Assignment 3:/Applications/IntelliJ IDEA 15.app/Contents/lib/idea_rt.jar" com.intellij.rt.execution.application.AppMain ChatServer 5000 Binding to port 5000 adding as trusted cert: Subject: CN=Joao Goncalves, OU=DEQ, O=UC, ST=Coimbra, C=PT Issuer: CN=DEI, OU=DEI, O=UC, L=Coimbra, ST=Coimbra, C=PT Algorithm: RSA; Serial number: 0xc94895f3863a5c36 Valid from Mon May 23 23:43:42 WEST 2016 until Tue May 23 23:43:42 WEST 2017 adding as trusted cert: Subject: CN=www.uc.pt, OU=DEM, O=UC, ST=Coimbra, C=PT Issuer: CN=DEI, OU=DEI, O=UC, L=Coimbra, ST=Coimbra, C=PT Algorithm: RSA; Serial number: 0xc94895f3863a5c35 Valid from Mon May 23 23:42:54 WEST 2016 until Tue May 23 23:42:54 WEST 2017 adding as trusted cert: Subject: CN=DEI, OU=DEI, O=UC, L=Coimbra, ST=Coimbra, C=PT Issuer: CN=DEI, OU=DEI, O=UC, L=Coimbra, ST=Coimbra, C=PT Algorithm: RSA; Serial number: 0xdb931da4e1abec22 Valid from Mon May 23 23:42:03 WEST 2016 until Tue May 23 23:42:03 WEST 2017 trigger seeding of SecureRandom done seeding SecureRandom Server started: [SSL: ServerSocket[addr=0.0.0.0/0.0.0.0,localport=5000]] Waiting for a client ... Allow unsafe renegotiation: false Allow legacy hello messages: true Is initial handshake: true Is secure renegotiation: false Client accepted: 74ce57fc[SSL_NULL_WITH_NULL_NULL: Socket[addr=/127.0.0.1,port=57519,localport=5000]] Waiting for a client ... Server Thread 57519 running. Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA256 for TLSv1 No available cipher suite for TLSv1 Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA256 for TLSv1.1 No available cipher suite for TLSv1.1 [Raw read]: length = 5 0000: 16 03 03 00 52 ....R [Raw read]: length = 82 0000: 01 00 00 4E 03 03 57 44 7B 3B B8 1E 77 88 AF 4E ...N..WD.;..w..N 0010: C7 CA 73 CE AC 38 62 5D 18 BD 9A 16 7E 25 86 25 ..s..8b].....%.% 0020: 36 1C EF F5 B6 FF 00 00 02 00 3D 01 00 00 23 00 6.........=...#. 0030: 0D 00 1A 00 18 06 03 06 01 05 03 05 01 04 03 04 ................ 0040: 01 03 03 03 01 02 03 02 01 02 02 01 01 FF 01 00 ................ 0050: 01 00 .. Thread-1, READ: TLSv1.2 Handshake, length = 82 *** ClientHello, TLSv1.2 RandomCookie: GMT: 1464105787 bytes = { 184, 30, 119, 136, 175, 78, 199, 202, 115, 206, 172, 56, 98, 93, 24, 189, 154, 22, 126, 37, 134, 37, 54, 28, 239, 245, 182, 255 } Session ID: {} Cipher Suites: [TLS_RSA_WITH_AES_256_CBC_SHA256] Compression Methods: { 0 } Extension signature_algorithms, signature_algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA224withECDSA, SHA224withRSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA, MD5withRSA Extension renegotiation_info, renegotiated_connection:  *** [read] MD5 and SHA1 hashes: len = 82 0000: 01 00 00 4E 03 03 57 44 7B 3B B8 1E 77 88 AF 4E ...N..WD.;..w..N 0010: C7 CA 73 CE AC 38 62 5D 18 BD 9A 16 7E 25 86 25 ..s..8b].....%.% 0020: 36 1C EF F5 B6 FF 00 00 02 00 3D 01 00 00 23 00 6.........=...#. 0030: 0D 00 1A 00 18 06 03 06 01 05 03 05 01 04 03 04 ................ 0040: 01 03 03 03 01 02 03 02 01 02 02 01 01 FF 01 00 ................ 0050: 01 00 .. %% Initialized: [Session-1, SSL_NULL_WITH_NULL_NULL] %% Invalidated: [Session-1, SSL_NULL_WITH_NULL_NULL] Thread-1, SEND TLSv1.2 ALERT: fatal, description = handshake_failure Thread-1, WRITE: TLSv1.2 Alert, length = 2 [Raw write]: length = 7 0000: 15 03 03 00 02 02 28 ......( Thread-1, called closeSocket() Thread-1, handling exception: javax.net.ssl.SSLHandshakeException: no cipher suites in common 57519 ERROR reading: no cipher suites in common Removing client thread 57519 at 0 Thread-1, called close() Thread-1, called closeInternal(true) Thread-1, called close() Thread-1, called closeInternal(true) Thread-1, called close() Thread-1, called closeInternal(true) Process finished with exit code 130 

每个经过身份validation的一方的密钥库,始终是服务器,此处客户端也因为您指定了NeedClientAuth必须具有 NeedClientAuth 和证书 ,而不仅仅是证书。 有两种方法可以做到这一点:

  • 将OpenSSL生成的私钥和相关证书转换为PKCS#12,然后将PKCS#12转换为JKS,或者只使用Java中的PKCS#12(即使您指定JKS,JCE也可以处理它,以及最新版本的Java8! – 请参阅密钥库兼容模式下的http://www.oracle.com/technetwork/java/javase/8u60-relnotes-2620227.html 。 看到:

    如何导入Java密钥库中的现有x509证书和私钥以在SSL中使用?
    如何从现有证书(abc.crt)和abc.key文件创建密钥库?
    在Java KeyStore中导入私钥/公钥证书对
    将证书从pem转换为jks (披露:我的)
    如何从cer文件创建密钥库 (披露:我的)

  • 使用Java生成EE私钥和CSR,然后使用OpenSSL(带CA密钥和证书)颁发EE证书,并将证书导回Java密钥库:

     keytool -keystore server.jks -genkeypair -keyalg RSA # before j7 best to add -keysize 2048 see below keytool -keystore server.jks -certreq >server.csr openssl ca -in server.csr ... -out server.crt # or submit the CSR to a real CA and get its response # then either install the chain all at once: cat server.crt ca.crt >temp keytool -keystore server.jks -importcert -file temp # and confirm (need temp so stdin available for confirm; # if using a public CA, can add -trustcacerts and use pipe instead) # or install the certs separately, top down: keytool -keystore server.jks -importcert -file ca.crt -alias ca # and confirm, THEN keytool -keystore server.jks -importcert -file server.crt # (last) response must be 'Certificate reply was installed' # NOT merely 'Certificate was added' which means you messed up # and similarly for client 

后一种方法(ca.crt和privatekey + server.crt的单独条目)具有以下优点:同一文件可以用作密钥库和信任库,您不需要将serverkey用作clienttrust,反之亦然。 如果这些是真实的系统,这将是一个安全的好处。

最后一点:您应该开始使用RSA 2048位密钥。 自2014年以来,NIST和CABforum等机构已经禁止1024位,尽管Java仍然接受它们,但大多数浏览器和许多其他工具已经警告过它们,并且可能很快就会拒绝它们。 出于类似的原因,您应该使用至少SHA256签署证书 – 这可以在ca使用的配置文件中设置,或者您可以使用命令行标志-md sha256

我从未见过KeyManagerFactory以你的方式声明,我通常会这样看:

 KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 

我意识到我错过了第二个链接给你:

http://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/JSSERefGuide.html#Debug

您需要完成SSL调试。 但是,您需要明确启用某些密码(如果我没记错),并建议使用该路径。