Fixing Java SSL Handshake Errors

This guide is not limited to qFT8 and can also be applied to other Java-based applications that use their own Java Runtime Environment or truststore. The focus is on qFT8 simply because it was the application involved in the specific use case that prompted this article.

When connecting qFT8 to a self-hosted Wavelog instance, the connection may fail with an SSL handshake error:

Couldn't connect: Could reach Wavelog secure connection failed (SSLHandshakeException).
Check the address and the network.

💡 This can happen even when the Wavelog website opens correctly in a browser and the Windows or Linux operating system already trusts the private Certificate Authority (CA).

Why does this happen?

Applications such as qFT8 may ship with their own Java Runtime Environment (JRE), including a separate Java truststore.

Consequently, there can be two different certificate trust configurations:

  • System truststore: Contains the trusted CAs configured in the Linux operating system.
  • Application truststore: Contains the trusted CAs used by the Java runtime bundled with qFT8.

Adding a private CA to the Linux system truststore does not necessarily make it available to an application’s bundled Java runtime.

The solution is to import the private CA certificates into qFT8’s own Java truststore.

Fixing the SSL Handshake Error on Windows

The Windows version of qFT8 also ships with its own Java Runtime Environment (JRE). Therefore, adding private CA certificates to the Windows certificate store does not necessarily make them available to qFT8.

The following procedure identifies the bundled Java runtime and imports the required CA certificates into its dedicated truststore.

Step 1: Find the qFT8 Java Runtime

Start the qFT8 Application and open PowerShell

⚠️ Important prerequisite: qFT8 must be running before executing the following PowerShell commands. The commands identify the Java process started by qFT8. If the application is not running, the required process information will not be available.

Run the following command to identify the Java process started by qFT8:

Get-CimInstance Win32_Process |
  Where-Object {
    $_.Name -match 'qft8|java|javaw'
  } |
  Select-Object Name, ProcessId, ParentProcessId, ExecutablePath, CommandLine |
  Format-List

Output:
Name            : qFT8.exe
ProcessId       : <PID>
ParentProcessId : <PARENT_PID>
ExecutablePath  : C:\Users\<USERNAME>\Desktop\qFT8\qFT8.exe
CommandLine     : "C:\Users\<USERNAME>\Desktop\qFT8\qFT8.exe"

Name            : javaw.exe
ProcessId       : <PID>
ParentProcessId : <QFT8_PID>
ExecutablePath  : C:\Users\<USERNAME>\AppData\Local\qFT8\program\qFT8\<VERSION>\jre\bin\javaw.exe
CommandLine     : "C:\Users\<USERNAME>\AppData\Local\qFT8\program\qFT8\<VERSION>\jre\bin\javaw.exe"
                  -XX:+AutoCreateSharedArchive -XX:SharedArchiveFile=qft8.jsa -XX:InitialRAMPercentage=0.5
                  -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=30 -XX:+ShrinkHeapInSteps
                  -XX:G1PeriodicGCInterval=15000 -XX:G1PeriodicGCSystemLoadThreshold=0
                  "-Djava.library.path=C:\Users\<USERNAME>\AppData\Local\qFT8\program\qFT8\<VERSION>"
                  -jar "C:\Users\<USERNAME>\AppData\Local\qFT8\program\qFT8\<VERSION>\qft8-desktop.jar"

Look for the javaw.exe process. Its executable path identifies the bundled Java runtime. The actual version directory may differ between installations.

Step 2: Locate the Java Truststore

Search for the cacerts file inside the qFT8 installation directory:

Get-ChildItem "$env:LOCALAPPDATA\qFT8" `
  -Filter cacerts -Recurse -File `
  -ErrorAction SilentlyContinue

The result should point to a location similar to:

C:\Users\<username>\AppData\Local\qFT8\program\qFT8\<version>\jre\lib\security\cacerts

Output:
>>   -Filter cacerts -Recurse -File `
>>   -ErrorAction SilentlyContinue

Verzeichnis: C:\Users\<USER>\AppData\Local\qFT8\program\qFT8\<HASH>\jre\lib\security

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----        30.10.2026     10:02         153428 cacerts

This is the truststore used by the bundled Java runtime.

Step 3: Prepare the Java Environment

Close qFT8 completely.

Set the paths to the bundled JRE and its truststore. Replace <HASH> with the directory identified in the previous steps:

$QFT8_JRE = "C:\Users\<USER>\AppData\Local\qFT8\program\qFT8\<HASH>\jre"
$KEYTOOL = "$QFT8_JRE\bin\keytool.exe"
$TRUSTSTORE = "$QFT8_JRE\lib\security\cacerts"

Place your private CA certificates in a convenient directory, for example:

C:\Certificates\ExampleCorp Root-CA.crt
C:\Certificates\ExampleCorp Intermediate-CA.crt

Step 4: Back Up the Truststore

Before modifying the Java truststore, create a backup:

Copy-Item $TRUSTSTORE "$TRUSTSTORE.backup"

Step 5: Import the Root CA

Import the root CA certificate:

& $KEYTOOL -importcert `
  -alias examplecorp-root-ca `
  -file "C:\Certificates\ExampleCorp Root-CA.crt" `
  -keystore $TRUSTSTORE

Enter the truststore password when prompted. The default Java truststore password is usually changeit.

Confirm the certificate import by entering:

Trust this certificate? [No]:  yes
Certificate was added to the keystore

Step 6: Import the Intermediate CA

Import the intermediate CA certificate:

