简体   繁体   English

如何在 JAVA 中将 RSA 私钥从 DER 转换为 PEM?

[英]How to convert RSA private key from DER to PEM in JAVA?

I have a RSA private key.der file (with a bunch of binary in it).我有一个 RSA private key.der 文件(里面有一堆二进制文件)。 How do I convert the file to PEM format in Java?如何将文件转换为 Java 中的 PEM 格式?

If I were to use openssl in command line, this is what I would do:如果我在命令行中使用 openssl,我会这样做:

openssl rsa -inform der -in privateKey.der -outform pem -out privateKey.pem

The privateKey.pem result looks like this: privateKey.pem 结果如下所示:

-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAyljm4IGV2JmL8amyTQz9nCSdtdJV+OGiiyefJasKuNEIVAyV
BzSNEUrJYL1LrAMM0cbenWh3M0I+RoQYfwo3J88fi/aLW5dbdU8bi001UZIeJnEB
...
...
lykp1jNHSXD8PoBNVD+pbY3zGCqH1OgPWd0/+IbMBp3Qo+HMp5Ku9w==
-----END RSA PRIVATE KEY-----

I heard that people say PEM is just Base64-encoded representation of DER plus certain header and footer.我听说人们说 PEM 只是 DER 的 Base64 编码表示加上某些 header 和页脚。 However, if I just do cat privateKey.der | base64但是,如果我只是做cat privateKey.der | base64 cat privateKey.der | base64

I get different results from what openssl gave me: MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDKWObggZXYmYvxqbJNDP2... ... GndCj4cynkq73我从 openssl 给我的结果中得到了不同的结果: MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDKWObggZXYmYvxqbJNDP2... ... GndCj4cynkq73

Despite the head & footer, the PEM string is also not the same.尽管有页眉和页脚,但 PEM 字符串也不相同。 Does anyone know why?有谁知道为什么?

Are there any library to convert DER to PEM in Java like what openssl does? Java 中是否有任何库可以将 DER 转换为 PEM,就像 openssl 一样? Been searching for a while but unfortunately, I didn't find anything that works.一直在寻找,但不幸的是,我没有找到任何有效的方法。 Any help would be greatly appreciated!任何帮助将不胜感激!

There are many PEM formats, and OpenSSL supports about a dozen PEM and DER formats for private keys. PEM 格式有很多种,OpenSSL 支持十几种 PEMDER 格式的私钥。 The format written by openssl rsa [-outform pem] is the algorithm-specific PKCS1 appendix A format, with or without rfc1421-based encryption, and is labelled RSA PRIVATE KEY . openssl rsa [-outform pem]编写的格式是算法特定的 PKCS1 附录 A 格式,有或没有基于 rfc1421 的加密,并标记为RSA PRIVATE KEY The format written by openssl pkey or openssl pkcs8 -topk8 is the algorithm-generic PKCS8 format, which defines its own encryption, and is accordingly labelled PRIVATE KEY or ENCRYPTED PRIVATE KEY -- note no RSA in either case;openssl pkeyopenssl pkcs8 -topk8编写的格式是算法通用的 PKCS8 格式,它定义了自己的加密,并相应地标记为PRIVATE KEYENCRYPTED PRIVATE KEY - 注意在任何一种情况下都没有 RSA; see rfc7468 .rfc7468 Java crypto eg PrivateKey.getEncoded() uses PKCS8 unencrypted, so if you got this DER from Java and converted it to base64 that's why you 'get different results'. Java 加密,例如PrivateKey.getEncoded()使用未加密的 PKCS8,所以如果你从 Java 得到这个 DER 并将其转换为 Z95A1446A7120E4AF5C0C8878ABB7E6,你为什么会得到不同的结果。 Also note PEM is base64 of DER with line breaks every 64 chars plus header and footer;另请注意,PEM 是 DER 的 base64,每 64 个字符换行一次,加上 header 和页脚; without the line breaks it won't always work.没有换行符,它不会总是有效。 For both of these see PKCS#1 and PKCS#8 format for RSA private key .对于这两者,请参见PKCS#1 和 PKCS#8 format for RSA private key

Your answer using BouncyCastle is a good solution if you have or can get Bouncy;如果您拥有或可以获得 Bouncy,那么您使用 BouncyCastle 的回答是一个很好的解决方案; bcpkix correctly handles most OpenSSL formats. bcpkix 正确处理大多数 OpenSSL 格式。 If you want to do it without Bouncy, it is nearly the same as How to encrypt RSA private key with PBE in PKCS#5 format in Java with IAIK JCE?如果您想在没有 Bouncy 的情况下执行此操作,它几乎与How to encrypt RSA private key with PBE in PKCS#5 format in Java with IAIK JCE 相同? (mine) except without encryption, so leave out the middle block and change the PEM header slightly: (我的)除了没有加密,所以省略中间块并稍微更改 PEM header:

    // given [RSA]PrivateKey privkey, get the PKCS1 part from the PKCS8 encoding
    byte[] pk8 = privkey.getEncoded();
    // this is wrong for RSA<=512 but those are totally insecure anyway
    if( pk8[0]!=0x30 || pk8[1]!=(byte)0x82 ) throw new Exception();
    if( 4 + (pk8[2]<<8 | (pk8[3]&0xFF)) != pk8.length ) throw new Exception();
    if( pk8[4]!=2 || pk8[5]!=1 || pk8[6]!= 0 ) throw new Exception();
    if( pk8[7] != 0x30 || pk8[8]==0 || pk8[8]>127 ) throw new Exception();
    // could also check contents of the AlgId but that's more work
    int i = 4 + 3 + 2 + pk8[8];
    if( i + 4 > pk8.length || pk8[i]!=4 || pk8[i+1]!=(byte)0x82 ) throw new Exception();
    byte[] old = Arrays.copyOfRange (pk8, i+4, pk8.length);
    
    // write to PEM format (substitute other file if desired)
    System.out.println ("-----BEGIN RSA PRIVATE KEY-----");
    String b64 = Base64.getEncoder().encodeToString(old);
    for( int off = 0; off < b64.length(); off += 64 )
        System.out.println (b64.substring(off, off+64<b64.length()?off+64:b64.length()));
    System.out.println ("-----END RSA PRIVATE KEY-----");

I'd think carefully before calling this 'better' though.不过,在称其为“更好”之前,我会仔细考虑。 This is also similar to Convert a X509 Public key to RSA public key for publickey (also mine) and the reverse of Reading a PKCS#1 or SPKI public key in Java without libraries .这也类似于Convert a X509 Public key to RSA public key for publickey (also mine) 和Read a PKCS#1 or SPKI public key in Java without libraries 的相反

After lots of digging... I found a way, but it looks redundant.经过大量挖掘......我找到了一种方法,但它看起来是多余的。 Please let me know if you have better idea.如果您有更好的主意,请告诉我。

    // My private key DER file.
    byte[] privateKeyDerBytes = Files.readAllBytes(Paths.get("tst/resource/FromSecretsManager.der"));
    
    // Convert the DER file to a Java PrivateKey
    PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(privateKeyDerBytes);
    KeyFactory factory = KeyFactory.getInstance("RSA");
    PrivateKey pk = factory.generatePrivate(spec);

    // Use PemWriter to write the key to PEM format
    StringWriter sw = new StringWriter();
    try (PemWriter pw = new PemWriter(sw)) {
        PemObjectGenerator gen = new JcaMiscPEMGenerator(pk);
        pw.writeObject(gen);
    }
    String privateKeyPem = sw.toString();

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

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