How to get certificates into Java keystore?

My domain is: sophotech.ddns.net

I ran this command:
keytool -keystore keystore.test -import -alias sophotech.ddns.net -file
/etc/letsencrypt/live/sophotech.ddns.net/fullchain.pem -trustcacerts

Started server on port 80 (HTTP), port 9443 (HTTPS, letsencrypt certificate), port 443 (HTTPS, self-signed)
Ports 80 and 443 work as expected. Port 9443 is configured the same way as port 443 except for the port number and keystore location.

Client (Firefox) can’t connect: “Secure connection failed: the
connection to sophotech.ddns.net was interrupted while the page was loading. The page you are trying to view cannot be shown because the authenticity of the received data could not be verified.”

openssl s_client -connect sophotech.ddns.net:9443 </dev/null

It produced this output:
3073853116:error:140790E5:SSL routines:SSL23_WRITE:ssl handshake failure:s23_lib.c:177:
CONNECTED(00000003)


no peer certificate available


No client certificate CA names sent


SSL handshake has read 0 bytes and written 295 bytes


New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE

Using keytool to view the keystore shows this:

Alias name: sophotech.ddns.net
Creation date: Jan 18, 2017
Entry type: trustedCertEntry

Owner: CN=sophotech.ddns.net
Issuer: CN=Let’s Encrypt Authority X3, O=Let’s Encrypt, C=US
Serial number: 3291e62ea498a5d63a62c39e7cb3269b270
Valid from: Wed Jan 18 16:25:00 IST 2017 until: Tue Apr 18 17:25:00 IDT 2017
Certificate fingerprints:
MD5: 35:80:FE:F5:60:22:45:01:B1:EB:82:C7:47:14:4A:56
SHA1: 93:64:4E:60:91:82:0F:F5:67:49:5D:BE:2C:66:36:1A:43:57:90:91
SHA256: 29:6B:A5:89:F2:2E:76:57:C6:76:80:F9:3E:4C:7E:8E:16:7B:C1:04:E8:77:FE:EE:9F:7C:55:B1:4B:B0:F4:F2
Signature algorithm name: SHA256withRSA
Version: 3

Extensions:

#1: ObjectId: 1.3.6.1.5.5.7.1.1 Criticality=false
AuthorityInfoAccess [
[
accessMethod: ocsp
accessLocation: URIName: http://ocsp.int-x3.letsencrypt.org/
,
accessMethod: caIssuers
accessLocation: URIName: http://cert.int-x3.letsencrypt.org/
]
]

#2: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: A8 4A 6A 63 04 7D DD BA E6 D1 39 B7 A6 45 65 EF .Jjc…9…Ee.
0010: F3 A8 EC A1 …
]
]

#3: ObjectId: 2.5.29.19 Criticality=true
BasicConstraints:[
CA:false
PathLen: undefined
]

#4: ObjectId: 2.5.29.32 Criticality=false
CertificatePolicies [
[CertificatePolicyId: [2.23.140.1.2.1]
[] ]
[CertificatePolicyId: [1.3.6.1.4.1.44947.1.1.1]
[PolicyQualifierInfo: [
qualifierID: 1.3.6.1.5.5.7.2.1
qualifier: 0000: 16 1A 68 74 74 70 3A 2F 2F 63 70 73 2E 6C 65 74 …http://cps.let
0010: 73 65 6E 63 72 79 70 74 2E 6F 72 67 sencrypt.org

], PolicyQualifierInfo: [
qualifierID: 1.3.6.1.5.5.7.2.2
qualifier: 0000: 30 81 9E 0C 81 9B 54 68 69 73 20 43 65 72 74 69 0…This Certi
0010: 66 69 63 61 74 65 20 6D 61 79 20 6F 6E 6C 79 20 ficate may only
0020: 62 65 20 72 65 6C 69 65 64 20 75 70 6F 6E 20 62 be relied upon b
0030: 79 20 52 65 6C 79 69 6E 67 20 50 61 72 74 69 65 y Relying Partie
0040: 73 20 61 6E 64 20 6F 6E 6C 79 20 69 6E 20 61 63 s and only in ac
0050: 63 6F 72 64 61 6E 63 65 20 77 69 74 68 20 74 68 cordance with th
0060: 65 20 43 65 72 74 69 66 69 63 61 74 65 20 50 6F e Certificate Po
0070: 6C 69 63 79 20 66 6F 75 6E 64 20 61 74 20 68 74 licy found at ht
0080: 74 70 73 3A 2F 2F 6C 65 74 73 65 6E 63 72 79 70 tps://letsencryp
0090: 74 2E 6F 72 67 2F 72 65 70 6F 73 69 74 6F 72 79 t.org/repository
00A0: 2F /

]] ]
]

