如何使用 java BouncyCastle 创建带有密码的 OpenSSH 私钥

How to create OpenSSH private key with password using java BouncyCastle

提问人:Evgenii Sokolov 提问时间:10/17/2023 更新时间:10/18/2023 访问量:101

问:

使用非密码加密私钥创建 OpenSSH 密钥的简单方法:

    AsymmetricCipherKeyPairGenerator gen = new Ed25519KeyPairGenerator();
    gen.init(new KeyGenerationParameters(new SecureRandom(), 255));
    AsymmetricCipherKeyPair kp = gen.generateKeyPair();
    byte[] bprv = OpenSSHPrivateKeyUtil.encodePrivateKey(kp.getPrivate());
    StringWriter sw = new StringWriter();
    PemWriter w = new PemWriter(sw);
    w.writeObject(new PemObject("OPENSSH PRIVATE KEY", bprv)); w.close();
    String privateKey = sw.toString(); sw.close();
    System.out.println(privateKey);
    byte[] bpub = OpenSSHPublicKeyUtil.encodePublicKey(kp.getPublic());
    sw = new StringWriter();
    w = new PemWriter(sw);
    w.writeObject(new PemObject("SSH2 PUBLIC KEY", bpub)); w.close();
    String publicKey = sw.toString(); sw.close();

但是如何创建使用密码加密的私钥呢?可能吗?

java bouncycastle openssh

评论

0赞 Topaco 10/17/2023
我怀疑即使使用 BouncyCastle,这也无法开箱即用。您可能需要自己实现这一点,类似于非加密密钥代码(请参阅此处)。您是否仅限于 BouncyCastle,或者您也可以使用其他库(有些库可以生成加密的 OpenSSH 密钥)?
0赞 Evgenii Sokolov 10/18/2023
@Topaco 感谢您的回复。你给了我一些想法。我找到了一个足够详细地描述SSH密钥格式的资源。可能基于这些信息,我将能够编写自己的库。我不局限于BouncyCastle,只是我不知道其他任何事情。
0赞 Topaco 10/18/2023
如果你找不到 BC 的解决方案,或者它太复杂了,而且由于你不限于 BC,我将发布一个带有 maverick-synergy 库的解决方案,该库可用于相对容易地创建加密的 OpenSSH 密钥。

答:

1赞 Topaco 10/18/2023 #1

如果您找不到使用 BouncyCastle 导出加密的 OpenSSH 密钥的方法:允许导出加密的 OpenSSH 密钥的库是 sshtools/maverick-synergy

生成 Ed25519 密钥对并导出私钥加密密钥和公钥的示例:

import java.security.KeyPair;
import java.security.KeyPairGenerator;

import com.sshtools.common.publickey.SshKeyUtils;
import com.sshtools.common.ssh.components.SshKeyPair;
import com.sshtools.common.ssh.components.jce.JCEAlgorithms;
import com.sshtools.common.ssh.components.jce.SshEd25519PrivateKeyJCE;
import com.sshtools.common.ssh.components.jce.SshEd25519PublicKeyJCE;

...

// Create Ed25519 key pair
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(JCEAlgorithms.ED25519);
KeyPair kp = keyGen.generateKeyPair();
SshKeyPair pair = new SshKeyPair();
pair.setPrivateKey(new SshEd25519PrivateKeyJCE(kp.getPrivate()));
pair.setPublicKey(new SshEd25519PublicKeyJCE(kp.getPublic()));

// Export
System.out.println(SshKeyUtils.getFormattedKey(pair, "my passphrase"));
System.out.println(SshKeyUtils.getFormattedKey(pair.getPublicKey(), "my comment"));

替换为以下示例输出:

-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABDN
3FuF2G77g48ktctKG3NTAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIO+e
PM91Qbd6b6bF8sKfOJiox19K1llf8UwUbUayPmRHAAAAkD7rMYeeAKeyiwEU1uy2
iNsXTTWHR2jK2abcQ3Qndw1lQBmM+QfmdefnoN4HToSuSVzybaLBRoHl5vybFq74
5JaZ9ZQitXBrBLX91imjGmHDPr/nOYWo57Yuh05gPnx96MZHo61Ze6HNWhZ73xBT
6ve7YAxCDO8LjGjyC17k2/0HMcPOwbV8J39f5KTeXNf6vA==
-----END OPENSSH PRIVATE KEY-----

ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO+ePM91Qbd6b6bF8sKfOJiox19K1llf8UwUbUayPmRH my comment

OpenSSH私钥的格式在这里描述(更详细,特别是关于 加密逻辑,在这里)。如果加密的 OpenSSH 私钥是 Base64 解码的,则可以识别各个部分:

6f70656e7373682d6b65792d763100                              "openssh-key-v1" + 0x00
0000000a 6165733235362d637472                               length: 0x0a, "aes256-ctr"
00000006 626372797074                                       length: 0x06, "bcrypt"
00000018 00000010cddc5b85d86efb838f24b5cb4a1b735300000010   total length: 0x18, salt length: 0x10 + <salt, here 0xcddc5b85d86efb838f24b5cb4a1b7353> + <rounds, here 0x10>
00000001 
00000033 0000000b7373682d6564323535313900000020ef9e3ccf7541b77a6fa6c5f2c29f3898a8c75f4ad6595ff14c146d46b23e6447
00000090 3eeb31879e00a7b28b0114d6ecb688db174d35874768cad9a6dc437427770d6540198cf907e675e7e7a0de074e84ae495cf26da2c14681e5e6fc9b16aef8e49699f59422b5706b04b5fdd629a31a61c33ebfe73985a8e7b62e874e603e7c7de8c647a3ad597ba1cd5a167bdf1053eaf7bb600c420cef0b8c68f20b5ee4dbfd0731c3cec1b57c277f5fe4a4de5cd7fabc

第二行包含加密算法的说明,第三行包含所用密钥派生函数 (KDF) 的说明,第四行包含 KDF 选项的说明。
加密密钥的情况下,算法和 KDF 的行各0x000000046e6f6e65(即长度:0x04 + “none”),带有 KDF 选项的行为 0x00000000(即长度:0)。
对于生成的密钥,这些值是不同的,并表明密钥在 CTR 模式下使用 AES-256 加密,并且 bcrypt(更准确地说,是 bcrypt 的 OpenSSH 特定变体)作为 KDF 应用。