我需要用java生成openssl密钥对,它模拟以下内容:
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();
}
上面的代码有效,但生成的私钥似乎比我在顶部提到的通过命令行实用程序生成的私钥更长。
Privatekey与命令行:
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgGuyf3+/6+rnDKw0D
WbxVyggwNL0jlTVAzGm6cpl3ji2hRANCAAQ7zLtxLLvl6LJHJAlYAZr4hAc09fZn
bAniYIeKVqVBdKIvb5R445PFiUDFcfyneeX/resPXJHMEm/vAxfQeMqL
-----END PRIVATE KEY-----
Privatekey with 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-----
所以,我的第一个问题是关于私钥长度。它似乎更长。我的第二个问题是,我似乎没有正确地分割生成的密钥字节。线路肯定比预期的要多。如何纠正?
。。。私钥长度。。。似乎更长
它是,或者准确地说,表示/包含私钥的结构更长Java在算法特定数据的PKCS8-"参数"字段中包含可选和不必要(冗余(,该字段是SEC1附录C.4中定义的ECPrivateKey,而OpenSSL没有。在中读取时将忽略此项。两个结构中的实际键值大小正确。
我没有正确分割生成的密钥字节
相反,拆分对(密钥结构的(字节进行编码的(base64(字符。
在Splitter
之前查看Base64.encode
的输出。我敢打赌,你会发现它已经在每76个base64字符后包含换行符,这与MIME标准(RFC 1521 et seq(一致,有些人认为MIME标准比PEM更常见(或更重要?(,或者至少比PEM更新。(尽管XML和JWX甚至更新,现在也很常见,而且根本不插入换行符。(因此,您的Splitter
需要:
- 第一行的前64个字符
- 第一行剩下的12个字符,一个换行符,第二行剩下的51个字符
- 第二行剩下的25个字符,一个换行符,第三行最多38个字符
- 等等
尽管OpenSSL写入PEM文件时每64个字符(除了最后一行(使用换行符,但根据PEM标准(RFC 1421(,它始终能够读取4到76个字符的任意倍数的文件,这与MIME一致。自2016年1.1.0以来的最新版本,现在已经被广泛采用,可以读取多达数百个字符的行。因此,如果您的文件要由(任何使用(OpenSSL库读取,您可以只写split-at-76版本,而不做任何进一步的更改,除非确保有换行符终止最后一行。其他软件可能有所不同;如果您需要安全或严格遵守,请先从Base64.encode
输出中删除换行符,然后以64的正确间距将其添加回来。请参阅最近发布的重新规范。
PS:如果您使用Java将该密钥放入PKCS12密钥库(这需要您为其获取/创建证书(,openssl命令行可以直接读取该密钥,并将(1(私钥转换为PEM,(2(证书转换为PEM,您可以从中提取PEM中的公钥。