我需要在java中生成openssl key 对来模拟以下内容:
openssl ecparam -name prime256v1 -genkey -noout -out prime256v1.key
openssl pkcs8 -topk8 -in prime256v1.key -out prime256v1-priv.pem -nocrypt
openssl ec -in prime256v1-priv.pem -pubout -out prime256v1-pub.pem
我的java程序如下:
public static void main(String args[]) throws Exception{
Security.addProvider(new BouncyCastleProvider());
KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", "BC");
ECGenParameterSpec spec = new ECGenParameterSpec("secp256r1");
g.initialize(spec);
KeyPair keyPair = g.generateKeyPair();
byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
String publicKeyContent = Base64.encode(publicKeyBytes);
String publicKeyFormatted = "-----BEGIN PUBLIC KEY-----" + System.lineSeparator();
for (final String row:
Splitter
.fixedLength(64)
.split(publicKeyContent)
)
{
publicKeyFormatted += row + System.lineSeparator();
}
publicKeyFormatted += "-----END PUBLIC KEY-----";
BufferedWriter writer = new BufferedWriter(new FileWriter("publickey.pem"));
writer.write(publicKeyFormatted);
writer.close();
byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
String privateKeyContent = Base64.encode(privateKeyBytes);
String privateKeyFormatted = "-----BEGIN PRIVATE KEY-----" + System.lineSeparator();
for (final String row:
Splitter
.fixedLength(64)
.split(privateKeyContent)
)
{
privateKeyFormatted += row + System.lineSeparator();
}
privateKeyFormatted += "-----END PRIVATE KEY-----";
BufferedWriter writer2 = new BufferedWriter(new FileWriter("privatekey.pem"));
writer2.write(privateKeyFormatted);
writer2.close();
}
上面的代码可以工作,但生成的私钥似乎比我在顶部提到的通过命令行实用程序生成的私钥更长。
使用命令行的私钥:
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgGuyf3+/6+rnDKw0D
WbxVyggwNL0jlTVAzGm6cpl3ji2hRANCAAQ7zLtxLLvl6LJHJAlYAZr4hAc09fZn
bAniYIeKVqVBdKIvb5R445PFiUDFcfyneeX/resPXJHMEm/vAxfQeMqL
-----END PRIVATE KEY-----
使用 java 的私钥:
-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgYFPrkmxnwjVBgpUV
B02/luLD1rt9
UWZHj62YdhwYQESgCgYIKoZIzj0DAQehRANCAATZp7Jl8KXXApA
hvv9qeQtX5LbHQkrCdx3DfkUC
GgCUMSJWKxs7yJPNKtFZnFUTFZfyEF76fdEzky
zIon5H04MX
-----END PRIVATE KEY-----
即使我在这里删除了 2 行额外的行,即使如此,这似乎也是一个更大的关键。
使用命令行的公钥:
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEO8y7cSy75eiyRyQJWAGa+IQHNPX2
Z2wJ4mCHilalQXSiL2+UeOOTxYlAxXH8p3nl/63rD1yRzBJv7wMX0HjKiw==
-----END PUBLIC KEY-----
使用 Java 的公钥:
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2aeyZfCl1wKQIb7/ankLV+S2x0JK
wncdw35FAhoA
lDEiVisbO8iTzSrRWZxVExWX8hBe+n3RM5MsyKJ+R9ODFw==
-----END PUBLIC KEY-----
所以,我的第一个问题是关于私钥长度。看起来更长了。 我的第二个问题是我似乎没有正确分割生成的 key 字节。线路肯定比预期的要多。如何纠正?
最佳答案
... the private key length ... seems longer
它是,或者准确地说,表示/包含私钥的结构更长。 Java 在 PKCS8 中包含可选且不必要(冗余)的算法特定数据的“parameters”字段,即 SEC1 appendix C.4 中定义的 ECPrivateKey,而 OpenSSL才不是。读回时这将被忽略。两个结构中的实际键值是正确的大小。
I am not splitting the generated key bytes properly
而是分割编码字节(关键结构)的(base64)字符。
查看 Splitter
之前 Base64.encode
的输出。我敢打赌您会发现它已经在每 76 个 base64 字符后包含换行符,与 MIME 标准(RFC 1521 等)一致,有些人认为该标准更常见(或更重要?)或至少比 PEM 更新。 (尽管 XML 和 JWX 甚至更新,现在也很常见,并且根本不插入换行符。)因此,您的 Splitter
需要:
- 第一行的前 64 个字符
- 第一行剩余的 12 个字符、换行符和第二行的 51 个字符
- 第二行剩余的 25 个字符、换行符和第三行(最多)38 个字符
- 等等
尽管 OpenSSL 每 64 个字符(最后一行除外)写入正文换行符的 PEM 文件,但根据 PEM 标准 (RFC 1421),它始终能够读取 4 到 76 个字符的任意倍数的文件,与 MIME 一致。自 2016 年 1.1.0 以来的最新版本现已相当广泛采用,可以读取最多数百个字符的行。因此,如果您的文件要由(任何使用)OpenSSL 库读取,您可以只编写 split-at-76 版本,无需任何进一步的更改,除了确保有一个换行符终止最后行。其他软件可能有所不同;如果您需要安全或严格合规,请首先从 Base64.encode
输出中删除换行符,然后以正确的间距 64 将它们添加回来。请参阅 the recently published respecification 。
PS:如果您使用 Java 将此 key 放入 PKCS12 keystore (这要求您拥有/获取/创建它的证书),openssl 命令行可以读取直接转换,并将 (1) 私钥转换为 PEM,(2) 将证书转换为 PEM,您可以从中提取 PEM 中的公钥。
关于java - 使用java生成openssl key 对,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61145701/