& $KEYTOOL -importcert `
  -alias examplecorp-intermediate-ca `
  -file "C:\Certificates\ExampleCorp Intermediate-CA.crt" `
  -keystore $TRUSTSTORE

Again, enter the truststore password and confirm the import with yes.

Step 7: Verify the Imported Certificates

Check that both CA certificates are present:

& $KEYTOOL -list `
  -keystore $TRUSTSTORE `
  -storepass changeit |
  Select-String "examplecorp"

The output should contain both entries:

examplecorp-root-ca
examplecorp-intermediate-ca

Step 8: Restart qFT8

Start qFT8 again and test the connection to your Wavelog instance.

The bundled Java runtime should now trust the private PKI and be able to establish the HTTPS connection.

Important: The qFT8 installation directory may change when the applicatio is updated. If a future update installs a new bundled JRE, the CA certificates may need to be imported into its truststore again.

Fixing the SSL Handshake Error on Linux

The Linux version of qFT8 also ships with its own Java Runtime Environment (JRE). Therefore, adding private CA certificates to the Linux certificate store does not necessarily make them available to qFT8.

The following procedure identifies the bundled Java runtime and imports the required CA certificates into its dedicated truststore.

Step 1: Verify the System Certificate Trust

On AlmaLinux, trusted CA certificates are commonly stored in:

/etc/pki/ca-trust/source/anchors/

Check whether your private CA certificates are present:

ls -l /etc/pki/ca-trust/source/anchors/

For example:

ExampleCorp Root-CA.crt
ExampleCorp Intermediate-CA.crt

Verify that the system recognizes the CA:

trust list | grep -i examplecorp

You can also test the HTTPS connection independently:

openssl s_client \
  -connect wavelog.examplecorp.io:443 \
  -servername wavelog.examplecorp.io \
  -verify_return_error

Look for:

Verify return code: 0 (ok)

This confirms that OpenSSL can validate the server certificate using the system trust configuration.

Step 2: Verify the System Java Truststore

Check the installed Java version:

java -version

On AlmaLinux 9, the system Java truststore is typically available at:

/etc/pki/java/cacerts

Check whether the private CA certificates are already registered:

sudo keytool -list \
  -keystore /etc/pki/java/cacerts \
  -storepass changeit | grep -i examplecorp

If the root and intermediate CA certificates appear, the system Java truststore already knows your private PKI.

You can also test HTTPS using Java directly:

jshell

Enter the following commands at the jshell> prompt:

var url = new java.net.URL("https://wavelog.examplecorp.io");
var conn = url.openConnection();
conn.connect();
System.out.println("SSL connection successful");

Exit JShell:

/exit

If the connection succeeds, the system Java runtime can establish a trusted HTTPS connection.

Step 3: Find the Java Runtime used by qFT8

💡 qFT8 use a bundled JRE instead of the system Java installation.

Find the running application process:

ps aux | grep -i '[q]ft8'

Look for the Java executable in the process command line. A typical bundled runtime path looks like this:

~/.cache/qft8/<version>/jre/bin/java

For example:

~/.cache/qft8/v3.23.1-example-x86_64/jre/bin/java

Set the runtime path in a shell variable, adapting the version directory to your installation:

QFT8_JRE="$HOME/.cache/qft8/v3.23.1-example-x86_64/jre"

Check whether its Java truststore exists:

ls -l "$QFT8_JRE/lib/security/cacerts"

Now check whether your private CA is already registered:

"$QFT8_JRE/bin/keytool" -list \
  -keystore "$QFT8_JRE/lib/security/cacerts" \
  -storepass changeit | grep -i examplecorp

If no matching entries are returned, the bundled Java runtime does not contain your private CA certificates.

Step 4: Back up the qFT8 Truststore

⚠️ Close qFT8 completely before making changes.

Create a backup:

cp "$QFT8_JRE/lib/security/cacerts" \
   "$QFT8_JRE/lib/security/cacerts.backup"

This allows you to restore the original truststore if necessary.

Step 5: Import the Private CA Certificates

Use the existing CA certificate files from the Linux system trust directory.

Import the root CA:

"$QFT8_JRE/bin/keytool" -importcert \
  -alias examplecorp-root-ca \
  -file "/etc/pki/ca-trust/source/anchors/ExampleCorp Root-CA.crt" \
  -keystore "$QFT8_JRE/lib/security/cacerts"

Import the intermediate CA:

"$QFT8_JRE/bin/keytool" -importcert \
  -alias examplecorp-intermediate-ca \
  -file "/etc/pki/ca-trust/source/anchors/ExampleCorp Intermediate-CA.crt" \
  -keystore "$QFT8_JRE/lib/security/cacerts"

When prompted:

  • Enter the truststore password, usually changeit.
  • Confirm the certificate import by entering yes.

Only import CA certificates whose authenticity and fingerprints you have verified.

Step 6: Verify the imported certificates

Check the qFT8 truststore again:

"$QFT8_JRE/bin/keytool" -list \
  -keystore "$QFT8_JRE/lib/security/cacerts" \
  -storepass changeit | grep -i examplecorp

You should now see both entries:

examplecorp-root-ca
examplecorp-intermediate-ca

Step 7: Restart qFT8 and test the connection

  • Start qFT8 again and test the Wavelog connection.
  • The application should now be able to validate the HTTPS certificate through its own Java truststore.

Important notes

  • This procedure applies to qFT8 installations that use a bundled Java runtime with an independent truststore.
  • The system Java truststore and the bundled qFT8 truststore are separate configurations.
  • Updating the qFT8 application or replacing its bundled JRE may replace the truststore and require importing the CA certificates again.
  • Always use the actual CA certificates from your trusted PKI. Never import an unverified server certificate as a workaround.
  • If the problem persists, also check the certificate’s Subject Alternative Name (SAN), validity period, certificate chain and hostname configuration.

Summary

A working HTTPS connection does not guarantee that every Java application trusts the same certificates. When an application ships with its own JRE, its truststore must also contain the private CA certificates.