#5: ObjectId: 2.5.29.37 Criticality=false
ExtendedKeyUsages [
serverAuth
clientAuth
]

#6: ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
DigitalSignature
Key_Encipherment
]

#7: ObjectId: 2.5.29.17 Criticality=false
SubjectAlternativeName [
DNSName: sophotech.ddns.net
]

#8: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 75 95 6B 67 A2 B2 D8 30 DD 08 8E A5 6E 4C 3A 1B u.kg…0…nL:.
0010: 5A F3 7A 48 Z.zH
]
]

My operating system is (include version): Ubuntu 15.04

My web server is (include version): Jetty 8.1.4

My hosting provider, if applicable, is:

I can login to a root shell on my machine (yes or no, or I don’t know): yes

I’m using a control panel to manage my site (no, or provide the name and version of the control panel): no

I don't use Jetty, so may not be the best person to provide an accurate answer :wink:

As I was doing a quick check though, it would appear Jetty 8 is End of Life, so should be updated to v9 (although I'm sure that's not causing this issue )

Your command

keytool -keystore keystore.test -import -alias sophotech.ddns.net -file
/etc/letsencrypt/live/sophotech.ddns.net/fullchain.pem -trustcacerts

Doesn't look complete to me - in that I'd expect you to need to add the private key in some way to the keystore as well (or did you also do that separately )

Don’t worry, I know Jetty 8 is EOL. I’ve been avoiding upgrading for ages, on the basis of “if it ain’t broke, don’t fix it” and configuring Jetty 9 is a whole new ball game compared to Jetty 8.

I’ve never used a real certificate before (always used self-signed to avoid paying money until I heard about letsencrypt) but
a) I thought that fullchain.pem contained everything I needed
b) if you’re right, I have no idea how to add the private key to the keystore…

The only thing I could see in the Jetty Documentation was the section on “Loading Keys and Certificates via PKCS12”

fullchain.pem is “All certificates, including server certificate (aka leaf certificate or end-entity certificate). The server certificate is the first one in this file, followed by any intermediates.” but doesn’t include the private key.

I found some advice on stackoverflow about using openssl to create a PKCS12 file and then import it as a keystore using keytool:

openssl pkcs12 -export -in /etc/letsencrypt/live/sophotech.ddns.net/fullchain.pem -inkey /etc/letsencrypt/live/sophotech.ddns.net/privkey.pem -out cert.p12 -name sophotech.ddns.net

Type x when asked for the export password. Then delete keystore.test and do this:

keytool -importkeystore -destkeystore keystore.test -srckeystore cert.p12 -srcstoretype PKCS12 -srcstorepass x

but it generates a keystore which breaks the server on startup (java.security.UnrecoverableKeyException: Cannot recover key).

If someone who knows their way around keytool (and/or openssl) can help with this, I suspect it might make a good addition to the FAQ...

It may be worth having a look at other java installations such as Importing LetsEncrypt into Java and Glassfish How to use the certificate for Tomcat Let's encrypt and JIRA/Tomcat - fully automated script

The only difference I can see between what is given in your link and what I'm doing is I forgot to supply a -alias option to keytool. So I tried that, and I still get a server crash on startup. The keystore now looks like this (but I have no idea whether it looks sensible or not!):

Alias name: sophotech.ddns.net
Creation date: Jan 18, 2017
Entry type: PrivateKeyEntry
Certificate chain length: 2
Certificate[1]:
Owner: CN=sophotech.ddns.net
Issuer: CN=Let's Encrypt Authority X3, O=Let's Encrypt, C=US
Serial number: 3291e62ea498a5d63a62c39e7cb3269b270
Valid from: Wed Jan 18 16:25:00 IST 2017 until: Tue Apr 18 17:25:00 IDT 2017
Certificate fingerprints:
MD5: 35:80:FE:F5:60:22:45:01:B1:EB:82:C7:47:14:4A:56
SHA1: 93:64:4E:60:91:82:0F:F5:67:49:5D:BE:2C:66:36:1A:43:57:90:91
SHA256: 29:6B:A5:89:F2:2E:76:57:C6:76:80:F9:3E:4C:7E:8E:16:7B:C1:04:E8:77:FE:EE:9F:7C:55:B1:4B:B0:F4:F2
Signature algorithm name: SHA256withRSA
Version: 3

