简体   繁体   English

如何使用Java代码将证书和私钥导出为单个文件?

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

My requirement is to combine a certificate and private key and then, using PEMwriter, I need to export the file. 我的要求是将证书和私钥结合在一起,然后使用PEMwriter导出文件。

I used bouncycastle to generate private key and csr. 我使用bouncycastle生成私钥和csr。 Then, I submitted the CSR to CA and got the certificate. 然后,我将CSR提交给CA并获得了证书。 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. 以下代码用于接收证书并将其导出到.cer文件。

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). 并且请在编码部分(PemWriter)中指导我。 Thanks 谢谢

THe following code is for receiving the certificate and exporting it to .cer file. 以下代码用于接收证书并将其导出到.cer文件。

        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. 旁白:您不需要该循环即可将Collection转换为数组,只需一个调用.toArray (new T[optionally_correct_size]) 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() . 另一方面,由于只使用第一个元素,因此根本不需要数组,只需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. 如果需要完整的链条,它也各不相同,你是否可以只使用单个/独立PEM证书的序列或需要别的东西。

I don't know what Base64 encoder you are using; 我不知道您使用的是哪种Base64编码器; that signature does not match the BouncyCastle one. 该签名与BouncyCastle签名不匹配。 I hope it generates line breaks correctly. 我希望它能正确产生换行符。 PEM format is NOT merely BEGIN-line, base64, END-line; PEM格式不仅是BEGIN行,base64,END行; it is BEGIN-line, base64 with line breaks , END-line. 它是BEGIN行, 带有换行符的END64 ,END行。 Some software will work, sometimes , even without the line breaks, but some software, sometimes, will fail. 某些软件即使在没有断线的情况下可以正常工作,但是某些软件有时会失败。 See RFC 7468 sec 2 near the end . 参见RFC 7468第2秒即将结束

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. 特别是,PEM格式证书未加密,因此任何人都可以查看该证书并将其用于公钥操作(加密或验证),或者以其他方式使用该证书。

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. 但是要执行私钥操作(解密或签名),您需要私钥(通常如上所述链接到证书或链),并且私钥可以 (至少)以两种不同的方式以常规PEM格式进行密码加密,因此要求密码“打开”它。 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). 早在1990年代,OpenSSL便事实上定义了一种PEM加密方案,该方案可用于其几种“传统”或“传统”私钥格式,每种格式各不相同,并在BEGIN行中以PEM类型表示。 -----BEGIN RSA PRIVATE KEY----------BEGIN DSA PRIVATE KEY----- -----BEGIN RSA PRIVATE KEY----- -----BEGIN DSA PRIVATE KEY----------BEGIN EC PRIVATE KEY----- 私钥 -----BEGIN EC PRIVATE KEY-----等。 稍后定义PKCS8 所有算法的通用私钥格式,包括Java crypto(JCA)在内的许多其他软件使用该格式,包括由-----BEGIN ENCRYPTED PRIVATE KEY-----指定的加密变体(请注意没有算法名称)。 The PKCS8 version (only) has been made official by RFC7468 sec 11 . RFC7468 sec 11已正式宣布PKCS8版本(仅)。 (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). (对于OpenSSH,PuTTY和PGP,私钥还有其他与PEM类似的格式,但是它们都不使用X.509型证书。尽管GnuPG现在既实现了PGP和S / MIME,也使用了X .509 / PKIX( 用于S / MIMEbcpkix上存在许多现有的Q,如果您对此感兴趣,则还有一些关于security.SX或crypto.SX的Q。) bcpkix实现了OpenSSL-legacy形式 PKCS8形式。 。 (Java alone without BouncyCastle implements PKCS8 unencrypted .) (没有BouncyCastle的仅Java便实现了未加密的 PKCS8。)

Similarly, 'legacy' Java keystores (JKS and JCEKS) password-encrypt the privatekey(s), but not the certificate(s). 同样,“旧式” Java密钥库(JKS和JCEKS)对私钥(而不是证书)进行密码加密。 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); PKCS12标准实际上非常灵活(而且很复杂),但通常会实施 (包括您的BC-keystore示例),对私钥使用强密码加密,对证书使用很弱的易破解密码加密; 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. 正式地, .cer应该是DER证书,并且RFC7468 sec 5.3 建议.crt用作PEM证书。 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. 如果将密钥写到一个单独的文件中,使用.key.pem似乎很普遍,但是我知道没有标准(当然也没有IANA注册)来指定此密钥。 (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 . (RFC5958为未加密的DER PKCS8实际上注册了.p8,而RFC8351没有指定任何扩展名而注册了DER PKCS8加密了。)如果您将密钥和证书写入同一文件(PEM格式支持但并非所有程序都支持),我除了.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 : 因此,最后解决您的问题:-)假设您的提供程序列表(和类路径)中有BC提供程序,类路径中有bcpkix,而kv有一个PrivateKey对象:

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

As mentioned above for normal use you don't need to protect certificates with passwords. 如上所述,在正常使用情况下,您无需使用密码保护证书。 But if you want to store some certificates in a safe place, you can do it in a password-protected keystore file (only java.security code, no PemWriter necessary). 但是,如果要将某些证书存储在安全的地方,则可以在受密码保护的密钥库文件中进行操作(只需java.security代码,而无需PemWriter)。

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

            Certificate cert = getX509Cert(); // load certificate
            ks.setCertificateEntry("myCertAlias", cert);

            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream(PATH + "newKeyStore.p12");
                char[] password = PASSWORD_.toCharArray();
                ks.store(fos, password);
            } finally {
                if (fos != null) {
                    fos.close();
                }
            }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM