How to create a truststore from a public RSA key - java

I need to read an encrypted ID token in an OpenID Connect flow with a private and public key. Don't need a cert.
First I ran:
openssl req -new -newkey rsa:2048 -keyout key.pem -pubkey -out pubreq.pem -subj "/CN=MyKey"
Got the public and private keys.
Then I ran:
openssl pkcs12 -export -inkey key.pem -out encryptedprivatekeytest.p12 -nocerts
So far so good. The file encryptedprivatekeytest.p12 contains the private key. Now I just need to create a jks truststore file from the public key in pubreq.pem.
Literally no matter how hard I try to find out how to, I just can't.
Any takers?

You have actually created a certificate signing request with the above command.
To generate just a public-private key pair, you can use:
openssl genrsa -out private.key 2048
openssl rsa -in private.key -pubout -out public.key
However, a trust store contains certificates, not public keys.

Related

Create self-signed certificate for usage with Netty Websocket Server

I am trying to create self-signed certificate for usage with a Netty (4.1.86) Websocket Server. It's running fine without SSL as well as with a certificate created with the SelfSignedCertificate class. But I am struggling when creating a self signed certificate with openssl.
This:
SslContext sslCtx = SslContextBuilder.forServer(new File(certFile), new File(keyFile), password).build();
Throws the following error:
ERROR Thread-5 com..application.NettyWSServer - Exception caught:
java.lang.IllegalArgumentException: File does not contain valid private key: /home/johnny/testbench/application/app.pkcs8.key
at io.netty.handler.ssl.SslContextBuilder.keyManager(SslContextBuilder.java:386)
at io.netty.handler.ssl.SslContextBuilder.forServer(SslContextBuilder.java:120)
at com..application.NettyWSServer.start(NettyWSServer.java:78)
at com..application.ApplicationLauncher$2.run(ApplicationLauncherncher.java:315)
at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: java.security.NoSuchAlgorithmException: 1.2.840.113549.1.5.13 SecretKeyFactory not available
at java.base/javax.crypto.SecretKeyFactory.<init>(SecretKeyFactory.java:122)
at java.base/javax.crypto.SecretKeyFactory.getInstance(SecretKeyFactory.java:168)
at io.netty.handler.ssl.SslContext.generateKeySpec(SslContext.java:1084)
at io.netty.handler.ssl.SslContext.getPrivateKeyFromByteBuffer(SslContext.java:1170)
at io.netty.handler.ssl.SslContext.toPrivateKey(SslContext.java:1133)
at io.netty.handler.ssl.SslContextBuilder.keyManager(SslContextBuilder.java:384)
... 4 more
Here's how I create certFile (app.pem) and keyFile (app.pkcs8.key):
openssl genrsa -out app.key 2048
openssl pkcs8 -topk8 -in app.key -out app.pkcs8.key
openssl req -x509 -new -nodes -key app.key -sha256 -days 1024 -out app.pem
As per my understanding Netty needs a pkcs8 format key:
https://netty.io/4.1/api/io/netty/handler/ssl/SslContextBuilder.html#forServer-java.io.File-java.io.File-
Working solution thanks to dave_thompson_085:
openssl genrsa -out app.key 2048
openssl pkcs8 -topk8 -v1 PBE-SHA1-3DES -nocrypt -in app.key -out app.pkcs8.key
openssl req -x509 -new -nodes -key app.key -sha256 -days 1024 -out app.pem
More exactly the default SSLContext path needs either PKCS8-unencrypted or PKCS8-encrypted using a password-based encryption (PBE) scheme that is defined uniquely by the OID at the beginning of the AlgorithmIdentifier, like the schemes in PKCS5v1 (now retronymed PBES1) or PKCS12, but not the newer/better family of schemes in PKCS5v2 named PBES2, because the OID for PBES2 -- 1.2.840.113549.1.5.13 which you see in your exception message -- does not identify a single scheme and thus is not sufficient for Java to instantiate a SecretKeyFactory (causing the exception). And openssl pkcs8 -topk8 by default uses a PBES2 scheme since 1.1.0 in 2016.
But the netty 4.1 SSLContext.toPrivateKey method(s) (which you can see and your stacktrace confirms SSLContextBuilder calls) will instead use BouncyCastlePemReader if available, and Bouncy can handle both older PBE schemes and PBES2, and also OpenSSL 'traditional' format files that are not PKCS8 at all (and if/when encrypted use a scheme based on OpenSSL's own PBKDF EVP_BytesToKey).
Thus I think (but can't test) you have 3 options:
supply BouncyCastle. I think you need bcprov (or bcprov-ext) and bcpkix; you may now need bcutil (due to a reorg in recent versions I haven't fully grokked).
encrypt your file with a non-PBES2 scheme. The PBES1 schemes are all now broken or easily breakable; the 'best' (least bad) PKCS12 scheme is officially named pbeWithSHA1And3-KeyTripleDES-CBC (!) but OpenSSL has a more convenient 'shortname':
openssl pkcs8 -topk8 -v1 PBE-SHA1-3DES [-in oldfile -out newfile]
Technically you could also accomplish this by leaving the default on a very old version of OpenSSL, but that's usually more difficult(*) and in my view not really different. (* not for me, because I have my own archive of old versions, but I'm weird)
don't encrypt your keyfile, (only!) if you can adequately secure it otherwise, for example by access controls on your system(s) (and any backups). Use:
openssl pkcs8 -topk8 -nocrypt [-in oldfile -out newfile]
or a bit more simply:
openssl pkey [-in oldfile -out newfile]

keytool error: java.io.IOException: keystore password was incorrect

Created a Certificate for Tomcat, trying to get it installed in new keystore, and getting error (Edit: ran it with -v option, now getting more info):
keytool error: java.io.IOException: keystore password was incorrect
java.io.IOException: keystore password was incorrect
at sun.security.pkcs12.PKCS12KeyStore.engineLoad(PKCS12KeyStore.java:2015)
at java.security.KeyStore.load(KeyStore.java:1445)
at sun.security.tools.keytool.Main.loadSourceKeyStore(Main.java:1894)
at sun.security.tools.keytool.Main.doImportKeyStore(Main.java:1926)
at sun.security.tools.keytool.Main.doCommands(Main.java:1021)
at sun.security.tools.keytool.Main.run(Main.java:340)
at sun.security.tools.keytool.Main.main(Main.java:333)
Caused by: java.security.UnrecoverableKeyException: failed to decrypt safe contents entry: java.io.IOException: getSecretKey failed: Password is not ASCII
Sadly, it's correct, the passphrase has two "®". So, given what I've done (the private key has the non-ASCII password), how much of a pain will it be to recover from this?:
1: Create a passphrase file: vi .kp
2: Make CSR:
A: Generate a 2048 bit private key:
openssl genpkey -algorithm RSA -outform PEM -out mike.privateKey.pass.pem -pkeyopt rsa_keygen_bits:2048 -pass file:.kp
B: Make the CSR:
openssl req -new -sha256 -key mike.privateKey.pass.pem -out mike.ike.com.cert.csr
Note: CSR has different "challenge password" than in the passphrase file, if that matters
3: Submit CSR to Comodo
4: Get certificate file mike_ike_com.cer & Comodo trust chain files: COMODORSAOrganizationValidationSecureServerCA.crt, COMODORSAAddTrustCA.crt, AddTrustExternalCARoot.crt
5: Convert the Certificates:
A: Convert to PEM:
openssl x509 -inform DER -in COMODORSAOrganizationValidationSecureServerCA.crt -out COMODORSAOrganizationValidationSecureServerCA.pem -outform PEM
openssl x509 -inform DER -in COMODORSAAddTrustCA.crt -out COMODORSAAddTrustCA.pem -outform PEM
openssl x509 -inform DER -in AddTrustExternalCARoot.crt -out AddTrustExternalCARoot.pem -outform PEM
B: Concat into a single file:
cat COMODORSAOrganizationValidationSecureServerCA.pem COMODORSAAddTrustCA.pem AddTrustExternalCARoot.pem > Comodo.root.crt
C: Use openssl to create a pkcs12 file:
openssl pkcs12 -export -in mike_ike_com.cer -inkey mike.privateKey.pass.pem -passin file:.kp -out mike_ike.p12 -name tomcat -caname root -chain -CAfile Comodo.root.crt
Note: when it asks "Enter Export Password" I give it the pw from .kp
6: Use keytool to create the keystore file:
$JAVA_HOME/bin/keytool -importkeystore -deststorepass:file .kp -destkeypass:file .kp -destkeystore .keystore -srckeystore mike_ike.p12 -srcstoretype PKCS12 -srcstorepass:file .kp -alias tomcat
The file ".keystore" does not exist. I am assuming that keytool will create it
I have got this sorted out. I was using my password that is 'password' to update cacerts keystore in JDK while default password for cacerts keystore is 'changeit'
Ok, so I have an answer.
1: I had a non-ASCII character in the password. openssl can handle that, keypass can't.
2: Having created the private key with the non-ASCII password, I'm stuck with it, so I renamed that file .kpkey, and created a new .kp file with a pure ASCII password
3: This required a change to 5:C:
openssl pkcs12 -export -in mike_ike_com.cer -inkey mike.privateKey.pass.pem -passin file:.kpkey -out mike_ike.p12 -name tomcat -caname root -chain -CAfile Comodo.root.crt
Note: when it asks "Enter Export Password" I give it the pw from .kp, NOT from .kpkey . The only change is -passin file:.kpkey
Everything else remains the same, and works
I would like to add another possible cause:
This error message can be misleading because it also occurs when the keystore is in an unsupported format.
In our situation the Application Server was not opening the Keystore.p12 that was supplied by the application, but the generated KeyStore.p12 during startup. Both were in different (yet similar) paths but had different passwords.

How to add SSH identity file keypair to JKS keystore

One of the tasks of a Java application I am building is to connect to a remote SFTP server. In order to do that I have the certificate of the remote machine and a local identity (id_rsa and id_rsa.pub in the .ssh folder). This is working fine.
I'd like to put the certificate and the identity in a password protected java keystore for easier and more secure configuration. I have this working for the certificate, but I am having problems storing the SSH identity in a JKS or PKCS12 keystore (either one would work).
To isolate the problem I have tried the following steps:
I use ssh-keygen -b 2048 to create the two identity files id_rsa_demo and id_rsa_demo.pub in te local directory. As I understand these are the private and public keys of the identity, so I try to combine those into an identity.p12 file:
openssl pkcs12 -export \
-inkey "id_rsa_demo" \
-in "id_rsa_demo.pub" \
-out "identity.p12" \
-password "pass:topsecret" \
-name "demoalias"
This gives me the error unable to load certificates. I searched around and it seems that openssl expects a certificate with a complete chain for the -in parameter. Since my generated identity does not have that, I tried the -nocerts option, like so:
openssl pkcs12 -export \
-inkey "id_rsa_demo" \
-in "id_rsa_demo.pub" \
-out "identity.p12" \
-password "pass:topsecret" \
-name "demoalias" \
-nocerts
I get no errors, but the -nocerts option lives up to its promise and does not add my public key to the pkcs12 file:
openssl pkcs12 -info -in identity.p12
Enter Import Password:
MAC Iteration 2048
MAC verified OK
PKCS7 Data
Shrouded Keybag: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 2048
Bag Attributes
friendlyName: demoalias
Key Attributes: <No Attributes>
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIAOXpzckBb28CAggA
MBQGCCqGSIb3DQMHBAjPq9ibr445xQSCBMi5IlOk5F28kQPB5D97afiUb5d3It46
...
ejwYfHTj6bm+dEOUk68zNrWwKqwuJx5AZv3U8sm1cicVmh9W0HpL5tSmMMpDS1ey
Uos=
-----END ENCRYPTED PRIVATE KEY-----
Is there a way to store an SSH identity into a PKCS12 or JKS keystore?
Supposing you have a private key that looks like this:
id_rsa
-----BEGIN RSA PRIVATE KEY-----
MIICWgIBAAKBgQCh3czej+KeEraesxts3xP6kx+cO/Fu8ROc/k4hSl7fO9jFZ6Lm
OsGlzsRsi8VDg9n/fh6iFng/Umgnfd4J0IiLQihSRYnvyOsqqXbIJ8mBtydqO4s+
CjZLLDRSEMx3dw6GhFOcQ7xYYOeUMNY8QFidPn2LjURfMxG9XWOrCww8rwIBJQKB
gGA+sSpjZCajV9P7yx4jxrCqgX99lnlREpSy4lj7ybUqgOQUG6t84dg1wOaYS8dH
erOXGSIbMr3d+L2JHD0v4ntcKqzJm6Nf1FE27V0hvpzZl3fNax4NI/cIXM78zBx4
lBblr5QMYnTSd5eADIcDy7TZHuScRPkPViQ2x9QPayQ9AkEA67lfOXFEJ8iTYHdu
ykvj0Xqcs/peDX5nYXCEJ2XECxgxfKYVbQPazO5ACgp1VsgFMCsd4rDSwahOAgkE
rGfgCwJBAK/KFkSqMCLga8m19uqOftTQ+GhFc0O1lchWQ0A99+b9Rcs0yAe10GCN
SbgrEmMuXEQS1emT6ZHM7KIh2P7kiG0CQQDSPYxH/TzJiWDZf0cjIRdMIT+ncJkS
9DKw2flTkh2NWsRaap1858MleowkoYs/j81Gov76nbUNlhwPpy2uhiivAkByBor8
G11+aA6QrWHkQMD4vuZReSgr62gTPt+DndE74o4i8c3bfNowyllU3asP5rhjgdbc
svheksMBYhA2ohNNAkAiKQdv08UAG77piJi09OFIEcetTiq/wy9Zeb6fmEuMFzsT
2aR6x0d43OXqAgcKFgFuzqdXgxqhP/n9/eIqXdVA
-----END RSA PRIVATE KEY-----
Do two things:
1) Create a certificate to wrap the key and expose the public key as a certificate, so that keytool understands it.
openssl x509 -signkey id_rsa -req -in example.req
2) Create a self-signed certificate from your new request.
openssl x509 -signkey id_rsa -req -in example.req -out example.cer
Then, combine the certificate and private key, and import into keytool.
cat example.cer id_rsa > example.full
keytool -import -keystore example.jks -file example.full
This will get the keys in there. Utilizing the private and public keys and interacting with the SSH/SFTP library of your choice is left as an exercise.