Extensions:

#1: ObjectId: 1.3.6.1.5.5.7.1.1 Criticality=false
AuthorityInfoAccess [
[
accessMethod: ocsp
accessLocation: URIName: http://ocsp.int-x3.letsencrypt.org/
,
accessMethod: caIssuers
accessLocation: URIName: http://cert.int-x3.letsencrypt.org/
]
]

#2: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: A8 4A 6A 63 04 7D DD BA E6 D1 39 B7 A6 45 65 EF .Jjc......9..Ee.
0010: F3 A8 EC A1 ....
]
]

#3: ObjectId: 2.5.29.19 Criticality=true
BasicConstraints:[
CA:false
PathLen: undefined
]

#4: ObjectId: 2.5.29.32 Criticality=false
CertificatePolicies [
[CertificatePolicyId: [2.23.140.1.2.1]
]
[CertificatePolicyId: [1.3.6.1.4.1.44947.1.1.1]
[PolicyQualifierInfo: [
qualifierID: 1.3.6.1.5.5.7.2.1
qualifier: 0000: 16 1A 68 74 74 70 3A 2F 2F 63 70 73 2E 6C 65 74 ..http://cps.let
0010: 73 65 6E 63 72 79 70 74 2E 6F 72 67 sencrypt.org

], PolicyQualifierInfo: [
qualifierID: 1.3.6.1.5.5.7.2.2
qualifier: 0000: 30 81 9E 0C 81 9B 54 68 69 73 20 43 65 72 74 69 0.....This Certi
0010: 66 69 63 61 74 65 20 6D 61 79 20 6F 6E 6C 79 20 ficate may only
0020: 62 65 20 72 65 6C 69 65 64 20 75 70 6F 6E 20 62 be relied upon b
0030: 79 20 52 65 6C 79 69 6E 67 20 50 61 72 74 69 65 y Relying Partie
0040: 73 20 61 6E 64 20 6F 6E 6C 79 20 69 6E 20 61 63 s and only in ac
0050: 63 6F 72 64 61 6E 63 65 20 77 69 74 68 20 74 68 cordance with th
0060: 65 20 43 65 72 74 69 66 69 63 61 74 65 20 50 6F e Certificate Po
0070: 6C 69 63 79 20 66 6F 75 6E 64 20 61 74 20 68 74 licy found at ht
0080: 74 70 73 3A 2F 2F 6C 65 74 73 65 6E 63 72 79 70 tps://letsencryp
0090: 74 2E 6F 72 67 2F 72 65 70 6F 73 69 74 6F 72 79 t.org/repository
00A0: 2F /

]] ]
]

#5: ObjectId: 2.5.29.37 Criticality=false
ExtendedKeyUsages [
serverAuth
clientAuth
]

#6: ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
DigitalSignature
Key_Encipherment
]

#7: ObjectId: 2.5.29.17 Criticality=false
SubjectAlternativeName [
DNSName: sophotech.ddns.net
]

#8: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 75 95 6B 67 A2 B2 D8 30 DD 08 8E A5 6E 4C 3A 1B u.kg...0....nL:.
0010: 5A F3 7A 48 Z.zH
]
]

Certificate[2]:
Owner: CN=Let's Encrypt Authority X3, O=Let's Encrypt, C=US
Issuer: CN=DST Root CA X3, O=Digital Signature Trust Co.
Serial number: a0141420000015385736a0b85eca708
Valid from: Thu Mar 17 18:40:46 IST 2016 until: Wed Mar 17 18:40:46 IST 2021
Certificate fingerprints:
MD5: B1:54:09:27:4F:54:AD:8F:02:3D:3B:85:A5:EC:EC:5D
SHA1: E6:A3:B4:5B:06:2D:50:9B:33:82:28:2D:19:6E:FE:97:D5:95:6C:CB
SHA256: 25:84:7D:66:8E:B4:F0:4F:DD:40:B1:2B:6B:07:40:C5:67:DA:7D:02:43:08:EB:6C:2C:96:FE:41:D9:DE:21:8D
Signature algorithm name: SHA256withRSA
Version: 3

