Creating a Self-Signed Certificate Chain with Keytool
What is a Self-Signed Certificate? #
A self-signed certificate is an SSL certificate that is signed by the same entity that it identifies. Unlike certificates issued by Certificate Authorities (CAs), self-signed certificates are not verified by a trusted third party. They are commonly used for testing and development purposes, where the security requirements are lower, and the cost of obtaining a CA-signed certificate is prohibitive.
Why Create a Self-Signed Certificate Authority? #
When using self-signed certificates, in languages like Java, you will need to import the certificate into the truststore of the client application. This can be cumbersome when dealing with multiple certificates, as the client will need to import each certificate individually to its truststore. It can also be a maintenance overhead when certificates expire or new servers are added.
By creating a self-signed Certificate Authority (CA), you can issue certificates signed by your CA, and the client only needs to trust the CA certificate. This simplifies the process of managing multiple certificates and reduces the overhead of importing each certificate individually, as the client only needs to add the CA certificate to its truststore.
The following steps will guide you through the process of creating a self-signed CA certificate, a server certificate signed by the CA, and configuring a server to use the server certificate. We will also create a client truststore containing the CA certificate to establish trust with the server.
Server Certificates Setup #
The Java keytool will be used to create a self-signed CA certificate. The keytool is a key and certificate management utility that comes with the Java Development Kit (JDK). The following steps will guide you through the process of creating a self-signed CA certificate:
The steps for creating a self-signed CA certificate and server certificate are as follows are outlined in the following diagram:
By the end of these steps you should have a server keystore file server-keystore.jks containing the CA certificate and the server certificate signed by the CA. The server certificate is identified by the alias myserver, and the CA certificate is identified by the alias myca.
Step 1: Generate CA Key Pair #
First, generate a key pair for the CA certificate using the following command:
keytool -genkeypair -alias myca -keyalg RSA -keysize 2048 -keystore server-keystore.jks -storepass changeit -dname "CN=myca" -ext bc=ca:true -validity 3650
This command generates a key pair for the CA certificate with the alias myca using the RSA algorithm with a key size of 2048 bits. The keystore file server-keystore.jks is created with the password changeit, and the distinguished name (DN) of the CA certificate is set to CN=myca. The -ext bc=ca:true option indicates that the certificate is a CA certificate, and the -validity 3650 option sets the validity period of the certificate to 10 years (3650 days).
Step 2: Generate Server Key Pair #
Next, generate a key pair for the server certificate using the keytool command:
keytool -genkeypair -alias myserver -keyalg RSA -keysize 2048 -keystore server-keystore.jks -storepass changeit -dname "CN=myserver"
This command generates a key pair for the server certificate with the alias myserver using the RSA algorithm with a key size of 2048 bits. The keystore file server.jks is created with the password changeit, and the distinguished name (DN) of the server certificate is set to CN=myserver.
However, the server certificate is sef-signed and not signed by the CA. We will sign the server certificate with the CA in the next step.
Step 3: Create a Server Certificate Signing Request (CSR) #
Now, create a server certificate signing request (CSR), which will be used to generate a server certificate signed by the CA. Use the following command to create a CSR:
keytool -certreq -alias myserver -keystore server-keystore.jks -storepass changeit -file myserver.csr
This command generates a CSR for the CA certificate with the alias myca from the server-keystore.jks keystore file and stores it in the myserver.csr file.
Step 4: Create a Server Certificate signed by the CA #
Next, create a server certificate signed by the CA using the myca.csr file generated in the previous step.
keytool -gencert -alias myca -keystore server-keystore.jks -storepass changeit -dname "CN=myserver" -ext SAN=dns:localhost -infile myserver.csr -outfile myserver.crt
Part of the SSL handshake process between the client and server involves hostname verification. The hostname verification ensures that the server certificate is issued for the correct domain. The -ext SAN=dns:localhost option sets the subject alternative name (SAN) of the certificate to dns:localhost, which allows the certificate to be used for the localhost domain. That is, the server and the client are running on the same machine. This is fine for a development environment on your local machine.
However for a test environment, you may want to use a domain name that is not localhost. In this case, you would replace localhost with the host name of your server, or optionally, the IP address of the server. If a combination of host names and IP addresses are used, you can specify multiple SANs separated by commas, for example, SAN=dns:example.com,ip:192.168.0.1.
Step 5: Import the Server Certificate #
Now that you have a server certificate signed by the CA, import the server certificate into the keystore with the command.
keytool -import -alias myserver -keystore server-keystore.jks -storepass changeit -file myserver.crt
This command imports the server certificate with the alias myserver into the server.jks keystore file. The server certificate is now signed by the CA certificate. Note that the same alias myserver is used for the server certificate and the CA certificate. The keytool will replace the existing server certificate with the new one signed by the CA.
You should now have a keystore file server.jks containing the CA certificate and the server certificate signed by the CA. The server certificate is identified by the alias myserver, and the CA certificate is identified by the alias myca.
Use can view this using the tool Keystore Explorer (https://keystore-explorer.org/).
Check the Server Keystore #
You folder should contain the following files if you have followed the steps correctly.
| File Name | Description |
|---|---|
| myserver.csr | Server CSR |
| server.crt | Server certificate signed by the CA |
| server-keystore.jks | Keystore containing the CA certificate and the server certificate |
To confirm that the server keystore contains the CA certificate and the server certificate, you can view the contents of the keystore using the following command:
keytool -list -v -keystore server-keystore.jks -storepass changeit
Example output:
Keystore type: PKCS12
Keystore provider: SUN
Your keystore contains 2 entries
Alias name: myca
Creation date: 13 Jul 2024
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=myca
Issuer: CN=myca
Serial number: 3a4d43a640ced572
Valid from: Sat Jul 13 08:15:07 BST 2024 until: Tue Jul 11 08:15:07 BST 2034
Certificate fingerprints:
SHA1: 79:D3:6E:4F:43:CD:9D:79:27:09:4C:5A:5C:FE:66:97:E5:A8:1B:34
SHA256: C9:42:2C:EC:F5:C4:B6:DB:D7:FA:C1:FE:61:4D:5D:EF:9E:94:93:0F:88:9E:03:D4:31:B8:8B:04:83:77:87:69
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
Extensions:
#1: ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
CA:true
PathLen: no limit
]
#2: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 16 FA 44 43 98 89 F6 40 EB 55 71 BF B6 C0 CE 03 ..DC...@.Uq.....
0010: F8 38 1B DF .8..
]
]
*******************************************
*******************************************
Alias name: myserver
Creation date: 13 Jul 2024
Entry type: PrivateKeyEntry
Certificate chain length: 2
Certificate[1]:
Owner: CN=myserver
Issuer: CN=myca
Serial number: 9a9ea2d252b22230
Valid from: Sat Jul 13 08:15:33 BST 2024 until: Fri Oct 11 08:15:33 BST 2024
Certificate fingerprints:
SHA1: 7C:F2:E2:5F:8E:72:08:08:68:B7:D3:36:52:9B:45:D1:26:8D:33:BC
SHA256: 5E:1D:EA:13:FE:8F:A3:71:E5:4D:F3:BA:95:BC:50:EF:FC:2E:9F:AA:CA:83:76:F7:50:09:73:CB:3C:D9:66:1F
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
Extensions:
#1: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: 16 FA 44 43 98 89 F6 40 EB 55 71 BF B6 C0 CE 03 ..DC...@.Uq.....
0010: F8 38 1B DF .8..
]
]
#2: ObjectId: 2.5.29.17 Criticality=false
SubjectAlternativeName [
DNSName: localhost
]
#3: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: C3 D3 CF 04 4F 31 92 DC BB 02 37 9A 2F 2B 2E 66 ....O1....7./+.f
0010: 8F EB DC 81 ....
]
]
Certificate[2]:
Owner: CN=myca
Issuer: CN=myca
Serial number: 3a4d43a640ced572
Valid from: Sat Jul 13 08:15:07 BST 2024 until: Tue Jul 11 08:15:07 BST 2034
Certificate fingerprints:
SHA1: 79:D3:6E:4F:43:CD:9D:79:27:09:4C:5A:5C:FE:66:97:E5:A8:1B:34
SHA256: C9:42:2C:EC:F5:C4:B6:DB:D7:FA:C1:FE:61:4D:5D:EF:9E:94:93:0F:88:9E:03:D4:31:B8:8B:04:83:77:87:69
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
Extensions:
#1: ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
CA:true
PathLen: no limit
]
#2: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 16 FA 44 43 98 89 F6 40 EB 55 71 BF B6 C0 CE 03 ..DC...@.Uq.....
0010: F8 38 1B DF .8..
]
]
*******************************************
*******************************************
Note that the server certificate, alias myserver, is signed by the CA certificate, which is indicated by the Issuer field in the certificate details, being the same as the Owner field of the CA certificate.
Client Certificates Setup #
For the client truststore, client-truststore.jks, it will only require the CA certificate. All client applications will use this truststore to establish trust with any server that presents a certificate signed by the CA.
Step 1: Export the CA Certificate #
First, we need to export the CA certificate from the server keystore, which will be imported into the truststore of the client application. Use the following command to export the CA certificate:
keytool -exportcert -alias myca -keystore server-keystore.jks -storepass changeit -file myca.crt
This command exports the CA certificate with the alias myca from the server.jks keystore file to the myca.crt file. This will be used to import the CA certificate into the truststore of the client application.
Step 2: Import the CA Certificate #
Import the CA certificate into the truststore of the client application using the following command:
keytool -import -alias myca -keystore client-truststore.jks -storepass changeit -file myca.crt
This command imports the CA certificate with the alias myca from the myca.crt file into the client.jks truststore file with the password changeit.
All Commands Console Output #
Here is the console output for all the commands used to create the self-signed CA certificate, the server certificate signed by the CA, and the client truststore containing the CA certificate for reference.
Testing the server and client certificates #
To test the server and client certificates, you can set up a simple server and client application using Java. The server application will use the server certificate signed by the CA, and the client application will use the client truststore containing the CA certificate.
We will use spring boot to create a simple server and a POJO client to test the server certificate.
Server Application #
Create a server application using Spring Boot initializer, https://start.spring.io/, that uses the server certificate signed by the CA. The server application will listen on port 8443 and serve a simple REST API.
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HomeController {
@GetMapping("/hello")
public String hello() {
return "hello world";
}
}
Place the server keystore file server-keystore.jks in the resources folder of the server application. Add the following properties to the application.properties file to configure the server to use the server certificate:
server.port=8443
server.ssl.key-store=classpath:server-keystore.jks
server.ssl.key-store-password=changeit
server.ssl.key-store-type=JKS
server.ssl.key-alias=myserver
Client Application #
Create a simple client application that connects to the server using the server certificate signed by the CA. The client application will use the client truststore containing the CA certificate.
Place the client truststore file client-truststore.jks in the resources folder of the client application. Use the following code to create a simple client application that connects to the server on port 8443 and prints the response.
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
public class Sender {
public void sendRequest() throws Exception {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://localhost:8443/hello"))
.build();
HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
System.out.println(response.body());
}
public static void main(String[] args) throws Exception {
System.setProperty("javax.net.ssl.trustStore", "src/main/resources/client-truststore.jks");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
Sender sender = new Sender();
sender.sendRequest();
}
}
Summary #
In this article, we have covered the steps to create a self-signed Certificate Authority (CA) certificate, a server certificate signed by the CA, and a client truststore containing the CA certificate. We have also shown how to configure a server to use the server certificate and a client application to use the client truststore to establish trust with the server. This setup allows you to test SSL connections using self-signed certificates with multiple SSL enabaled servers, in a development environment.