【问题标题】:Load an Encrypted PCKS8 PEM Private Key In Java在 Java 中加载加密的 PCKS8 PEM 私钥
【发布时间】:2021-05-22 23:15:39
【问题描述】:

我希望加载/使用在 Java 应用程序中提供给我的加密私钥。请找到密钥(加密私钥、解密私钥和公钥如下所示)。

加密私钥密码:“aWCTJPET9fL7UBTp97hX99gdofeWKUf5tuxSuJeST2sEkyvkyinrfrj6EiSUTErF”

密钥是通过在 NodeJS 中使用 Crypto 生成的:

generateKeyPairSync( "rsa", {
        modulusLength: 4096,
        publicKeyEncoding: {
          type: "spki",
          format: "pem"
        },
        privateKeyEncoding: {
          type: "pkcs8",
          format: "pem",
          cipher: "aes-256-cbc",
          passphrase: "aWCTJPET9fL7UBTp97hX99gdofeWKUf5tuxSuJeST2sEkyvkyinrfrj6EiSUTErF"
        }
      } )

Note: enforcing padding in encrypt and decrypt padding: RSA_PKCS1_PADDING

我正在使用:运行时版本:11.0.7+10-b765.64 amd64,OpenJDK 64-Bit

我找到的最好/最接近的参考是:How to read a password encrypted key with java?

我正在使用: IDE: IntelliJ IDEA 2020.1.3(社区版) 构建 #IC-201.8538.31,构建于 2020 年 7 月 7 日 运行时版本:11.0.7+10-b765.64 amd64 虚拟机:JetBrains s.r.o 的 OpenJDK 64 位服务器虚拟机。 Linux 5.4.0-65-通用 GC:ParNew、ConcurrentMarkSweep 内存:943M 核心:8 当前桌面:KDE

java --版本: openjdk 14.0.2 2020-07-14 OpenJDK 运行时环境 (build 14.0.2+12-Ubuntu-120.04) OpenJDK 64-Bit Server VM(build 14.0.2+12-Ubuntu-120.04,混合模式,共享)

下面提供的代码示例将失败:

java.security.NoSuchAlgorithmException: Cannot find any provider supporting 1.2.840.113549.1.5.13

我发现号码是 OID http://oid-info.com/get/1.2.840.113549.1.5.13

这实际上意味着:基于密码的加密方案 2 (PBES2)

我在 2020 年 10 月 30 日 09:27 发现了一个错误报告:https://bugs.openjdk.java.net/browse/JDK-8076999

所以问题是如何将给定的加密私钥 (PEM) 加载到 Java 应用程序中以便可以使用?有解决办法吗?

网上有很多关于使用 Bouncy Castle 库的资料,但我也找不到有效的示例。我在代码方面没有限制。如果可能,请提供一个示例。

例如,可以通过从密钥本身中提取 IV 来使用 AES 解密密钥吗?

String privKeyStrBase64Encoded = "THE ENCRYPTED PRIVATE KEY SHOWN FURTHER DOWN"
        privKeyStrBase64Encoded = privKeyStrBase64Encoded.replace("-----BEGIN ENCRYPTED PRIVATE KEY-----", "");
        privKeyStrBase64Encoded = privKeyStrBase64Encoded.replaceAll(System.lineSeparator(), "");
        privKeyStrBase64Encoded = privKeyStrBase64Encoded.replace("-----END ENCRYPTED PRIVATE KEY-----", "");

        byte[] encryptedPKInfo =  Base64.getDecoder().decode(privKeyStrBase64Encoded);
        EncryptedPrivateKeyInfo ePKInfo = new EncryptedPrivateKeyInfo(encryptedPKInfo);
        char[] password = "aWCTJPET9fL7UBTp97hX99gdofeWKUf5tuxSuJeST2sEkyvkyinrfrj6EiSUTErF".toCharArray();
        
        Cipher cipher = Cipher.getInstance(ePKInfo.getAlgName()); //Cannot find any provider supporting 1.2.840.113549.1.5.13
        PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
        // Now create the Key from the PBEKeySpec
        SecretKeyFactory skFac = SecretKeyFactory.getInstance(ePKInfo.getAlgName());
        Key pbeKey = skFac.generateSecret(pbeKeySpec);
        // Extract the iteration count and the salt
        AlgorithmParameters algParams = ePKInfo.getAlgParameters();
        cipher.init(Cipher.DECRYPT_MODE, pbeKey, algParams);
        // Decrypt the encryped private key into a PKCS8EncodedKeySpec
        KeySpec pkcs8KeySpec = ePKInfo.getKeySpec(cipher);
        // Now retrieve the RSA Public and private keys by using an
        // RSA keyfactory.
        KeyFactory rsaKeyFac = KeyFactory.getInstance("RSA");
        // First get the private key
        RSAPrivateCrtKey rsaPriv = (RSAPrivateCrtKey) rsaKeyFac.generatePrivate(pkcs8KeySpec);
        // Now derive the RSA public key from the private key
        RSAPublicKeySpec rsaPubKeySpec = new RSAPublicKeySpec(rsaPriv.getModulus(), rsaPriv.getPublicExponent());
        RSAPublicKey rsaPubKey = (RSAPublicKey) rsaKeyFac.generatePublic(rsaPubKeySpec);
    }