Extensions:

#1: ObjectId: 1.3.6.1.5.5.7.1.1 Criticality=false
AuthorityInfoAccess [
[
accessMethod: ocsp
accessLocation: URIName: http://isrg.trustid.ocsp.identrust.com
,
accessMethod: caIssuers
accessLocation: URIName: http://apps.identrust.com/roots/dstrootcax3.p7c
]
]

#2: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: C4 A7 B1 A4 7B 2C 71 FA DB E1 4B 90 75 FF C4 15 .....,q...K.u...
0010: 60 85 89 10 `...
]
]

#3: ObjectId: 2.5.29.19 Criticality=true
BasicConstraints:[
CA:true
PathLen:0
]

#4: ObjectId: 2.5.29.31 Criticality=false
CRLDistributionPoints [
[DistributionPoint:
[URIName: http://crl.identrust.com/DSTROOTCAX3CRL.crl]
]]

#5: ObjectId: 2.5.29.32 Criticality=false
CertificatePolicies [
[CertificatePolicyId: [2.23.140.1.2.1]
]
[CertificatePolicyId: [1.3.6.1.4.1.44947.1.1.1]
[PolicyQualifierInfo: [
qualifierID: 1.3.6.1.5.5.7.2.1
qualifier: 0000: 16 22 68 74 74 70 3A 2F 2F 63 70 73 2E 72 6F 6F ."http://cps.roo
0010: 74 2D 78 31 2E 6C 65 74 73 65 6E 63 72 79 70 74 t-x1.letsencrypt
0020: 2E 6F 72 67 .org

]] ]
]

#6: ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
DigitalSignature
Key_CertSign
Crl_Sign
]

#7: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: A8 4A 6A 63 04 7D DD BA E6 D1 39 B7 A6 45 65 EF .Jjc......9..Ee.
0010: F3 A8 EC A1 ....
]
]

I can only suggest changing your title to something like “how to get certificates in Java keystore” as that might attract those who know more about keystore

I have no idea how to change the title… so I’m going to post it as a new query with your suggested title… :slight_smile:

I’m obviously doing something wrong because listing the keystore contents with keytool shows “Your keystore contains 1 entry. Entry type: PrivateKeyEntry” whereas the link you gave me shows the ressult should have 2 entries (trustedCertEntry and PrivateKeyEntry).

Thanks for all your help so far!

There is a pencil mark next to the title - click that. (or I can edit it for you )

Thanks, I’ve done it!

1 Like

You’re somewhat close. The issue you’re running into is that the private key is protected with a passphrase, even though the overall keystore is not. Unless the configuration offers a way to provide a keystore and/or private key passphrase on startup, you’ll need to avoid using any kind of passphrase protection on the file. Note that such a configuration isn’t really secure, so make sure to protect the keystore file from outside access.

That said, you’ll need to do a two-step process to get things set up if you have a PEM-style set of files, which it appears you do.

The first step is to convert the private key and all certificates to a PKCS12 format. It appears you have done that. Remember when doing this step to leave the export passphrase blank (just press enter/return) when prompted.

If your software doesn’t support PKCS12 directly (some applications do), you then need to take the second step of converting to a JKS format file. If you only are going to have one certificate, you don’t need to worry about specifying an alias. You can also drop the “srcstorepass” switch as you didn’t export protected by a passphrase.

OK. There is a password on the keystore, but I did that by hand rather than using a command line switch. And the Jetty config includes the keystore password. The alias is there because that’s the way I was told to do it Lo-These-Many-Years-Ago, but the Jetty config doesn’t seem to refer to it anywhere so I’ll take it out to see if that helps.

The “PEM-style set of files” is the standard set of 4 files that Let’s Encrypt delivered.

Everything I read about the openssl conversion to PKCS12 said I MUST specify an export password – and without it keytool gives me a NullPointerException. And Jetty uses JKS, so I need to use keytool – I don’t think there’s any choice about that, I’ll check in case it’s changed since lo-these-many-years-ago.

I don’t have time to try this now, it’ll have to wait till tomorrow, sorry!

Many thanks for the advice, and I’ll no doubt bug you some more tomorrow… :slight_smile:

I’ve now tried everything I can think of, and am completely stumped.

  1. Importing fullchain.pem on its own means that the server starts successfully, but I can’t connect because there is no private key
  2. Using openssl to create a PKCS12 file and using keytool to convert it to JKS means that the server fails on startup with an UnrecoverableKeyException: Cannot recover key
  3. Importing both fullchain.pem and then the PKCS12 file also fails on startup
  4. using openssl with -in cert.pem -inkey privkey.pem -CAfile chain.pem also fails on startup.

The export password for creating the PKCS12 file is required – if it isn’t present, keytool prompts you for it and then crashes with a NullPointerException if you just hit return.

Since all my incantations so far have got me nowhere, I’m off to the shops now to buy a chicken to sacrifice…

OK. When I have my other hat on (at work), I use Jetty with SSL certificates extensively. However we definitely use passwords to protect the private key, I will now go to work, and look at whether the password is optional, or if not, how you tell Jetty the password you’ve used. FWIW Changing the title helped make sure I looked at this thread at least.

I give Jetty the keystore password in my jetty.xml:

<Call name="addConnector">
<Arg>
<New class="org.eclipse.jetty.server.ssl.SslSelectChannelConnector">
<Set name="Port">9443</Set>
<Set name="MaxIdleTime">30000</Set>
<Set name="Acceptors">2</Set>
<Set name="AcceptQueueSize">100</Set>
<Set name="Keystore"><Property name="jetty.home" default="." />/keystore.test</Set>
<Set name="Password">foo</Set>
<Set name="KeyPassword">foo</Set>
</New>
</Arg>
</Call>

And as I said, keytool requires a password on the PKCS12 file as well.

Glad to hear that changing the title helped -- thanks to serverco for suggesting it!

Meanwhile, I got the chicken, and I'm standing by... :slight_smile:

I sacrificed the chicken, and my prayers were answered!

I'm not exactly sure which bit of the incantation did the trick, but for the record I did this:

rm keystore.test
openssl pkcs12 -export -in cert.pem -inkey privkey.pem -CAfile chain.pem -out cert.pkcs12 -name sophotech.ddns.net -caname root

I specified the same password as I use for the keystore when prompted.

Next step:

keytool -importkeystore -destkeystore keystore.test -srckeystore cert.pkcs12 -srcstoretype PKCS12

I then supplied the keystore password (twice) and the same when prompted for the source keystore password. Suddenly the server started, and my browser connected and said "Hello world".

Since from my point of view all I'm doing is reciting magic spells which I don't fully understand, the only changes I can see that I made here are:

  1. specifying "-caname root" to openssl
  2. using the same password for both the PKCS12 keystore and the JKS keystore.

I specified -name to openssl but forgot to include -alias in keytool, so it seems that doesn't matter. I'm going to play around with some variations on this theme and see what tips the balance. I also want to see if I need to specify the chain separately for -CAfile, or I can use -in fullchain.pem (or maybe -CAfile fullchain.pem?).

But before I do anything else, I'm going to replace "rm keystore.test" with "mv keystore.test keystore.ok"... :slight_smile:

I've now tied it down. The various bits of fluff in the openssl command were not needed, so the first step simplifies to this:

openssl pkcs12 -export -in fullchain.pem -inkey privkey.pem -out cert.pkcs12

I now specify a password (x), twice, when prompted.

Next step:

keytool -importkeystore -destkeystore keystore.test -srckeystore cert.pkcs12 -srcstoretype PKCS12

I now specify a keystore password (y), twice, when prompted, and the source keystore password (x) when prompted.

In the Jetty config file (jetty.xml), I specify the passwords x and y like so:

<Set name="Keystore"><Property name="jetty.home" default="." />/keystore.test</Set>
<Set name="Password">y</Set>
<Set name="KeyPassword">x</Set>

And that's it -- my problem was that the "password" and "keyPassword" were both set to the password for the JKS keystore, which worked OK with a self-signed certificate that didn't include a private key. I hadn't realised that the "password" was the JKS keystore password, and the "keyPassword" was the imported PKCS12 keystore password.

Problem solved! Thanks for all the advice, everyone.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.