gpt4 book ai didi

java - 如何在Java中将私钥写入受密码保护的DER格式?

转载 作者:行者123 更新时间:2023-12-02 11:43:58 27 4
gpt4 key购买 nike

我有一个用 Java 生成的 RSA key 对,我需要以编程方式将私钥写入与运行此命令时 openssl 相同的格式(并根据提示输入相应的数据,即要保护的密码)私钥):

openssl req -out request.csr -newkey rsa:2048 -keyout privkeyfile

生成 key 对的 Java 代码非常标准:

KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair keyPair = keyGen.genKeyPair();

运行 openssl 命令(在我的 Windows 计算机上)的示例输出是:

-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQI1c9lGdEA388CAggA
MBQGCCqGSIb3DQMHBAgKdWZEOyS2XgSCBMgI7b/vAeE6yz136BZkOzOPLv0uTz/Z
5mP4xO8IdAybE+PHJ71Mro4kgz+EMN39dk0ZWxbNnPpHGD+a6LfNKxos8fJL+dbz
dgc7I4fH9UQFLnYM64Xmq4aG66fIehuhqXBUUru+PJdBf5bfPDJDYAVEUsZ2J8bW
n4pLeS62Orwe+hhe/i/Y4gmgGAxGhUlDGc7T/N4RvhWUVNXQKGCGynj9Mv+LRzW6
rkGbBALQAKnj2tuihGSKhhR3WoNxTFqU9HsRGkzbJ5AiYhyBk7ObQw285TyIQS6k
OH25byIeaqzQ3Xn/wB6VrOQrsCvbWim1DZEIGRp6B+RKd0vUrkxFZRBsLdGXN1w/
bCM4dmhUJng2O+a9tSif7CC0emJqXgvkE2lGA9RMZltfOK+Kohi4L0WKxIX8TQqP
KzDhecSOaOkdXI0Tpho1QZDS6D+nvN2OiXlswgB0By3pkLdx4j7ZtjhqH/cg4rlE
8R1HzcilIrSlMK579UNGieis2wHaWobeinJqP6ruHK3HiAvG/WLFQ4TKexFa4/gy
EdXPaV9owRi+9nyRZGT8NsfzUDg5oTBLcg08uOlNHr8z8pF6a7l2sr4bzgcYFlko
BCIunMJpXYp/lUnL9daElbOPbGgeLNa8KfU7tXnzYsCg3iUx79fQUoql2pn2wMc3
0vQVTZ7/Enzl8cM2srl0uf1JMxMGOJ2kbdYZ8VwxaaMHnghN97eBsp+aRFCuAN4x
+D90ABBxRcBwzBOf8sT77vYXvQZNqUnzl5GJh2hlXCB5upNFqbSGaa6Yk+y4cw5e
3tB3/BHwZop2AAnPexnnQuCsn+SpCiLF+/agMouph61oWWJYQMwmUemNy/5G6AoP
KdBGqBAXonRSk8pBNqglHl0GOiBITl45+Bk4JBGM6+NcEpQ8B3OA+Vkj0n/aF/Iw
66Fo+UyA64fboC3q6DLxHZuTAY/giytwUW2QM4yFkEOm1v1WisTf0MO3Zt+ghuBn
8DG9MXGxP0XA9QzHAjCcDD8DK/hXsxaBg6xOV4bV+HhhJXsyWQAqcqKQro9Ik3L9
YvdJNU9BWyzKV79j5gYkDgLZgcA8QrGDArFZ5Hr9HdepBu8Njk09YDKJsfVMmk4E
6NbzxqHgPyYY3QtANLKg3EImBfuRHwfgfbaamrmYE0fSyh/QJMK0zDtDpkgiiTre
A8b16rBdBxZBSaO/J+Oje0pePLBRRhwX4WxcPsZeN5fO6S2NTECrWsf0jDG4D6pa
cannasXB4LoBifAYhKKTXFbQRY74wOxVfI7gw0qEjB7Jb1M2zCMwddgumOiCzGpu
d9voABJdGMdhwZ/FLbuxcr0y3p8Y5N9vW8ffSlxEtvhbPlszpgTPi2WWTNE+wTUQ
so4cvWFq9SP3Mg6Te6AStjdN1Mnhj2fb7ogxa5rsNxVrE/guRVgly4i9vG7Mi2Wd
bhT1vQypyL9g97nq0rRznDAjAtLenOagK4h+WJgZN2RpUhkWmO1trLGao/PrhgvD
8mOMCnZIQGMk5vS55druRoakPjsx4yZpzZvw5gPBXJ0H1KmbFUO1aSy/6N4nVBW+
Khr+ZHxboPD0zxJMzANjuOIJ/C46Hx5Wb/VP49NDmOLzLAi3+YSAhi3PB9D8vzxQ
MwM=
-----END ENCRYPTED PRIVATE KEY-----

编辑更改了 openssl 的示例输出

编辑我尝试使用下面的代码使用Java读取openssl生成的私钥文件来尝试获取一些参数,但最终得到以下异常:

Exception in thread "main" java.io.IOException: ObjectIdentifier() -- data isn't an object ID (tag = 48)
at sun.security.util.ObjectIdentifier.<init>(Unknown Source)
at sun.security.util.DerInputStream.getOID(Unknown Source)
at com.sun.crypto.provider.PBES2Parameters.engineInit(PBES2Parameters.java:267)
at java.security.AlgorithmParameters.init(Unknown Source)
at sun.security.x509.AlgorithmId.decodeParams(Unknown Source)
at sun.security.x509.AlgorithmId.<init>(Unknown Source)
at sun.security.x509.AlgorithmId.parse(Unknown Source)
at javax.crypto.EncryptedPrivateKeyInfo.<init>(EncryptedPrivateKeyInfo.java:95)
at crypto.ReadOpensslKey.main(ReadOpensslKey.java:35)

读取文件的Java代码:

package crypto;

import org.bouncycastle.util.encoders.Base64;
import javax.crypto.EncryptedPrivateKeyInfo;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;

public class ReadOpensslKey {

public static void main(String[] args) throws Exception {

String encrypted = new String(Files.readAllBytes(Paths.get("<insert path to openssl generated privkeyfile>")));

//Create object from encrypted private key
encrypted = encrypted.replace("-----BEGIN ENCRYPTED PRIVATE KEY-----", "");
encrypted = encrypted.replace("-----END ENCRYPTED PRIVATE KEY-----", "");
EncryptedPrivateKeyInfo pkInfo = new EncryptedPrivateKeyInfo(Base64.decode(encrypted)); // exception is thrown here
System.out.println(pkInfo.getAlgName());
PBEKeySpec keySpec = new PBEKeySpec("abcde".toCharArray()); // password
SecretKeyFactory pbeKeyFactory = SecretKeyFactory.getInstance(pkInfo.getAlgName());
PKCS8EncodedKeySpec encodedKeySpec = pkInfo.getKeySpec(pbeKeyFactory.generateSecret(keySpec));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey encryptedPrivateKey = keyFactory.generatePrivate(encodedKeySpec);
}

}

最佳答案

首先,您真的需要这种特定格式,还是只是 OpenSSL(以及使用 OpenSSL 的程序,如 Apache httpd、nginx、curl 和 PHP 等)可以使用的格式?如果是后者,还有其他几种更容易、更好的选择。但你没有问这个,所以我不会回答。

其次,您必须拥有非常旧的 OpenSSL。自 2010 年发布 1.0.0 以来,req -newkey -keyout 写入 PKCS8 格式,而不是传统的又名遗留格式。

第三,这个格式是PEM而不是DER;有传统的 DER 格式,但无法加密。 (PKCS8 可以在 DER 或 PEM 中加密。)

第四,如果你可以使用BouncyCaSTLe,它可以直接做到这一点;来自(任何最新版本的)bcpkix 在标准 JCE PrivateKey 上使用 org.bouncycaSTLe.openssl.jcajce.JcaMiscPEMGeneratorJcePEMEncryptorBuilder 指定 DES-EDE3-CBC 在内存中创建一个 PEMObject,然后使用 PEMWriter 将其写出。即使您实际上无法使用 BC,它也是开源的(在我看来,设计得相当好,尽管大多数评论很少),并且它可能会帮助您查看他们的代码。

也就是说,您所询问的内容(几乎)由 PEM_write_RSAPrivateKey 的手册页记录(它应该在您的系统上,但由于您的版本较旧,您最好使用 the web copy ),位于接近结尾处标题为“PEM ENCRYPTION FORMAT”的部分,结合 EVP_BytesToKey 的引用手册页。具体来说:

  • 构建“传统”编码 RSAPrivateKey from PKCS1 (currently rfc8017) ,而不是PKCS8/rfc5208 PrivateKeyInfo JCE PrivateKey.getEncoded() 返回的编码。 PKCS8 编码确实包含 PKCS1 编码作为一部分(PKCS8 是围绕任意数量的特定于算法的编码的算法通用包装器),因此您可以从 PKCS8 中提取 PKCS1(如 BC 所做的那样)或直接从关键组件n、e、d、p、q、dmp1、dmq1、qinvp。 (otherPrimeInfos 仅适用于所谓的“多重”RSA,这意味着超过 n 的 2 个因子,而且几乎没有人实际使用。)
  • 使用应用于password||salt的一次MD5迭代从密码中导出实际的加密 key ,其中salt是随机IV的副本(3DES为8字节) )加上(因为这还不够)MD5(firstblock||password||salt),然后将总数截断为 24 个字节。

  • 使用 3DES(JCE 称为 DESEDE)和 CBC(使用如上所述的 IV)和 PKCS5 填充进行加密。 (对于 3DES 或 DES, key 的每个字节的低位名义上是奇偶校验,但您不需要设置它们,因为 JCE 没有实现它们。)

  • 转换为 base64,每 64 个字符换行一次,并添加 BEGIN 和 END 行以及标题行,正如您猜对的那样,包括十六进制的 IV

关于java - 如何在Java中将私钥写入受密码保护的DER格式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48333877/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com