我想构建TLS多线程客户端/服务器聊天应用程序,我能够使用ServerSocket构建简单的客户端服务器,但我在使用TLS时遇到问题:
我得到的错误来自服务器端:
收听[SSL:ServerSocket [addr = 0.0.0.0 / 0.0.0.0,localport = 786]] 来自21a947fe的连接[SSL_NULL_WITH_NULL_NULL: 插座[ADDR = / 127.0.0.1,端口= 39264,将localPort = 786]] javax.net.ssl.SSLHandshakeException:没有共同的密码套件 sun.security.ssl.Alerts.getSSLException(Alerts.java:192)at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1917)at at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:301)at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:291)at sun.security.ssl.ServerHandshaker.chooseCipherSuite(ServerHandshaker.java:1007) 在 sun.security.ssl.ServerHandshaker.clientHello(ServerHandshaker.java:724) 在 sun.security.ssl.ServerHandshaker.processMessage(ServerHandshaker.java:213) 在sun.security.ssl.Handshaker.processLoop(Handshaker.java:925)at at sun.security.ssl.Handshaker.process_record(Handshaker.java:860)at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1043)at at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1343) 在 sun.security.ssl.SSLSocketImpl.readDataRecord(SSLSocketImpl.java:909)
在客户端:
javax.net.ssl.SSLHandshakeException:收到致命警报: handshake_failure
这是我的服务器代码:
public class Server {
// The ServerSocket we'll use for accepting new connections
private SSLServerSocket server;
private final SSLContext sc;
private final Hashtable outputStreams;
// Constructor and while-accept loop all in one.
public Server(int port) throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException, KeyManagementException {
this.outputStreams = new Hashtable();
KeyStore ks = initKeyStore(KEYSTORE, "JKS", KEYSTORE_PASSWORD);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(
KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, KEYSTORE_PASSWORD.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init(ks);
this.sc = SSLContext.getInstance("TLS");
TrustManager[] trustManagers = tmf.getTrustManagers();
this.sc.init(kmf.getKeyManagers(), trustManagers, null);
// All we have to do is listen
listen(port);
}
private static final String KEYSTORE = "serverkeystore";
private static final String KEYSTORE_PASSWORD = "blackzero@server";
private static KeyStore initKeyStore(String keyStore, String keyStoreType, String keyPass) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
KeyStore ks = null;
try {
ks = KeyStore.getInstance(keyStoreType);
//ks.load(new FileInputStream(keyStore), keyPass.toCharArray());
ks.load(null, keyPass.toCharArray());
} catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException e) {
Logger.getLogger(CNS.class.getName()).
log(Level.SEVERE, null, e);
//X509Certificate certificate = generateX509Certificate("CNS");
ks = KeyStore.getInstance(keyStoreType);
ks.load(null, keyPass.toCharArray());
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
KeyPair kp = kpg.genKeyPair();
Key publicKey = kp.getPublic();
Key privateKey = kp.getPrivate();
Certificate[] certChain = new Certificate[1];
certChain[0] = certificate;
ks.setKeyEntry("key1", privateKey, KEYSTORE_PASSWORD.toCharArray(), certChain);
try (FileOutputStream writeStream = new FileOutputStream(keyStore)) {
ks.store(writeStream, keyPass.toCharArray());
}
}
return ks;
}
private static X509Certificate generateX509Certificate(String certificateName) throws CertificateException, FileNotFoundException, IOException {
InputStream inStream = null;
X509Certificate cert = null;
try {
inStream = new FileInputStream(certificateName);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
cert = (X509Certificate) cf.generateCertificate(inStream);
} finally {
if (inStream != null) {
inStream.close();
}
}
return cert;
}
private void listen(int port) throws IOException {
// Create the ServerSocket
SSLServerSocketFactory ssf = sc.getServerSocketFactory();
server = (SSLServerSocket) ssf.createServerSocket(port);
//server = new ServerSocket(port);
// Tell the world we're ready to go
System.out.println("Listening on " + server);
// Keep accepting connections forever
while (true) {
// Grab the next incoming connection
SSLSocket s = (SSLSocket) server.accept();
//Socket s = server.accept();
// Tell the world we've got it
System.out.println("Connection from " + s);
// Create a DataOutputStream for writing data to the
// other side
DataOutputStream dout = new DataOutputStream(s.getOutputStream());
// Save this stream so we don't need to make it again
outputStreams.put(s, dout);
// Create a new thread for this connection, and then forget // about it
new ServerThread(this, s);
}
}
// Get an enumeration of all the OutputStreams, one for each client
// connected to us
Enumeration getOutputStreams() {
return outputStreams.elements();
}
// Send a message to all clients (utility routine)
void sendToAll(String message) {
// We synchronize on this because another thread might be
// calling removeConnection() and this would screw us up
// as we tried to walk through the list
synchronized (outputStreams) {
// For each client ...
for (Enumeration e = getOutputStreams(); e.hasMoreElements();) {
// ... get the output stream ...
DataOutputStream dout = (DataOutputStream) e.nextElement();
// ... and send the message
try {
dout.writeUTF(message);
} catch (IOException ie) {
System.out.println(ie);
}
}
}
}
// Remove a socket, and it's corresponding output stream, from our
// list. This is usually called by a connection thread that has
// discovered that the connectin to the client is dead.
void removeConnection(Socket s) {
// Synchronize so we don't mess up sendToAll() while it walks
// down the list of all output streamsa
synchronized (outputStreams) {
// Tell the world
System.out.println("Removing connection to " + s);
// Remove it from our hashtable/list
outputStreams.remove(s);
// Make sure it's closed
try {
s.close();
} catch (IOException ie) {
System.out.println("Error closing " + s);
ie.printStackTrace();
}
}
}}
客户代码:
public class Client implements Runnable {
private SSLSocket socket;
// The streams we communicate to the server; these come
// from the socket private
DataOutputStream dout;
private DataInputStream din;
// Constructor
public Client(String host, int port) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException, KeyManagementException, java.security.cert.CertificateException {
// Connect to the server
try {
// Initiate the connection
KeyStore ks = initKeyStore(KEYSTORE, "JKS", KEYSTORE_PASSWORD);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(
KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, KEYSTORE_PASSWORD.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init(ks);
SSLContext sc = SSLContext.getInstance("TLS");
TrustManager[] trustManagers = tmf.getTrustManagers();
sc.init(kmf.getKeyManagers(), trustManagers, null);
SSLSocketFactory ssf = sc.getSocketFactory();
socket = (SSLSocket) ssf.createSocket(host, port);
socket.startHandshake();
//socket = new Socket(host, port);
// We got a connection! Tell the world
System.out.println("connected to " + socket);
// Let's grab the streams and create DataInput/Output streams
// from them
din = new DataInputStream(socket.getInputStream());
dout = new DataOutputStream(socket.getOutputStream());
// Start a background thread for receiving messages
new Thread(this).start();
} catch (IOException ie) {
System.out.println(ie);
}
}
private static final String KEYSTORE = "clientkey.store";
private static final String KEYSTORE_PASSWORD = "blackzero@client";
private static KeyStore initKeyStore(String keyStore, String keyStoreType, String keyPass) throws KeyStoreException, IOException, NoSuchAlgorithmException, java.security.cert.CertificateException {
KeyStore ks = null;
try {
ks = KeyStore.getInstance(keyStoreType);
ks.load(null, keyPass.toCharArray());
} catch (KeyStoreException | IOException | NoSuchAlgorithmException | java.security.cert.CertificateException e) {
Logger.getLogger(Client.class.getName()).
log(Level.SEVERE, null, e);
ks.load(null, keyPass.toCharArray());
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
KeyPair kp = kpg.genKeyPair();
Key publicKey = kp.getPublic();
Key privateKey = kp.getPrivate();
ks.setKeyEntry("keyForSeckeyDecrypt", privateKey, null, null);
ks.setKeyEntry("keyForDigitalSignature", publicKey, null, null);
try (FileOutputStream writeStream = new FileOutputStream(keyStore)) {
ks.store(writeStream, keyPass.toCharArray());
}
}
return ks;
}
// Gets called when the user types something
private void processMessage(String message) {
try {
// Send it to the server
dout.writeUTF(message);
// Clear out text input field
//tf.setText("");
} catch (IOException ie) {
System.out.println(ie);
}
}
// Variables declaration - do not modify
// End of variables declaration
@Override
public void run() {
try {
// Receive messages one-by-one, forever
while (true) {
// Get the next message
String message = din.readUTF();
// Print it to our text window
//ta.append(message + "\n");
}
} catch (IOException ie) {
System.out.println(ie);
}
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
// Get the port # from the command line
int port = 786;
try {
Client c1 = new Client("localhost", port);
c1.processMessage("Hi c1 is here");
} catch (KeyStoreException
| NoSuchAlgorithmException
| CertificateException
| UnrecoverableKeyException
| KeyManagementException
| java.security.cert.CertificateException ex) {
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
}
}}
如果你能指出我正确的方向,我将不胜感激。
谢谢, Attiqe