I am struggling to configure Tomcat for SSL and would be grateful for any help. For my purposes, I need to create a root certificate and a server certificate which is signed by the root certificate.
To help my understanding, I am breaking this into two steps: create certificates using openssl, and importing certificates into Tomcat's keystore. Here's my sequence of steps with the openssl and keystore commands.
-- Create certficates --
1. Create CA key in private directory:
openssl genrsa -out CA.key 4096
2. Create CA PEM request using CN <Root Name> and CA key:
openssl req -key ../private/CA.key -new -out CA.req
3. Create CA PEM using Create CA PEM request:
openssl x509 -req -days 365 -in CA.req -signkey ../private/CA.key -sha256 -out CA.pem
4. Create server key (encrypted with password):
openssl genrsa -des3 -out server.key 4096
5. Create server PEM request using CN <FQDN of Server>:
openssl req -new -key server.key -out server.req
6. Create server PEM using Create server PEM request:
openssl x509 -req -days 3650 -in server.req -signkey ../private/CA.key -out server.pem
7. Export CA certificate as PKCS12 file (possibly not necessary):
openssl pkcs12 -export -inkey ../private/CA.key -in CA.pem -out CA.p12
8. Export server certificate as PKCS12 file:
openssl pkcs12 -export -inkey ../private/CA.key -in server.pem -out server.p12
-- Install certificates --
9. Add CA PEM to server keystore:
keytool -import -trustcacerts -alias root -file CA.pem -keystore keystore.jks
10. Add server PKCS12 to client keystore:
keytool -import -alias server -file server.p12 -keystore KeyStore.jks
At the last command, I get this error message:
keytool error: java.lang.Exception: Input not an X.509 certificate
I don't entirely understand the relationship between the PEM and PKCS12 formats and why I need to import the root certificate in the PEM format and the server certificate in PKCS12 format - maybe that's where I am going wrong?
Thanks for any help.
Mostly dupe HAProxy SSL termination + client certificate validation + curl / java client .
First, your #6 is wrong; you don't create a server cert signed by the CA, but rather a self-signed cert with the server name and CA key. That's why your #8 needed to pair the 'server' cert with the CA key, which would normally make no sense. Use -CA
and -CAkey
instead in #6, and change 8 to use the server key and server cert and best also the CA cert (only) as CA cert using -CA
-- that is:
openssl pkcs12 -export -in server.pem -inkey server.key -CA CA.pem -out server.p12
Second, as explained in the dupe I have now updated, you may not need to convert to JKS at all; Java can often handle PKCS12. But if you do, you need to use -importkeystore
as shown there, which is a separate and different operation, not -import[cert]
.
Finally, part of your confusion is probably caused by misuse of 'PEM'. PEM is not a single thing; it is a format that can be used for many things, and consists of a BEGIN line containing 5 hyphens, the word BEGIN, one or a few words identifying the type of data, and 5 more hyphens; then an optional (and fairly rare) RFC822-style header; then one or more lines of base64 which encodes whatever data was specified by the BEGIN line; then a similar line with END instead of BEGIN.
PEM can be used for certificates (in a few formats), keys (in several formats), CSRs, CRLs, OCSP requests and responses, messages in several formats, and perhaps other things. The files you have named '.pem' are more accurately certificates in PEM format; the '.key' and '.req' files you use are also in PEM format, but are not certificates, being instead keys and certificate requests (CSRs). The key difference (pardon the pun) between a certificate file (in PEM or any other format) and a PKCS12 file is that PKCS12 normally contains both a privatekey and a certificate or (usually) a 'chain' of several certificates. (Although Java9 uses -- arguably abuses -- PKCS12 so it can contain only certs; this is unconventional and AFAICT uninteroperable.)
All these PEM files have non-PEM equivalents, which OpenSSL calls 'DER' (although 'binary' might be more accurate). You can have a certificate in PEM or DER and it's still a certificate; you can have a key in PEM or DER; etc. PKCS12 is an exception; it is supported only in binary/non-PEM format. Although not all OpenSSL commands support both PEM and DER in all cases. Your error message was not due to trying to '-importcert' something in non-PEM format as such, but trying to '-importcert' something that isn't specifically a certificate (in any format). That's why it says 'Input not an X.509 certificate' -- that means the input does not consist of a certificate.
Normally (with rare exceptions not relevant here) an SSL/TLS server requires a privatekey and certificate or chain; a Java SSL/TLS server like older Tomcat requires a privatekey and cert/chain in a format supported by Java for a keystore, which as above can be JKS or PKCS12. (Tomcat 8.5 and 9 change this to sort of 'merge' the configurations for Java JSSE and APR OpenSSL.) If you are importing a certificate reply issued by a CA for a keypair that was generated in Java and specifically in a keystore file, then you normally need to import the multiple certificates making up that chain, either as a single chain (typically P7B) or separately starting from the root; you may have been looking at instructions for that case, which is not this case where you generated the keypair (and also certs) in OpenSSL and not in a Java keystore.
The SSL/TLS client OTOH requires only the trust-anchor cert (no key), usually the root CA cert. For certs issued by a 'real' (public) CA, most clients including Java already have the public root CAs built in, but since you are creating your own CA, you need to distribute the CA cert (only) to your client(s) (possibly excepting any that ignore incorrect certs, like openssl s_client
and curl -k
).
A final warning: certs generated with OpenSSL this way (and thus your server using them) won't work in Chrome 58 or up (since mid 2017) since it now requires SubjectAltNames aka SAN; see numerous existing Qs about this.