How to export Certificate and private key as a single file using java code?

saravana_kumar :

My requirement is to combine a certificate and private key and then, using PEMwriter, I need to export the file.

I used bouncycastle to generate private key and csr. Then, I submitted the CSR to CA and got the certificate. THe following code is for generating keypair

KeyPair idPair = KeyPairGenerator.getInstance("RSA").genKeyPair();
PublicKey publicKey = idPair.getPublic();
PrivateKey privkey = idPair.getPrivate();

THe following code is for receiving the certificate and exporting it to .cer file.

CertStore store = response.getCertStore();
            Collection<? extends Certificate> certs = store
                    .getCertificates(null);
            Certificate[] chain = new Certificate[certs.size()];

            int i = 0;
            for (Certificate certificate : certs) {
                chain[i++] = certificate;
            }

            FileOutputStream os = new FileOutputStream("cert.cer");
            os.write("-----BEGIN CERTIFICATE-----\n".getBytes("US-ASCII"));
            os.write(Base64.encodeBase64(chain[0].getEncoded(), true));
            os.write("-----END CERTIFICATE-----\n".getBytes("US-ASCII"));
            os.close();

Now, my goal is when I open the exported file, I must be prompted to input password to install/view the certificate. I'm really confused about this part. Previously I added password to keystore like the following

KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC");
        keyStore.load(null, null);

        keyStore.setKeyEntry("mykey", (Key) keyPair.getPrivate(), "Password1!".toCharArray(), certz);

        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        keyStore.store(bout, "Password1!".toCharArray()); // this is the password to open the .p12

        byte[] keystore = bout.toByteArray();
        bout.close();

But, now, I need to protect the certificate with password. Is this even possible? If so, what will be the file extension? And please guide me in the coding part (PemWriter). Thanks

dave_thompson_085 :

THe following code is for receiving the certificate and exporting it to .cer file.

        CertStore store = response.getCertStore();
        Collection<? extends Certificate> certs = store
                .getCertificates(null);
        Certificate[] chain = new Certificate[certs.size()];
        int i = 0;
        for (Certificate certificate : certs) {
            chain[i++] = certificate;
        }

Asides: you don't need that loop to convert a Collection to an array, just one call .toArray (new T[optionally_correct_size]) will do it. On the other hand, since you're only using the first element you don't need an array at all, just Collection<T>.iterator().next().

More substantively and on the third hand, many -- but definitely not all -- operations done with the privatekey also need the full cert chain, not just the leaf/end-entity cert, to work correctly. For example some signers can compute a signature without the chain, but in some cases or circumstances that signature cannot be verified and thus will be rejected by whatever receives it. Since you give no clue how, by what and for what your exported file(s) will be used, it is impossible to give any advice on this. And if the full chain is needed, it also varies whether you can just use a sequence of individual/separate PEM certs or need something else.

I don't know what Base64 encoder you are using; that signature does not match the BouncyCastle one. I hope it generates line breaks correctly. PEM format is NOT merely BEGIN-line, base64, END-line; it is BEGIN-line, base64 with line breaks, END-line. Some software will work, sometimes, even without the line breaks, but some software, sometimes, will fail. See RFC 7468 sec 2 near the end.

Now, my goal is when I open the exported file, I must be prompted to input password to install/view the certificate. ... But, now, I need to protect the certificate with password. Is this even possible?

You need to distinguish between the privatekey and the certificate(s). Although related in an important way, these are different things, and the privatekey is designed to be private/secret while the certificate is designed to be public. In particular, the PEM format certificate is not encrypted, so anyone can view it and use it for publickey operations (encrypt or verify) and otherwise do what they like with it.

But to perform a privatekey operation (decrypt or sign) you need the privatekey -- usually linked to a cert or chain as above -- and the privatekey can be password-encrypted in (at least) two different ways in customary PEM formats, thus requiring the password to 'open' it. OpenSSL back in the 1990s de-facto defined a PEM encryption scheme which can be used for its several 'legacy' or 'traditional' privatekey formats, which are different for each algorithm, and indicated in the PEM type in the BEGIN-line, thus -----BEGIN RSA PRIVATE KEY-----, -----BEGIN DSA PRIVATE KEY-----, -----BEGIN EC PRIVATE KEY-----, etc. Later PKCS8 defined a generic privatekey format for all algorithms, used by much other software including Java crypto (JCA), including an encrypted variant designated by -----BEGIN ENCRYPTED PRIVATE KEY----- (note no algorithm name). The PKCS8 version (only) has been made official by RFC7468 sec 11. (There are also other and quite different PEM-like formats for privatekeys used by OpenSSH, PuTTY, and PGP, but none of them use X.509-type certificates. Although GnuPG now implements both PGP and S/MIME, and does use X.509/PKIX for S/MIME. There are numerous existing Qs on SO, and some on security.SX or crypto.SX, if you are interested in these.) bcpkix implements the OpenSSL-legacy forms and the PKCS8 form(s). (Java alone without BouncyCastle implements PKCS8 unencrypted.)

Similarly, 'legacy' Java keystores (JKS and JCEKS) password-encrypt the privatekey(s), but not the certificate(s). The PKCS12 standard is actually very flexible (and complicated), but as usually implemented (including your BC-keystore example) uses strong password encryption for the privatekey(s), and a very weak easily breakable password encryption for the certificate(s); I've never been quite sure why, because this is less convenient without providing any security benefit.

If so, what will be the file extension?

That's mostly up to you, or whoever (if anyone) these files are for. No standard requires a file extension to match the file contents or vice versa, although people usually do so because it is much more convenient. Officially .cer is supposed to be a DER certificate, and RFC7468 sec 5.3 recommends using .crt for a PEM certificate. If you write the key to a separate file, it seems to be fairly common to use .key or .pem, but I know of no standard (and certainly no IANA registration) specifying this. (RFC5958 registers .p8 for what is actually DER PKCS8 unencrypted and RFC8351 registers DER PKCS8 encrypted without specifying any extension.) If you write the key and cert(s) to the same file, which PEM format supports but not all programs do, I haven't even seen common practice other than .pem.

So to finally get to your question :-) Given you have the BC provider in your provider list (and the classpath), bcpkix in the classpath, and a PrivateKey object in kv:

import org.bouncycastle.openssl.PKCS8Generator;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.openssl.jcajce.JcaPKCS8Generator;
import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8EncryptorBuilder;
import org.bouncycastle.openssl.jcajce.JcePEMEncryptorBuilder;
import org.bouncycastle.util.io.pem.PemWriter;

        // for the OpenSSL legacy form
        JcaPEMWriter x = new JcaPEMWriter (new OutputStreamWriter(System.out)); // or whatever 
        x.writeObject(kv, new JcePEMEncryptorBuilder("DES-EDE3-CBC").build("password".toCharArray()) );
        // can substitute AES-{128,192,256}-CBC if desired, for more see source
        x.close(); // or flush to keep underlying writer/stream

        // for the PKCS8 form
        PemWriter y = new PemWriter (new OutputStreamWriter(System.out)); // or whatever 
        y.writeObject(new JcaPKCS8Generator (kv, new JceOpenSSLPKCS8EncryptorBuilder(
                PKCS8Generator.DES3_CBC).setPasssword("password".toCharArray()).build() ) );
        // or AES_{128,192,256}_CBC, others will use PBES1 which is deprecated
        y.close(); // or flush to keep underlying writer/stream

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=328018&siteId=1