在 Java 中加载加密的 PCKS8 PEM 私钥

如何解决在 Java 中加载加密的 PCKS8 PEM 私钥

我希望加载/使用在 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 位

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

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

java --version: openjdk 14.0.2 2020-07-14 OpenJDK 运行时环境(构建 14.0.2+12-Ubuntu-120.04) OpenJDK 64 位服务器 VM(构建 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)

我发现一个错误报告说它已经解决:https://bugs.openjdk.java.net/browse/JDK-8076999 于 2020-10-30 09:27

那么问题如何将给定的加密私钥 (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-----

解决方法

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 和/或密码),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?(披露:均为我的)。

,

我将您的私钥(希望是示例)插入到 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);
        }
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-