-----BEGIN ENCRYPTED PRIVATE KEY-----
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
-----END ENCRYPTED PRIVATE KEY-----

解密时的参考密钥如下:

-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

相关的公钥是这样你可以测试:

-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAva1oBuzswb28TDmaY1r6
CE5QIv0d9ffk/5tmzEgF0hhVVz0hgeyc9zOP38IlkFbd9naQz5jfrYezByEY6+/t
uPCzl76nrhcHgpxXsenenkhEGGVMYHEFHvsfmetdbP/TFYfaxCdjP5rcmdI5vnBI
7MYEhBqo+QWaF1fy4I4L8UMl1Kzqh4bc4s4w9r2d+JDHS7uwZBY9iwRSD4ChJyzH
GYBhnCNBmHz78F+hTlGrxLgGGouQXTxsGu1XUTUPqUgDXPRuD+plKtiJ10VADBsL
/WQaIbJF7CoWWvZNDqd1kiBNDdUmTfSw/VIIUjLuJpJIsZlnK3PY0/nA0FWBtK71
YsKNM/QFSQ6VQ78tMd0BCPEaMxCHaabsas8QZE4zteSKAJ5EigmggcjtiP63cZnp
iijX6av6myULqz94+yD4qyTGWyiA1JtqQACn6yBHhyUbs4y5/xTwGy9nFi0LI4IA
mmFPquGOLG1Vsy6b9KCoq9HiCjPPx+S28g76JDTC5cq2sU0lPJRGQfhIzUsDW7Pf
U0+rnUQRxi+rznMEsllj1uDQa1T608IjVRyF93vl4ni0QOY7ZYLH1qiK2YSAbhQ3
4krHOc7bUbWy7MyNRF1l+jcmgbxceC9Y/FhKyoZT2FSYHlOG/P95osLGXKjkAQYl
jVTPIlD4+5ZNhWLyfzDwlAMCAwEAAQ==
-----END PUBLIC KEY-----