How to ensure server certificate is created with which private key through java

Customer has created key and certificate using openssl command below
openssl req -newkey rsa:1024 -sha1 -keyout OCkey.pem -out OCreq.pem -subj "/C=country/L=city/O=OCserver/OU=myLab/CN=OCserverName/" -config req.conf
openssl ca -in OCreq.pem -cert CAcert.pem -keyfile CAkey.pem -out OCcert.pem -days 3650 -config sign.conf -extfile sign.ext -md sha1 -notext
So they have given me the certificate(OCcert.pem) and private key (OCkey.pem).
How to ensure that OCcert.pem is created through OCkey.pem using Java? Note :I can't ask customer to change the ssl command.
Please help.
they have given me the certificate(OCcert.pem) and private key
(OCkey.pem)
Why? They should throw that private key away immediately, it is compromised. What was the purpose of this exercise?

How to import an existing X.509 certificate and private key in Java keystore to use in SSL?

I have this in an ActiveMQ config:
<sslContext>
<sslContext keyStore="file:/home/alex/work/amq/broker.ks"
keyStorePassword="password" trustStore="file:${activemq.base}/conf/broker.ts"
trustStorePassword="password"/>
</sslContext>
I have a pair of X.509 cert and a key file.
How do I import those two in order to use them in SSL and SSL+stomp connectors? All examples I could google always generate the key themselves, but I already have a key.
I have tried
keytool -import -keystore ./broker.ks -file mycert.crt
but this only imports the certificate and not the key file and results in
2009-05-25 13:16:24,270 [localhost:61612] ERROR TransportConnector - Could not accept connection : No available certificate or key corresponds to the SSL cipher suites which are enabled.
I have tried concatenating the cert and the key but got the same result.
How do I import the key?
I used the following two steps which I found in the comments/posts linked in the other answers:
Step one: Convert the x.509 cert and key to a pkcs12 file
openssl pkcs12 -export -in server.crt -inkey server.key \
-out server.p12 -name [some-alias] \
-CAfile ca.crt -caname root
Note: Make sure you put a password on the pkcs12 file - otherwise you'll get a null pointer exception when you try to import it. (In case anyone else had this headache). (Thanks jocull!)
Note 2: You might want to add the -chain option to preserve the full certificate chain. (Thanks Mafuba)
Step two: Convert the pkcs12 file to a Java keystore
keytool -importkeystore \
-deststorepass [changeit] -destkeypass [changeit] -destkeystore server.keystore \
-srckeystore server.p12 -srcstoretype PKCS12 -srcstorepass some-password \
-alias [some-alias]
Finished
OPTIONAL Step zero: Create self-signed certificate
openssl genrsa -out server.key 2048
openssl req -new -out server.csr -key server.key
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
FAQ: I get error IOException: keystore password was incorrect
If you are using OpenSSL 3.0 and a JDK newer than Java8u302 and get the following error:
keytool error: java.io.IOException: keystore password was incorrect
You might caught in a change pf default cypher within openssl. This Stack Overflow Answer provides an answer. Maybe thank Thomas with an upvote.
Keytool in Java 6 does have this capability: Importing private keys into a Java keystore using keytool
Here are the basic details from that post.
Convert the existing cert to a PKCS12 using OpenSSL. A password is required when asked or the 2nd step will complain.
openssl pkcs12 -export -in [my_certificate.crt] -inkey [my_key.key] -out [keystore.p12] -name [new_alias] -CAfile [my_ca_bundle.crt] -caname root
Convert the PKCS12 to a Java Keystore File.
keytool -importkeystore -deststorepass [new_keystore_pass] -destkeypass [new_key_pass] -destkeystore [keystore.jks] -srckeystore [keystore.p12] -srcstoretype PKCS12 -srcstorepass [pass_used_in_p12_keystore] -alias [alias_used_in_p12_keystore]
Believe or not, keytool does not provide such basic functionality like importing private key to keystore. You can try this workaround with merging PKSC12 file with private key to a keystore:
keytool -importkeystore \
-deststorepass storepassword \
-destkeypass keypassword \
-destkeystore my-keystore.jks \
-srckeystore cert-and-key.p12 \
-srcstoretype PKCS12 \
-srcstorepass p12password \
-alias 1
Or just use more user-friendly KeyMan from IBM for keystore handling instead of keytool.
Using Let's Encrypt certificates
Assuming you've created your certificates and private keys with Let's Encrypt in /etc/letsencrypt/live/you.com:
1. Create a PKCS #12 file
openssl pkcs12 -export -in fullchain.pem -inkey privkey.pem -out pkcs.p12 \
-name letsencrypt
This combines your SSL certificate fullchain.pem and your private key privkey.pem into a single file, pkcs.p12.
You'll be prompted for a password for pkcs.p12.
The export option specifies that a PKCS #12 file will be created rather than parsed (according to the manual).
2. Create the Java keystore
keytool -importkeystore -destkeystore keystore.jks -srckeystore pkcs.p12 \
-srcstoretype PKCS12 -alias letsencrypt
If keystore.jks doesn't exist, it will be created containing the pkcs.12 file created above. Otherwise, you'll import pkcs.12 into the existing keystore.
These instructions are derived from the post "Create a Java Keystore (.JKS) from Let's Encrypt Certificates" on this blog.
Here's more on the different kind of files in /etc/letsencrypt/live/you.com/.
First convert to p12:
openssl pkcs12 -export -in [filename-certificate] -inkey [filename-key] -name [host] -out [filename-new-PKCS-12.p12]
Create new JKS from p12:
keytool -importkeystore -deststorepass [password] -destkeystore [filename-new-keystore.jks] -srckeystore [filename-new-PKCS-12.p12] -srcstoretype PKCS12
And one more:
#!/bin/bash
# We have:
#
# 1) $KEY : Secret key in PEM format ("-----BEGIN RSA PRIVATE KEY-----")
# 2) $LEAFCERT : Certificate for secret key obtained from some
# certification outfit, also in PEM format ("-----BEGIN CERTIFICATE-----")
# 3) $CHAINCERT : Intermediate certificate linking $LEAFCERT to a trusted
# Self-Signed Root CA Certificate
#
# We want to create a fresh Java "keystore" $TARGET_KEYSTORE with the
# password $TARGET_STOREPW, to be used by Tomcat for HTTPS Connector.
#
# The keystore must contain: $KEY, $LEAFCERT, $CHAINCERT
# The Self-Signed Root CA Certificate is obtained by Tomcat from the
# JDK's truststore in /etc/pki/java/cacerts
# The non-APR HTTPS connector (APR uses OpenSSL-like configuration, much
# easier than this) in server.xml looks like this
# (See: https://tomcat.apache.org/tomcat-6.0-doc/ssl-howto.html):
#
# <Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol"
# SSLEnabled="true"
# maxThreads="150" scheme="https" secure="true"
# clientAuth="false" sslProtocol="TLS"
# keystoreFile="/etc/tomcat6/etl-web.keystore.jks"
# keystorePass="changeit" />
#
# Let's roll:
TARGET_KEYSTORE=/etc/tomcat6/foo-server.keystore.jks
TARGET_STOREPW=changeit
TLS=/etc/pki/tls
KEY=$TLS/private/httpd/foo-server.example.com.key
LEAFCERT=$TLS/certs/httpd/foo-server.example.com.pem
CHAINCERT=$TLS/certs/httpd/chain.cert.pem
# ----
# Create PKCS#12 file to import using keytool later
# ----
# From https://www.sslshopper.com/ssl-converter.html:
# The PKCS#12 or PFX format is a binary format for storing the server certificate,
# any intermediate certificates, and the private key in one encryptable file. PFX
# files usually have extensions such as .pfx and .p12. PFX files are typically used
# on Windows machines to import and export certificates and private keys.
TMPPW=$$ # Some random password
PKCS12FILE=`mktemp`
if [[ $? != 0 ]]; then
echo "Creation of temporary PKCS12 file failed -- exiting" >&2; exit 1
fi
TRANSITFILE=`mktemp`
if [[ $? != 0 ]]; then
echo "Creation of temporary transit file failed -- exiting" >&2; exit 1
fi
cat "$KEY" "$LEAFCERT" > "$TRANSITFILE"
openssl pkcs12 -export -passout "pass:$TMPPW" -in "$TRANSITFILE" -name etl-web > "$PKCS12FILE"
/bin/rm "$TRANSITFILE"
# Print out result for fun! Bug in doc (I think): "-pass " arg does not work, need "-passin"
openssl pkcs12 -passin "pass:$TMPPW" -passout "pass:$TMPPW" -in "$PKCS12FILE" -info
# ----
# Import contents of PKCS12FILE into a Java keystore. WTF, Sun, what were you thinking?
# ----
if [[ -f "$TARGET_KEYSTORE" ]]; then
/bin/rm "$TARGET_KEYSTORE"
fi
keytool -importkeystore \
-deststorepass "$TARGET_STOREPW" \
-destkeypass "$TARGET_STOREPW" \
-destkeystore "$TARGET_KEYSTORE" \
-srckeystore "$PKCS12FILE" \
-srcstoretype PKCS12 \
-srcstorepass "$TMPPW" \
-alias foo-the-server
/bin/rm "$PKCS12FILE"
# ----
# Import the chain certificate. This works empirically, it is not at all clear from the doc whether this is correct
# ----
echo "Importing chain"
TT=-trustcacerts
keytool -import $TT -storepass "$TARGET_STOREPW" -file "$CHAINCERT" -keystore "$TARGET_KEYSTORE" -alias chain
# ----
# Print contents
# ----
echo "Listing result"
keytool -list -storepass "$TARGET_STOREPW" -keystore "$TARGET_KEYSTORE"
In my case I had a pem file which contained two certificates and an encrypted private key to be used in mutual SSL authentication.
So my pem file looked like this:
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,C8BF220FC76AA5F9
...
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
Here is what I did:
Split the file into three separate files, so that each one contains just one entry,
starting with "---BEGIN.." and ending with "---END.." lines. Lets assume we now have three files: cert1.pem cert2.pem and pkey.pem
Convert pkey.pem into DER format using openssl and the following syntax:
openssl pkcs8 -topk8 -nocrypt -in pkey.pem -inform PEM -out pkey.der -outform DER
Note, that if the private key is encrypted you need to supply a password( obtain it from the supplier of the original pem file )
to convert to DER format,
openssl will ask you for the password like this: "enter a pass phraze for pkey.pem: "
If conversion is successful, you will get a new file called "pkey.der"
Create a new java key store and import the private key and the certificates:
String keypass = "password"; // this is a new password, you need to come up with to protect your java key store file
String defaultalias = "importkey";
KeyStore ks = KeyStore.getInstance("JKS", "SUN");
// this section does not make much sense to me,
// but I will leave it intact as this is how it was in the original example I found on internet:
ks.load( null, keypass.toCharArray());
ks.store( new FileOutputStream ( "mykeystore" ), keypass.toCharArray());
ks.load( new FileInputStream ( "mykeystore" ), keypass.toCharArray());
// end of section..
// read the key file from disk and create a PrivateKey
FileInputStream fis = new FileInputStream("pkey.der");
DataInputStream dis = new DataInputStream(fis);
byte[] bytes = new byte[dis.available()];
dis.readFully(bytes);
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
byte[] key = new byte[bais.available()];
KeyFactory kf = KeyFactory.getInstance("RSA");
bais.read(key, 0, bais.available());
bais.close();
PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec ( key );
PrivateKey ff = kf.generatePrivate (keysp);
// read the certificates from the files and load them into the key store:
Collection col_crt1 = CertificateFactory.getInstance("X509").generateCertificates(new FileInputStream("cert1.pem"));
Collection col_crt2 = CertificateFactory.getInstance("X509").generateCertificates(new FileInputStream("cert2.pem"));
Certificate crt1 = (Certificate) col_crt1.iterator().next();
Certificate crt2 = (Certificate) col_crt2.iterator().next();
Certificate[] chain = new Certificate[] { crt1, crt2 };
String alias1 = ((X509Certificate) crt1).getSubjectX500Principal().getName();
String alias2 = ((X509Certificate) crt2).getSubjectX500Principal().getName();
ks.setCertificateEntry(alias1, crt1);
ks.setCertificateEntry(alias2, crt2);
// store the private key
ks.setKeyEntry(defaultalias, ff, keypass.toCharArray(), chain );
// save the key store to a file
ks.store(new FileOutputStream ( "mykeystore" ),keypass.toCharArray());
(optional) Verify the content of your new key store:
keytool -list -keystore mykeystore -storepass password
Keystore type: JKS Keystore provider: SUN
Your keystore contains 3 entries
cn=...,ou=...,o=.., Sep 2, 2014, trustedCertEntry, Certificate
fingerprint (SHA1): 2C:B8: ...
importkey, Sep 2, 2014, PrivateKeyEntry, Certificate fingerprint
(SHA1): 9C:B0: ...
cn=...,o=...., Sep 2, 2014, trustedCertEntry, Certificate fingerprint
(SHA1): 83:63: ...
(optional) Test your certificates and private key from your new key store against your SSL server:
( You may want to enable debugging as an VM option: -Djavax.net.debug=all )
char[] passw = "password".toCharArray();
KeyStore ks = KeyStore.getInstance("JKS", "SUN");
ks.load(new FileInputStream ( "mykeystore" ), passw );
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, passw);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
TrustManager[] tm = tmf.getTrustManagers();
SSLContext sclx = SSLContext.getInstance("TLS");
sclx.init( kmf.getKeyManagers(), tm, null);
SSLSocketFactory factory = sclx.getSocketFactory();
SSLSocket socket = (SSLSocket) factory.createSocket( "192.168.1.111", 443 );
socket.startHandshake();
//if no exceptions are thrown in the startHandshake method, then everything is fine..
Finally register your certificates with HttpsURLConnection if plan to use it:
char[] passw = "password".toCharArray();
KeyStore ks = KeyStore.getInstance("JKS", "SUN");
ks.load(new FileInputStream ( "mykeystore" ), passw );
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, passw);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
TrustManager[] tm = tmf.getTrustManagers();
SSLContext sclx = SSLContext.getInstance("TLS");
sclx.init( kmf.getKeyManagers(), tm, null);
HostnameVerifier hv = new HostnameVerifier()
{
public boolean verify(String urlHostName, SSLSession session)
{
if (!urlHostName.equalsIgnoreCase(session.getPeerHost()))
{
System.out.println("Warning: URL host '" + urlHostName + "' is different to SSLSession host '" + session.getPeerHost() + "'.");
}
return true;
}
};
HttpsURLConnection.setDefaultSSLSocketFactory( sclx.getSocketFactory() );
HttpsURLConnection.setDefaultHostnameVerifier(hv);
Yes, it's indeed a sad fact that keytool has no functionality to import a private key.
For the record, at the end I went with the solution described here
Based on the answers above, here is how to create a brand new keystore for your java based web server, out of an independently created Comodo cert and private key using keytool (requires JDK 1.6+)
Issue this command and at the password prompt enter somepass - 'server.crt' is your server's cert and 'server.key' is the private key you used for issuing the CSR:
openssl pkcs12 -export -in server.crt -inkey server.key -out server.p12 -name www.yourdomain.com -CAfile AddTrustExternalCARoot.crt -caname "AddTrust External CA Root"
Then use keytool to convert the p12 keystore into a jks keystore:
keytool -importkeystore -deststorepass somepass -destkeypass somepass -destkeystore keystore.jks -srckeystore server.p12 -srcstoretype PKCS12 -srcstorepass somepass
Then import the other two root/intermediate certs you received from Comodo:
Import COMODORSAAddTrustCA.crt:
keytool -import -trustcacerts -alias cert1 -file COMODORSAAddTrustCA.crt -keystore keystore.jks
Import COMODORSADomainValidationSecureServerCA.crt:
keytool -import -trustcacerts -alias cert2 -file COMODORSADomainValidationSecureServerCA.crt -keystore keystore.jks
What I was trying to achieve was using already provided private key and certificate to sign message that was going someplace that needed to make sure that the message was coming from me (private keys sign while public keys encrypt).
So if you already have a .key file and a .crt file?
Try this:
Step1: Convert the key and cert to .p12 file
openssl pkcs12 -export -in certificate.crt -inkey privateKey.key -name alias -out yourconvertedfile.p12
Step 2: Import the key and create a .jsk file with a single command
keytool -importkeystore -deststorepass changeit -destkeystore keystore.jks -srckeystore umeme.p12 -srcstoretype PKCS12
Step 3: In your java:
char[] keyPassword = "changeit".toCharArray();
KeyStore keyStore = KeyStore.getInstance("JKS");
InputStream keyStoreData = new FileInputStream("keystore.jks");
keyStore.load(keyStoreData, keyPassword);
KeyStore.ProtectionParameter entryPassword = new KeyStore.PasswordProtection(keyPassword);
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry("alias", entryPassword);
System.out.println(privateKeyEntry.toString());
If you need to sign some string using this key do the following:
Step 1: Convert the text you want to encrypt
byte[] data = "test".getBytes("UTF8");
Step 2: Get base64 encoded private key
keyStore.load(keyStoreData, keyPassword);
//get cert, pubkey and private key from the store by alias
Certificate cert = keyStore.getCertificate("localhost");
PublicKey publicKey = cert.getPublicKey();
KeyPair keyPair = new KeyPair(publicKey, (PrivateKey) key);
//sign with this alg
Signature sig = Signature.getInstance("SHA1WithRSA");
sig.initSign(keyPair.getPrivate());
sig.update(data);
byte[] signatureBytes = sig.sign();
System.out.println("Signature:" + Base64.getEncoder().encodeToString(signatureBytes));
sig.initVerify(keyPair.getPublic());
sig.update(data);
System.out.println(sig.verify(signatureBytes));
References:
How to import an existing x509 certificate and private key in Java keystore to use in SSL?
http://tutorials.jenkov.com/java-cryptography/keystore.html
http://www.java2s.com/Code/Java/Security/RetrievingaKeyPairfromaKeyStore.htm
How to sign string with private key
Final program
public static void main(String[] args) throws Exception {
byte[] data = "test".getBytes("UTF8");
// load keystore
char[] keyPassword = "changeit".toCharArray();
KeyStore keyStore = KeyStore.getInstance("JKS");
//System.getProperty("user.dir") + "" < for a file in particular path
InputStream keyStoreData = new FileInputStream("keystore.jks");
keyStore.load(keyStoreData, keyPassword);
Key key = keyStore.getKey("localhost", keyPassword);
Certificate cert = keyStore.getCertificate("localhost");
PublicKey publicKey = cert.getPublicKey();
KeyPair keyPair = new KeyPair(publicKey, (PrivateKey) key);
Signature sig = Signature.getInstance("SHA1WithRSA");
sig.initSign(keyPair.getPrivate());
sig.update(data);
byte[] signatureBytes = sig.sign();
System.out.println("Signature:" + Base64.getEncoder().encodeToString(signatureBytes));
sig.initVerify(keyPair.getPublic());
sig.update(data);
System.out.println(sig.verify(signatureBytes));
}
You can use these steps to import the key to an existing keystore. The instructions are combined from answers in this thread and other sites. These instructions worked for me (the java keystore):
Run
openssl pkcs12 -export -in yourserver.crt -inkey yourkey.key -out server.p12 -name somename -certfile yourca.crt -caname root
(If required put the -chain option. Putting that failed for me).
This will ask for the password - you must give the correct password else you will get an error
(heading error or padding error etc).
It will ask you to enter a new password - you must enter a password here - enter anything but remember it. (Let us assume you enter Aragorn).
This will create the server.p12 file in the pkcs format.
Now to import it into the *.jks file run:
keytool -importkeystore -srckeystore server.p12 -srcstoretype PKCS12
-destkeystore yourexistingjavakeystore.jks -deststoretype JKS -deststorepass existingjavastorepassword -destkeypass existingjavastorepassword
(Very important - do not leave out the deststorepass and the destkeypass parameters.)
It will ask you for the src key store password. Enter Aragorn and hit enter.
The certificate and key is now imported into your existing java keystore.
Previous answers point out correctly that you can only do this with the standard JDK tools by converting the JKS file into PKCS #12 format first. If you're interested, I put together a compact utility to import OpenSSL-derived keys into a JKS-formatted keystore without having to convert the keystore to PKCS #12 first: http://commandlinefanatic.com/cgi-bin/showarticle.cgi?article=art049
You would use the linked utility like this:
$ openssl req -x509 -newkey rsa:2048 -keyout localhost.key -out localhost.csr -subj "/CN=localhost"
(sign the CSR, get back localhost.cer)
$ openssl rsa -in localhost.key -out localhost.rsa
Enter pass phrase for localhost.key:
writing RSA key
$ java -classpath . KeyImport -keyFile localhost.rsa -alias localhost -certificateFile localhost.cer -keystore localhost.jks -keystorePassword changeit -keystoreType JKS -keyPassword changeit
If you have a PEM file (e.g. server.pem) containing:
the trusted certificate
the private key
then you can import the certificate and key into a JKS keystore like this:
1) Copy the private key from the PEM file into an ascii file (e.g. server.key)
2) Copy the cert from the PEM file into an ascii file (e.g. server.crt)
3) Export the cert and key into a PKCS12 file:
$ openssl pkcs12 -export -in server.crt -inkey server.key \
-out server.p12 -name [some-alias] -CAfile server.pem -caname root
the PEM file can be used as the argument to the -CAfile option.
you are prompted for an 'export' password.
if doing this in git bash then add winpty to the start of the command so the export password can be entered.
4) Convert the PKCS12 file to a JKS keystore:
$ keytool -importkeystore -deststorepass changeit -destkeypass changeit \
-destkeystore keystore.jks -srckeystore server.p12 -srcstoretype PKCS12 \
-srcstorepass changeit
the srcstorepass password should match the export password from step 3)
Just make a PKCS12 keystore, Java can use it directly now. In fact, if you list a Java-style keystore, keytool itself alerts you to the fact that PKCS12 is now the preferred format.
openssl pkcs12 -export -in server.crt -inkey server.key \
-out server.p12 -name [some-alias] \
-CAfile ca.crt -caname root -chain
You should have received all three files (server.crt, server.key, ca.crt) from your certificate provider. I am not sure what "-caname root" actually means, but it seems to have to be specified that way.
In the Java code, make sure to specify the right keystore type.
KeyStore.getInstance("PKCS12")
I got my comodo.com-issued SSL certificate working fine in NanoHTTPD this way.
in a case of Elliptic Curve and answer the question import an existing x509 certificate and private key in Java keystore, you may want to have a look also to this thread How to read EC Private key in java which is in .pem file format
If you have received a combined cert and key in a single .pem file, like the MongoDB Atlas' authentication, then,
Open the pem file with a text editor and split them into two files, for example cert.pem and key.pem (where you can make a split is very clear in the file) and then use the openssl command to create a single p12 format file like this:
openssl pkcs12 -export -out server.p12 -name test\
-in cert.pem -inkey key.pem
I am using Java 8 and as it turns out at least in Java 8 or later the resulting p12 (server.p12) is now the keystore file so you can use it directly without a need to use the keytool if you do not need to add any more certs to it.

Categories

Resources