I’m building a small desktop application which will connect to a server I own and transfer some encrypted data via an SSLServerSocket for the server, and a normal Socket for the client. To do this, I’m using Keystores I’ve created myself which contain the Server’s certificates. The Client side is working fine, it reports connecting to the server, and never throws an error. It doesn’t even end the socket connection when the server-side crashes. The server itself logs the client connecting without issue, but as soon as I try to initialize a BufferedWriter object, it crashes with the following stack trace;
javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_unknown
at sun.security.ssl.Alert.createSSLException(Alert.java:131)
at sun.security.ssl.Alert.createSSLException(Alert.java:117)
at sun.security.ssl.TransportContext.fatal(TransportContext.java:318)
at sun.security.ssl.Alert$AlertConsumer.consume(Alert.java:293)
at sun.security.ssl.TransportContext.dispatch(TransportContext.java:185)
at sun.security.ssl.SSLTransport.decode(SSLTransport.java:152)
at sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1397)
at sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1305)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:440)
at sun.security.ssl.SSLSocketImpl.ensureNegotiated(SSLSocketImpl.java:818)
at sun.security.ssl.SSLSocketImpl.access$200(SSLSocketImpl.java:73)
at sun.security.ssl.SSLSocketImpl$AppInputStream.read(SSLSocketImpl.java:909)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at ClientHandler.<init>(ClientHandler.java:27)
at KillSwitchServer.run(KillSwitchServer.java:55)
at KillSwitchServer.main(KillSwitchServer.java:38)
Line 27 of ClientHandler (A class that exists exclusively server-side) is where the BufferedWriter initialization takes place.
I’m currently using JDK 1.8, though I have also tried switching to version 21, and got the same results. From what I’ve found on this error already, it seems like it almost always occurs exclusively client-side, so I’m at a bit of a loss here. I’ve gone through and verified that my keystores are both correct several times, and that all certificates are active and non-expired. When connecting to my server through google, the browser confirms that the certificate is up to date and the webpage is secured with HTTPS. Just to be safe I also added the Server’s certificates to the cacerts file of the JDK I’m using, though since the program environment uses it’s own truststore, this shouldn’t have effected anything, and no changes were noticed.
Here’s the important bits of the KillSwitchServer and ClientHandler files, which handles initializing the SSLServerSocket and connecting the client
public class KillSwitchServer {
private static final int PORT_NUMBER = 2048;
private static final String KEYSTORE_FILE = "Server_Keystore.jks";
private static final String KEYSTORE_PASSWORD = "Server_Keystore_password";
public static void main(String[] args) throws IOException{
System.setProperty("javax.net.ssl.keyStore", KEYSTORE_FILE);
System.setProperty("javax.net.ssl.keyStorePassword", KEYSTORE_PASSWORD);
SSLServerSocketFactory factory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
SSLServerSocket serverSocket = (SSLServerSocket) factory.createServerSocket(PORT_NUMBER);
new KillSwitchServer(serverSocket).run();
}
private SSLServerSocket serverSocket;
public KillSwitchServer(SSLServerSocket serverSocket){
this.serverSocket = serverSocket;
}
public void run(){
try{
while(!serverSocket.isClosed()){
Socket clientSocket = serverSocket.accept();
System.out.println("Client connected.");
ClientHandler clientHandler = new ClientHandler(clientSocket);
new Thread(clientHandler).start();
}
}catch(IOException e){
closeServerSocket();
}
}
// ****** CLIENT HANDLER - Normally a separate file *********
public class ClientHandler implements Runnable {
public static ClientHandler currentClient;
private Socket clientSocket;
private BufferedReader clientReader;
private BufferedWriter clientWriter;
private String encodedKey;
private String passwordHash;
public ClientHandler(Socket clientSocket) {
try{
this.clientSocket = clientSocket;
this.clientReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
//Crash either occurs here
this.clientWriter = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));
//Or here
this.encodedKey = clientReader.readLine();
this.passwordHash = clientReader.readLine();
if(authenticateClient()){
currentClient = this;
System.out.println("Password correct.");
messageClient("Connection established.");
}else{
messageClient("Invalid password. Please try again.");
System.out.println("Password incorrect.");
closeConnection(clientSocket, clientReader, clientWriter);
}
}catch (IOException e) {
closeConnection(clientSocket, clientReader, clientWriter);
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (InvalidKeySpecException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException |
BadPaddingException e) {
throw new RuntimeException(e);
}
}
@Override
public void run() {
String clientMessage;
while(clientSocket.isConnected()) {
try{
clientMessage = clientReader.readLine();
processClientMessage(clientMessage);
}catch(IOException e){
closeConnection(clientSocket, clientReader, clientWriter);
break;
}
}
}
public void messageClient(String message){
try{
clientWriter.write(message);
clientWriter.newLine();
clientWriter.flush();
}catch(IOException e){
closeConnection(clientSocket, clientReader, clientWriter);
}
}
Any help on this would be really appreciated, thanks!
Lyonic is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.