【问题讨论】:

    标签: java rsa public-key-encryption private-key


    【解决方案1】:

    https://bugs.openjdk.java.net/browse/JDK-8076999不适用于您的问题;它是关于解析 PBES2 参数,而不是关于实现 PBES2 算法。根据底部到https://bugs.openjdk.java.net/browse/JDK-8202837 的链接,该问题实际上已在 2018 年的 11.0.1 版本中修复(至少对于使用 AES 的 PBES2,这是您所需要的),其中包括您的 14.0.2,事实证明你的 new EncryptedPrivateKeyInfo 没有失败。

    但是 Oracle/OpenJDK(从 14 开始)没有通过名称或 OID 实现通用 PBES2(keyfactory 和/或 Cipher),BouncyCastle(从 1.66 开始)也没有。前者确实按名称实现了一些特定的 PBES2 AES 方案,包括您需要的方案,但您必须在参数对象中挖掘才能找到该名称,如下所示:

    public static void main (String[] args) throws Exception {
        //String file = args[0], pw = args[1];
        String file = "n:66286457.pem", pw = "aWCTJPET9fL7UBTp97hX99gdofeWKUf5tuxSuJeST2sEkyvkyinrfrj6EiSUTErF";
        String pem = new String(Files.readAllBytes(Paths.get(file)));
        String b64 = pem.replaceAll("-----(BEGIN|END) ENCRYPTED PRIVATE KEY-----|\r?\n","");
        byte[] der = Base64.getDecoder().decode(b64);
        
        // Oracle/OpenJDK param parsing for PBES2 only works in 11.0.1 up 
        // and currently only for AES-128,256 see JDK-8076999,JDK-8202837
        EncryptedPrivateKeyInfo eki = new EncryptedPrivateKeyInfo(der);
        if( !eki.getAlgName().equals("1.2.840.113549.1.5.13") ) 
            throw new Exception ("not PBES2"); // or other handling 
        AlgorithmParameters top = eki.getAlgParameters();
        // hack to get nonpublic field and class, may break if Java keeps getting stricter
        Class<?> clazz = top.getClass();
        Object spi = access(clazz,"paramSpi").get(top);
        clazz = Class.forName("com.sun.crypto.provider.PBES2Parameters");
        String spiname = (String) access(clazz,"pbes2AlgorithmName").get(spi);
    
        SecretKeyFactory fact = SecretKeyFactory.getInstance(spiname);
        SecretKey skey = fact.generateSecret(new PBEKeySpec(pw.toCharArray()));
        Cipher ciph = Cipher.getInstance(spiname); 
        ciph.init(Cipher.DECRYPT_MODE, skey, eki.getAlgParameters());
    
        KeyFactory fac2 = KeyFactory.getInstance("RSA");
        PrivateKey pkey = fac2.generatePrivate(eki.getKeySpec(ciph));
    
        System.out.println ( ((RSAPrivateKey)pkey).getModulus() ); // for test
    }
    static Field access (Class<?> clazz, String name) throws Exception {
        Field f = clazz.getDeclaredField(name); 
        f.setAccessible(true);
        return f;
    }
    

    顺便说一句:IV 不是问题;此案例的 AlgorithmParameters 包装了 com.sun.crypto.provider.PBES2Parameters,其中包括 PBKDF2 参数(salt 和 itercount)和加密参数 (IV)。

    BouncyCastle(bcpkix 加 bcprov)确实是一种简单且完全受支持的方式;您可以直接解析 PEM(无需对其进行调整和 base64 解码)或解析(dePEMified 或其他方式)DER,然后使用PKCS8EncryptedPrivateKeyInfo.decryptPrivateKeyInfoJceOpenSSLPKCS8DecryptorProviderBuilderJcaPEMKeyConverter(它只是通过一个合适的KeyFactory,如果您愿意,可以手动完成)。对于 PEM,请参阅 Reading PKCS8 in PEM format: Cannot find provider 或对于 DER How read a PKCS8 encrypted Private key which is also encoded in DER with bouncycastle?(披露:都是我的)。

    【讨论】:

      【解决方案2】:

      我将您的私钥(希望是一个示例)插入 https://lapo.it/asn1js/

      得到了这个结果:

      SEQUENCE (2 elem)
        SEQUENCE (2 elem)
          OBJECT IDENTIFIER 1.2.840.113549.1.5.13 pkcs5PBES2 (PKCS #5 v2.0)
          SEQUENCE (2 elem)
            SEQUENCE (2 elem)
              OBJECT IDENTIFIER 1.2.840.113549.1.5.12 pkcs5PBKDF2 (PKCS #5 v2.0)
              SEQUENCE (3 elem)
                OCTET STRING (8 byte) 459A7208041047CA
                INTEGER 2048
                SEQUENCE (2 elem)
                  OBJECT IDENTIFIER 1.2.840.113549.2.9 hmacWithSHA256 (RSADSI digestAlgorithm)
                  NULL
            SEQUENCE (2 elem)
              OBJECT IDENTIFIER 2.16.840.1.101.3.4.1.42 aes256-CBC (NIST Algorithm)
              OCTET STRING (16 byte) BC87885E3BC691E4D689AB9A3CF66C81
        OCTET STRING (2384 byte) 3A36F8C22E5C7FB2FA107D7F1D6455EEF2228821D0311E6139D32C0ADA7A0DB2B9D4…
      

      正如您自己发现的那样,算法“OBJECT IDENTIFIER 1.2.840.113549.1.5.13 pkcs5PBES2 (PKCS #5 v2.0)”不是 Java 11 已知,因此我建议使用 Bouncy Castle 以编程方式读取密钥。

      你需要两个库来完成这项工作,bcprov-jdk15onbcpkix-jdk15on,我使用的是实际版本1.68。

      代码将打印一些调试信息,并且能够读取更多格式,我从自己的一个项目中获取。

      这是导入的简单输出:

      Load an Encrypted PCKS8 PEM Private Key In Java
      key in pkcs8 encoding
      encryption algorithm: 1.2.840.113549.1.5.13
      privateKey: RSA Private CRT Key [e0:a5:fb:49:0c:7d:12:89:93:06:ea:43:30:60:af:02:f9:0a:6a:1f],[56:66:d1:a4]
                   modulus: bdad6806ececc1bdbc4c399a635afa084e5022fd1df5f7e4ff9b66cc4805d21855573d2181ec9cf7338fdfc2259056ddf67690cf98dfad87b3072118ebefedb8f0b397bea7ae1707829c57b1e9de9e484418654c6071051efb1f99eb5d6cffd31587dac427633f9adc99d239be7048ecc604841aa8f9059a1757f2e08e0bf14325d4acea8786dce2ce30f6bd9df890c74bbbb064163d8b04520f80a1272cc71980619c2341987cfbf05fa14e51abc4b8061a8b905d3c6c1aed5751350fa948035cf46e0fea652ad889d745400c1b0bfd641a21b245ec2a165af64d0ea77592204d0dd5264df4b0fd52085232ee269248b199672b73d8d3f9c0d05581b4aef562c28d33f405490e9543bf2d31dd0108f11a33108769a6ec6acf10644e33b5e48a009e448a09a081c8ed88feb77199e98a28d7e9abfa9b250bab3f78fb20f8ab24c65b2880d49b6a4000a7eb204787251bb38cb9ff14f01b2f67162d0b2382009a614faae18e2c6d55b32e9bf4a0a8abd1e20a33cfc7e4b6f20efa2434c2e5cab6b14d253c944641f848cd4b035bb3df534fab9d4411c62fabce7304b25963d6e0d06b54fad3c223551c85f77be5e278b440e63b6582c7d6a88ad984806e1437e24ac739cedb51b5b2eccc8d445d65fa372681bc5c782f58fc584aca8653d854981e5386fcff79a2c2c65ca8e40106258d54cf2250f8fb964d8562f27f30f09403
           public exponent: 10001
      

      以下代码没有异常处理,仅用于教育目的:

      import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
      import org.bouncycastle.jce.provider.BouncyCastleProvider;
      import org.bouncycastle.openssl.PEMEncryptedKeyPair;
      import org.bouncycastle.openssl.PEMKeyPair;
      import org.bouncycastle.openssl.PEMParser;
      import org.bouncycastle.openssl.bc.BcPEMDecryptorProvider;
      import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
      import org.bouncycastle.operator.InputDecryptorProvider;
      import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
      import org.bouncycastle.pkcs.PKCSException;
      import org.bouncycastle.pkcs.jcajce.JcePKCSPBEInputDecryptorProviderBuilder;
      
      import java.io.IOException;
      import java.io.StringReader;
      import java.security.PrivateKey;
      import java.security.Security;
      import java.security.cert.CertificateException;
      
      public class MainSo {
          public static void main(String[] args) throws IOException, PKCSException, CertificateException {
              // https://stackoverflow.com/questions/66286457/load-an-encrypted-pcks8-pem-private-key-in-java
              System.out.println("Load an Encrypted PCKS8 PEM Private Key In Java");
              // you need 2 bouncy castle libraries, I'm using the actual ones version 1.68:
              // https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on
              // bcprov-jdk15on-1.68.jar
              // https://mvnrepository.com/artifact/org.bouncycastle/bcpkix-jdk15on
              // bcpkix-jdk15on-1.68.jar
      
              Security.addProvider(new BouncyCastleProvider());
              char[] password = "aWCTJPET9fL7UBTp97hX99gdofeWKUf5tuxSuJeST2sEkyvkyinrfrj6EiSUTErF".toCharArray();
              String privKeyStrBase64Encoded =
                      "-----BEGIN ENCRYPTED PRIVATE KEY-----\n" +
                              "MIIJrTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIRZpyCAQQR8oCAggA\n" +
                              "MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBC8h4heO8aR5NaJq5o89myBBIIJ\n" +
                              "UDo2+MIuXH+y+hB9fx1kVe7yIogh0DEeYTnTLAraeg2yudSoNLq4xF2Ftid7Ax9Q\n" +
                              "6BwdrY6YB8/Pk9vn7gWBoTdrfUpJZZu6DtLu7URXPTN5mgRNmJzC4uAv9y+T8CRv\n" +
                              "sLXjwYOSbF4Vsbm8JlgJPuKPKkH1lTi3t1jxik+nNrDyAXRJOYfowkW9xnGqNSxq\n" +
                              "GEFXN1S16msrEq+chTblSpn8liY6o2Yk405qJ2G+dz/Lv50vOjoc1GTh8vNw23ER\n" +
                              "HckRiRS8TY/sjyVtI6tZp+30azbMcXgLEHFoMDIm/bu366/aYyRI2fI76LWJUuHA\n" +
                              "ZmU8GYU8j0+IewLPgtAXkjkTMuvrucHgCH3/ZRDDqFI+YTj3q19r4xmPGRWoxp/c\n" +
                              "2Oex3HfUCutLonof+89I5OGfvlnaESaQcSzLJSzPV6qXGMMTFVZ6G2ZW1gfaJGC1\n" +
                              "VwyL2k7Ejg8ncnOPKsf27oCFOflDLvfVQc/iW/XXZfFkGpO3xgoBE1CYq9y1w4VZ\n" +
                              "gzbi88AwIV2oJTqUQkNFkDMYwPnCj/D4BAT1ipysMA3Q9gs4+EnfD9nMevtef9dR\n" +
                              "wYaPNcnC/dy3Soacqm6VPOQ9RP77BIMNAQKlE95lsB++DXhPs3uDyxavar0pnJn2\n" +
                              "nBfPxRGbJeVc9hxyxzQtosgkrVIIt/0nEUigTukYCESpvJAd1vsupbi6v/FGXvzg\n" +
                              "4aU2aGTYUWcPbsi4kZUQH8ysmaPAC4QmHBPumzmeA+k4LxUaGJaWwYxCqD+WBCye\n" +
                              "D1w1utaFTjG+HSwIqGvVTxMXZswm3Tw8+bAu5UpPeIpXejWa06duCxDkvncpOW04\n" +
                              "En3hJuPTBEMxzCVIlYjk3Z7bNtjOayJRqxrHlY0OkHoy8BXRSN6f9O84go8S2qie\n" +
                              "MmchFTarLfG7dir9SyDJdH2bTephRdGV3nmPLX/xza3ES4JmyHEOmYxbgqnSXsSG\n" +
                              "8RZQ7Cg5tQVDHf1ydgyfqJ/lcG3SaehKHVuUR/Ulv8u5WpAYoxr1aGWafUtFYSoC\n" +
                              "g/RF6E9gEbrcl/KnnPEcG5jI+86BeEsfVkpjqsh10lHG008oyeI92nXzYvZv76+d\n" +
                              "9bshKT6qERGlA+2HYvZkNMtuwh0eUlq0sHCKQW3D7PTerfok3EP5ohiHwIZ0cME6\n" +
                              "Jq8Dz4ORGFFAdYi86NhCkW7s4nXtP1utppaZBeZLF7nxk0XYIb3NP+a/Ll9eQQSe\n" +
                              "WNDdv/387J++PzpygviGmZfF3rBl253cbPX/nhhNAOiPajdN2q3qDTpAnZpE3G+v\n" +
                              "t9OnNFtgmYZ0SEOMxo8T3kMVnmUhP3OsVoWcwcqIOjwPXmwCL8c4Dju8sxfZyCQh\n" +
                              "rRGKsbJyON+UTXA07rpH1JzqmWSQDiir75JBsLlX56yKIzKe86CWDlohbyykVkPs\n" +
                              "vT0qTgpSvkU8N3jErjZUJsIk29uMFVcVjvR1GmMiCOIG7jZ+KefjXx43JFl6Av4w\n" +
                              "fjdfaMb2J2/jd9suBhGDpakftZl57Qhz/FN8yDONmetJMcum5+dwxnbB9hCP7fpJ\n" +
                              "T5tnE/w0KNFJEtXeylOHlU225czr4+gdsj+ncKiTEwIm0htulrDDgKsN74EXPcor\n" +
                              "ywEpc3oZZMFF8084g6j0rjAnLO/fgtf0nXfMGDGUfIGo9AJFqoYKKMu5u5vQ+XY0\n" +
                              "cc93QAB1lJ1a8yyRwfUoLHNbq2AJ9NMw1sNvkRV26dpk7ecZ8LgoUdizbztz5vb2\n" +
                              "6VfArHvT1pZEdjgPwsQnegu4i4/ELXZWTZu2hfIM/aTgP9avAQmQDiKEnOnOQZ/3\n" +
                              "QyFEFo5qFfH4wUKbLoQXVWslIyz7bRd+F26GoJTLPOHkPAZZ3UCCzVUHwCzc78eO\n" +
                              "o9V4wgfVNFNkdyXi81X97v0bKKbkxfanz0+kBSDmeOUOKTDmyFOmhbC5SKLBF3k/\n" +
                              "gNHR6BzCET7ReGZ+qIVF10Oy6SzP7M/Fmt1TU2y58CoiM3pKPDUzDYX47NCoUU6c\n" +
                              "S8iberxh1O4mMxDNfwQmFzXe8cst8JGBxWX2O+Oqvl9EpGojWGXY9ydut//nDRPv\n" +
                              "sQyMngcK9KLAG8/JL4hp8plKee+JtNzelCHzbjObE18waF3cveKns8WaumqpgzyT\n" +
                              "2nX0vct0UxBeynIefaMEwT4WbsFXu/iMrRQpsrQmxlq3LLiet5lj3UdE9EiBvQox\n" +
                              "FftQdOR8t36zNzCDnHmPzmrxggiKEjDw6BFN4sV+jm6SZWzAlypplzBHGfexYS8t\n" +
                              "4taG0Z6lXev9xYgAcTFZrNJRVQ18c8/8W2V+LMB86LNTG7IKa8Cbo4FMPFGQhlAZ\n" +
                              "n3URL2jaV7Cf2tTuHq0IT4Sv4/dx/Cttx8qdFjGbTt3ILCpUsh9KjxTEjteA6Ydu\n" +
                              "e8lYsU8C5E3mdldojkie8iZHFSjrwRuk4EyUGXRoMe900CDHXmNQ3g6Cb2cE3AgM\n" +
                              "RQvCLTQgDpQe6WJ4/HFMRXtCE3dX6P46E1968MYlgn4RAmYel1HPIh/8oWaLmwxX\n" +
                              "IPEO/kxjybWkrvRDw8tQxVbR8D3sdurmYuMid2EpzI1OFLPp08JcpHn+9LyvBEw9\n" +
                              "9w8ngP260HSt0rckOCyRm+JWmFRmql6LRwdWl0ht1yTASDn1+/BkQm9JfOyjMxlE\n" +
                              "mXFdCHL8EK0+xcYn1IMypP0oG7TA0o1BK+vsmDoEO1pT3Qh4pTA2lFAoshWR9YBP\n" +
                              "ZMW2Pyscia5+wkRuL06yAujyJP5OOmHnLp9uni8tpo4OtSqt4DRCYLM9hsB2zL6Y\n" +
                              "3WvgavGznflve604cZ1jkJFkzg74WgwdXXn9XrI9OoEJm84avdQIdK2WlOiA5md5\n" +
                              "lMfyMVZtCZLh/6KpC8jB4t0FlOGdoQxubslWDXcJwhPFO0KvdNv+6TeWjt8ZBECI\n" +
                              "zRMq+jAR6yLN0gld3y4YI37cll6kr6wNYBd0NoL7Bzl/WtPn8MJTrwcRpohNqQkJ\n" +
                              "8MOrqL14yRDvPtQ2Rijzztnd3Vb9EL0Zgkrwh9uD1ZTVDyHWHnmwW7LeBOi0/vD3\n" +
                              "k/bi3qVGEqAc5YkCZbydMfzw0W506WsEMlysfbAhTEx+w/IV31mfD4VwpZ+ueSyE\n" +
                              "crFUOhrrE+9a34z2mSDke3Hte1pvjhIIH6B30mjFyQh3sOoouwl9PkvbuUo8Q7v2\n" +
                              "ojIAhmdgVMd19lfA+ihPnmalOtR7hCwdrjoHR3N5A+Ng\n" +
                              "-----END ENCRYPTED PRIVATE KEY-----";
              PrivateKey rsaPrivateKey = stringToPrivateKey(privKeyStrBase64Encoded, password);
              System.out.println("privateKey: " + rsaPrivateKey);
          }
      
          static public PrivateKey stringToPrivateKey(String s, char[] password)
                  throws IOException, PKCSException {
              PrivateKeyInfo pki;
              try (PEMParser pemParser = new PEMParser(new StringReader(s))) {
                  Object o = pemParser.readObject();
                  if (o instanceof PKCS8EncryptedPrivateKeyInfo) { // encrypted private key in pkcs8-format
                      System.out.println("key in pkcs8 encoding");
                      PKCS8EncryptedPrivateKeyInfo epki = (PKCS8EncryptedPrivateKeyInfo) o;
                      System.out.println("encryption algorithm: " + epki.getEncryptionAlgorithm().getAlgorithm());
                      JcePKCSPBEInputDecryptorProviderBuilder builder =
                              new JcePKCSPBEInputDecryptorProviderBuilder().setProvider("BC");
                      InputDecryptorProvider idp = builder.build(password);
                      pki = epki.decryptPrivateKeyInfo(idp);
                  } else if (o instanceof PEMEncryptedKeyPair) { // encrypted private key in pkcs8-format
                      System.out.println("key in pkcs1 encoding");
                      PEMEncryptedKeyPair epki = (PEMEncryptedKeyPair) o;
                      PEMKeyPair pkp = epki.decryptKeyPair(new BcPEMDecryptorProvider(password));
                      pki = pkp.getPrivateKeyInfo();
                  } else if (o instanceof PEMKeyPair) { // unencrypted private key
                      System.out.println("key unencrypted");
                      PEMKeyPair pkp = (PEMKeyPair) o;
                      pki = pkp.getPrivateKeyInfo();
                  } else {
                      throw new PKCSException("Invalid encrypted private key class: " + o.getClass().getName());
                  }
                  JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
                  return converter.getPrivateKey(pki);
              }
          }
      }
      

      【讨论】:

      • JcePKCSPBEInputDecryptorProviderBuilder 是最近的变化吗?在我使用的大约 1.48 到 1.66 的版本中,它是 JceOpenSSLPKCS8DecryptorProviderBuilder。
      • @dave_thompson_085:我从 2020 年 7 月的这个答案中获取了代码,它对我有用 - 我从未使用过“JceOpenSSLPKCS8DecryptorProviderBuilder”,但在其他一些评论中提到了它:stackoverflow.com/a/62739844/8166854跨度>
      • 这可能对其他人也有帮助:crypto.stackexchange.com/questions/88362/…
      • 快速参考:BC87885E3BC691E4D689AB9A3CF66C81 是 IV。
      猜你喜欢
      • 2021-09-22
      • 1970-01-01
      • 1970-01-01
      • 2013-12-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多