Search code examples
javaopensslpublic-key-encryption

openssl der files importing into java keystore


I created a keypair with openssl and want them to import into the java-keystore:

1) openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out my_privatekey.pem
2) openssl rsa -pubout -outform DER -in my_privatekey.pem -out mypublic_key.der
3) openssl pkcs8 -topk8 -nocrypt -outform DER -in my_privatekey.pem -out my_privatekey.der

First, I create a private-key (in .pem-Format), then I create a public key and at the end I convert the private key into a format that can be used in java (pkcs8).

Now, I want to use those keys in a secure way in my java application, so I did some research and the solution seems to be by using the java-keystore.

However, if I am correct, you are not able to store the public key directly into the keystore, because you must create a certificate first:

convert your certificate in a DER format :

openssl x509 -outform der -in certificate.pem -out certificate.der

import into the keystore

keytool -import -alias your-alias -keystore cacerts -file certificate.der

This brings me now to my question. Is it possible to import the private and public key into the keystore without a certificate? I don't need a certificate, as I only want to store my keys securely, so they are password-protected.

If this is not possible, than you could create and sign your own certificate. However, a certificate can be expired, so after some time I have to always renew it or am I wrong?

I share this public key with a third-party (they need this key to verify the data which I signed with my private key) and I also get a public key from them, to encrypt some data. So I need to store 2 public keys at the end (my key and the public key which I receive).

How do I do that? Do I need to create 2 certificates as a hack, in order to be able to store them into the java-keystore?


Solution

  • Aside: for just the keypair you don't need genpkey and then pkcs8 -topk8 -nocrypt -outform der; genpkey ... -outform der (without -$cipher) will create PKCS8-clear DER, the format JCE supports directly. For that matter even PKCS8-clear PEM can be handled easily enough by stripping the header and trailer lines and decoding the base64. This was not the case for the other commands needed in OpenSSL before 1.0.0 in 2010 (genrsa; gendsa; ecparam -genkey) which is part of the reason you will find lots of wrong advice about it all over the World Wide Copying.

    The java.security.KeyStore API supports storing PrivateKey values only in conjunction with a certificate chain (which can be a single certificate). It also supports storing a lone certificate (to verify signatures from or encrypt data to someone else) but not a lone publickey. So yes you must create (or otherwise obtain) certificates.

    The usual convention, if you don't want the security features of real certificate(s), is to create a 'self-signed' certificate -- one which follows the standard X.509v3 format, and contains the publickey, but is signed by its own key (specifically, the privatekey matching the publickey in the cert) rather than any CA's key. OpenSSL can do this several ways, such as the commonly recommended

    openssl genpkey ... -out priv.pem 
    openssl req -new -key priv.pem -x509 ... -out cert.pem
    

    or you can combine key generation and cert creation with the simpler

    openssl req -newkey rsa:2048 -keyout priv.pem -nodes -x509 ... -out cert.pem
    # this doesn't support all the options of genpkey 
    # but it does support the simple case you are using
    

    It is also possible to split the 'req' and 'sign' steps:

    openssl genpkey ... -out priv.pem
    openssl req -new -key priv.pem ... -out csr.pem
    openssl x509 -req -in csr.pem -signkey priv.pem ... -out cert.pem
    # or
    openssl req -newkey rsa:2048 -keyout priv.pem ... -out csr.pem
    openssl x509 -req -in csr.pem -signkey priv.pem ... -out cert.pem
    

    You can then write (simple) code to read in the privatekey and certificate and create a KeyStore entry -- and persist if you wish (which you presumably do). However keytool cannot do this directly. You can instead use openssl pkcs12 -export to combine the two OpenSSL files into a single PKCS12 file, which is password-encrypted and is usable directly as a keystore in currently supported Java. For really old Java you may need to convert the PKCS12 file to a JKS file using keytool -importkeystore -srcstoretype PKCS12 [-deststoretype JKS] ..., another piece of advice you will find widely copied.

    Alternatively, unless you need the OpenSSL files for something else, keytool can do the whole job at once; just do keytool -genkeypair -keyalg rsa -keysize 2048 -keystore $file plus other options like -validity and -dname as desired, and it will generate the keypair and a self-signed cert for it and store the privatekey with the self-signed cert in a password-protected keystore file. (Through j8 defaults to JKS but you can specify otherwise; j9+ defaults to PKCS12.)

    However, a certificate can be expired, so after some time I have to always renew it or am I wrong?

    After some time, yes, but that time can be several thousand years. I suspect that both you and the third-party will be defunct by then, plus probably Java will no longer exist so the requirements imposed by Java APIs will no longer matter. Note that older versions of OpenSSL on 32-bit systems -- each rare today and the combination rarer -- sometimes were affected by the "Y2038" issue, and that is now only 18 years in the future -- many systems in use today will probably be obsolete by then, but not all. Java uses its own timestamp format and never had this problem.

    CA-issued certificates usually have much shorter lifetimes, rarely more than 1-2 years and sometimes much less (as a notable example, 90 days for LetsEncrypt), but that's not inherent in the certificate spec and code.

    Do I need to create 2 certificates as a hack, in order to be able to store them into the java-keystore?

    Yes. To be exact, you certainly need to create a (dummy) cert for you own key. For the other party's key, it's easier if they create the (dummy) cert. You can't create a self-signed cert for them because you don't have their privatekey -- or at least you shouldn't. You can create a slightly more complicated structure where you create a dummy CA and use it to sign a cert for them, which you then import and use. I will expand if needed, but this is as I said more complicated, whereas many common tools are set up to allow them to do it very easily.