在 Java 中加载加密的 PCKS8 PEM 私钥 [英] Load an Encrypted PCKS8 PEM Private Key In Java

查看:228
本文介绍了在 Java 中加载加密的 PCKS8 PEM 私钥的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I wish to load/use an Encrypted Private Key provided to me in a Java application. Please find the keys (Encrypted Private, Decrypted Private and Public keys shown below).

加密的私钥密码:aWCTJPET9fL7UBTp97hX99gdofeWKUf5tuxSuJeST2sEkyvkyinrfrj6EiSUTErF"

The Encrypted Private key password: "aWCTJPET9fL7UBTp97hX99gdofeWKUf5tuxSuJeST2sEkyvkyinrfrj6EiSUTErF"

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

The keys were generated by using Crypto in NodeJS using:

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 位

I am running using: Runtime version: 11.0.7+10-b765.64 amd64, OpenJDK 64-Bit

我发现的最好/最接近的参考是:How用java读取密码加密密钥?

The best/closest reference I have found is: 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 amd64VM:JetBrains s.r.o. 的 OpenJDK 64 位服务器 VM.Linux 5.4.0-65-通用GC:ParNew、ConcurrentMarkSweep内存:943M核心数:8当前桌面:KDE

I am running using: IDE: IntelliJ IDEA 2020.1.3 (Community Edition) Build #IC-201.8538.31, built on July 7, 2020 Runtime version: 11.0.7+10-b765.64 amd64 VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o. Linux 5.4.0-65-generic GC: ParNew, ConcurrentMarkSweep Memory: 943M Cores: 8 Current Desktop: KDE

java --version:openjdk 14.0.2 2020-07-14OpenJDK 运行时环境(构建 14.0.2+12-Ubuntu-120.04)OpenJDK 64 位服务器 VM(构建 14.0.2+12-Ubuntu-120.04,混合模式,共享)

java --version: openjdk 14.0.2 2020-07-14 OpenJDK Runtime Environment (build 14.0.2+12-Ubuntu-120.04) OpenJDK 64-Bit Server VM (build 14.0.2+12-Ubuntu-120.04, mixed mode, sharing)

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

The code example provided below will fail with :

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

I found the number to be OID http://oid-info.com/get/1.2.840.113549.1.5.13

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

Which effectively seems to mean: Password-Based Encryption Scheme 2 (PBES2)

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

I found a bug report which says it has been resolved: https://bugs.openjdk.java.net/browse/JDK-8076999 on the 2020-10-30 09:27

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

So the question How do you load a given Encrypted Private Key (PEM) into a Java application so it can be used ? Is there a work around ?

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

There is a lot of stuff online about using the Bouncy Castle libs but I could not find a working example either. I have no restrictions code wise. Please provide an example if possible.

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

For example can the key be decrypted using AES by extracting the IV from the key itself ?

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-----

参考解密时的密钥如下:

For reference the key when decrypted looks like:

-----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/浏览/JDK-8202837 该问题实际上已在 2018 年 11.0.1 版中解决(至少对于使用 AES 的 PBES2,这是您需要的),其中包括您的 14.0.2,事实证明您的 new EncryptedPrivateKeyInfo 没有失败.

https://bugs.openjdk.java.net/browse/JDK-8076999 is not applicable to your problem; it is about parsing PBES2 parameters not about implementing the PBES2 algorithm(s). And per the link at the bottom to https://bugs.openjdk.java.net/browse/JDK-8202837 that problem was actually fixed (at least for PBES2 using AES, which is what you need) in 2018 in 11.0.1 up, which includes your 14.0.2 as evidenced by the fact that your new EncryptedPrivateKeyInfo did NOT fail.

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

But Oracle/OpenJDK (as of 14) does not implement generic PBES2 (keyfactory and/or Cipher) either by name or OID, and neither does BouncyCastle (as of 1.66). The former does implement some specific PBES2 AES schemes by name, including the one you need, but you must dig inside the parameters object to find that name, like this:

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).

BTW: IV is not a problem; the AlgorithmParameters for this case wraps com.sun.crypto.provider.PBES2Parameters which includes both the PBKDF2 parameters (salt and itercount) and the encryption parameter (IV).

BouncyCastle(bcpkix 加 bcprov)确实是一种简单且完全受支持的方式;您可以直接解析 PEM(无需调整和 base64 解码)或解析(dePEMified 或其他)DER,然后使用 PKCS8EncryptedPrivateKeyInfo.decryptPrivateKeyInfoJceOpenSSLPKCS8DecryptorProviderBuilder, 和 JcaPEMKeyConverter(它只是通过合适的 KeyFactory 运行它,如果你愿意,你可以手动执行).对于 PEM,请参阅以 PEM 格式读取 PKCS8:找不到提供者或对于 DER 如何读取同样用 bouncycastle 编码在 DER 中的 PKCS8 加密私钥?(披露:都是我的).

BouncyCastle (bcpkix plus bcprov) is indeed an easy and fully supported way; you can parse the PEM directly (without adjusting and base64-decoding it) or else parse the (dePEMified or otherwise) DER, and either way then use PKCS8EncryptedPrivateKeyInfo.decryptPrivateKeyInfo with JceOpenSSLPKCS8DecryptorProviderBuilder, and JcaPEMKeyConverter (which just runs it through a suitable KeyFactory, which you can do manually if you prefer). For PEM see Reading PKCS8 in PEM format: Cannot find provider or for DER How read a PKCS8 encrypted Private key which is also encoded in DER with bouncycastle? (disclosure: both mine).

这篇关于在 Java 中加载加密的 PCKS8 PEM 私钥的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