将 openSSH rsa 密钥转换为 javax.crypto.Cipher 兼容格式
Posted
技术标签:
【中文标题】将 openSSH rsa 密钥转换为 javax.crypto.Cipher 兼容格式【英文标题】:convert openSSH rsa key to javax.crypto.Cipher compatible format 【发布时间】:2013-10-22 08:31:42 【问题描述】:有没有办法以编程方式将Jsch 生成的 SSH RSA 密钥转换为javax.crypto.Cipher 可用于加密的格式?我看到的答案大多类似于this:
openssl pkcs8 -topk8 -inform PEM -outform DER -in private_key_file -nocrypt > pkcs8_key
但我无法访问 openSSL 或 shell 命令。顺便说一句:我只使用JDK6。
感谢@erickson 的帮助,我可以使用指数和模数将公钥从RFC4716 转换为Java 公钥,因为BigInteger
类型通过RSAPublicKeySpec
传递给KeyFactory
。他的解决方案如下。
现在我正在尝试转换私钥。这是使用writePrivateKey(str filename)
生成的示例privateJsch.key
:
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQC0ouLgTjmKjHU6UjNSL8HyTIdFM1UdVpgU81paWKreN8L36YoT
goZQHeyyUCCHmq3r3cKaySyu93mHBY0l76qSAIRZgE1IAFkBhNWBdlJ9UYA9HXm/
MqTQHbpqz0EYGE9TsFHS8dn1/utsJxKSWZ4xPNYjfS4Ps6G84iRwfdrIbQIDAQAB
AoGAKv3xnY1AqLcRV5Yk3NS9Blwsfc3f3iG0BJh+0q3zzPvcjYCp+kbAjOTyZuYn
N98asd6P6KMk3WfNJtOtanAGWl46bmtzNsQtSr5rVQEgs2w8i2yJcwVAYf2Td4qX
m3dH+roJA/CEFRSDat4sUfjOVmsYQXIBa0W2XTpp+7T1U4ECQQD1wSR6iTz7Bja0
MPcizDbRTRQHALBf7E8j8YOLpN/IGSox9pT+ktjsI2vMaD+b3SM4s0FD8quBlppE
o5FAguHxAkEAvCrCK7eZU3H+Ul1iw9Kd3WPHjDvQcdT5rEL+NSYEZyHgU7ipXEih
UHvK47Bkte/PVIu3jBFBnMujA0XiT0gSPQJBAI3+8j/nChgU6AjHfhRaIJZgzeCZ
8k8KcFPZWWOXeUHZ4HqL+lz5pmMSuFecKJy7cn1xfZVwIs62oR5l0CiRN1ECQCui
CqaSi3ZjH6M/znA0PbEhuxsUn7BVv5OncUUnzKuRmnAviO5CVU3Rdum3dJMPydcE
Ewri0YEnY2SV5vWVc80CQH43uBbshz7ju3DdVykHFrRElQB+f0YMK3Ad7eu+us0w
dLrOOoXP0T60B/bMTo8rdMa6XU/0w/w8FsOqoxNY23U=
-----END RSA PRIVATE KEY-----
使用openssl asn1parse -in privateJsch.key -out privateJsch.der
yield:
$ openssl asn1parse -in privateJsch.key -out privateJsch.der
0:d=0 hl=4 l= 604 cons: SEQUENCE
4:d=1 hl=2 l= 1 prim: INTEGER :00
7:d=1 hl=3 l= 129 prim: INTEGER :B4A2E2E04E398A8C753A5233522FC1F24C874533551D569814F35A5A58AADE37C2F7E98A138286501DECB25020879AADEBDDC29AC92CAEF77987058D25EFAA92008459804D4800590184D58176527D51803D1D79BF32A4D01DBA6ACF4118184F53B051D2F1D9F5FEEB6C271292599E313CD6237D2E0FB3A1BCE224707DDAC86D
139:d=1 hl=2 l= 3 prim: INTEGER :010001
144:d=1 hl=3 l= 128 prim: INTEGER :2AFDF19D8D40A8B711579624DCD4BD065C2C7DCDDFDE21B404987ED2ADF3CCFBDC8D80A9FA46C08CE4F266E62737DF1AB1DE8FE8A324DD67CD26D3AD6A70065A5E3A6E6B7336C42D4ABE6B550120B36C3C8B6C8973054061FD93778A979B7747FABA0903F0841514836ADE2C51F8CE566B184172016B45B65D3A69FBB4F55381
275:d=1 hl=2 l= 65 prim: INTEGER :F5C1247A893CFB0636B430F722CC36D14D140700B05FEC4F23F1838BA4DFC8192A31F694FE92D8EC236BCC683F9BDD2338B34143F2AB81969A44A3914082E1F1
342:d=1 hl=2 l= 65 prim: INTEGER :BC2AC22BB7995371FE525D62C3D29DDD63C78C3BD071D4F9AC42FE3526046721E053B8A95C48A1507BCAE3B064B5EFCF548BB78C11419CCBA30345E24F48123D
409:d=1 hl=2 l= 65 prim: INTEGER :8DFEF23FE70A1814E808C77E145A209660CDE099F24F0A7053D95963977941D9E07A8BFA5CF9A66312B8579C289CBB727D717D957022CEB6A11E65D028913751
476:d=1 hl=2 l= 64 prim: INTEGER :2BA20AA6928B76631FA33FCE70343DB121BB1B149FB055BF93A7714527CCAB919A702F88EE42554DD176E9B774930FC9D704130AE2D18127636495E6F59573CD
542:d=1 hl=2 l= 64 prim: INTEGER :7E37B816EC873EE3BB70DD57290716B44495007E7F460C2B701DEDEBBEBACD3074BACE3A85CFD13EB407F6CC4E8F2B74C6BA5D4FF4C3FC3C16C3AAA31358DB75
base64 解码关键部分产生以下 hex 分解成 ASN.1 PKCS#1 组件(参见 RFC3447)对于此示例 仅,其他将遵循模式不同字节数:
30:82: x3082 == ASN.1 Sequence
02:5C: Key Length == 604 bytes
02:01: x02 == ASN.1 integer, Value Length == 1 byte
00: Version == 0
02:81:81: x02 == ASN.1 integer, Modulus Length == 129 bytes
00:B4:A2:E2:E0:4E:39:8A:8C:75:3A:52:33:52:2F:C1:
F2:4C:87:45:33:55:1D:56:98:14:F3:5A:5A:58:AA:DE:
37:C2:F7:E9:8A:13:82:86:50:1D:EC:B2:50:20:87:9A:
AD:EB:DD:C2:9A:C9:2C:AE:F7:79:87:05:8D:25:EF:AA:
92:00:84:59:80:4D:48:00:59:01:84:D5:81:76:52:7D:
51:80:3D:1D:79:BF:32:A4:D0:1D:BA:6A:CF:41:18:18:
4F:53:B0:51:D2:F1:D9:F5:FE:EB:6C:27:12:92:59:9E:
31:3C:D6:23:7D:2E:0F:B3:A1:BC:E2:24:70:7D:DA:C8:
6D: Modulus
02:03: x02 == ASN.1 integer, Value Length == 3 bytes
01:00:01: Public Exponent
02:81:80: x02 == ASN.1 integer, Value Length == 128 bytes
2A:FD:F1:9D:8D:40:A8:B7:11:57:96:24:DC:D4:BD:06:
5C:2C:7D:CD:DF:DE:21:B4:04:98:7E:D2:AD:F3:CC:FB:
DC:8D:80:A9:FA:46:C0:8C:E4:F2:66:E6:27:37:DF:1A:
B1:DE:8F:E8:A3:24:DD:67:CD:26:D3:AD:6A:70:06:5A:
5E:3A:6E:6B:73:36:C4:2D:4A:BE:6B:55:01:20:B3:6C:
3C:8B:6C:89:73:05:40:61:FD:93:77:8A:97:9B:77:47:
FA:BA:09:03:F0:84:15:14:83:6A:DE:2C:51:F8:CE:56:
6B:18:41:72:01:6B:45:B6:5D:3A:69:FB:B4:F5:53:81:
Private Exponent
02:41: x02 == ASN.1 integer, Value Length == 65 bytes
00:F5:C1:24:7A:89:3C:FB:06:36:B4:30:F7:22:CC:36:
D1:4D:14:07:00:B0:5F:EC:4F:23:F1:83:8B:A4:DF:C8:
19:2A:31:F6:94:FE:92:D8:EC:23:6B:CC:68:3F:9B:DD:
23:38:B3:41:43:F2:AB:81:96:9A:44:A3:91:40:82:E1:
F1: Prime P
02:41: x02 == ASN.1 integer, Value Length == 65 bytes
00:BC:2A:C2:2B:B7:99:53:71:FE:52:5D:62:C3:D2:9D:
DD:63:C7:8C:3B:D0:71:D4:F9:AC:42:FE:35:26:04:67:
21:E0:53:B8:A9:5C:48:A1:50:7B:CA:E3:B0:64:B5:EF:
CF:54:8B:B7:8C:11:41:9C:CB:A3:03:45:E2:4F:48:12:
3D: Prime Q
02:41: x02 == ASN.1 integer, Value Length == 65 bytes
00:8D:FE:F2:3F:E7:0A:18:14:E8:08:C7:7E:14:5A:20:
96:60:CD:E0:99:F2:4F:0A:70:53:D9:59:63:97:79:41:
D9:E0:7A:8B:FA:5C:F9:A6:63:12:B8:57:9C:28:9C:BB:
72:7D:71:7D:95:70:22:CE:B6:A1:1E:65:D0:28:91:37:
51: Prime P Exponent
02:40: x02 == ASN.1 integer, Value Length == 64 bytes
2B:A2:0A:A6:92:8B:76:63:1F:A3:3F:CE:70:34:3D:B1:
21:BB:1B:14:9F:B0:55:BF:93:A7:71:45:27:CC:AB:91:
9A:70:2F:88:EE:42:55:4D:D1:76:E9:B7:74:93:0F:C9:
D7:04:13:0A:E2:D1:81:27:63:64:95:E6:F5:95:73:CD:
Prime Q Exponent
02:40: x02 == ASN.1 integer, Value Length == 64 bytes
7E:37:B8:16:EC:87:3E:E3:BB:70:DD:57:29:07:16:B4:
44:95:00:7E:7F:46:0C:2B:70:1D:ED:EB:BE:BA:CD:30:
74:BA:CE:3A:85:CF:D1:3E:B4:07:F6:CC:4E:8F:2B:74:
C6:BA:5D:4F:F4:C3:FC:3C:16:C3:AA:A3:13:58:DB:75
CRT Coefficient
类似帖子:
how-to-convert-openssh-public-key-file-format-to-pem how-to-generate-ssh-compatible-id-rsa-pub-from-java how-to-load-rsa-private-key-from-file formatting-rsa-keys-for-openssl-in-java 还有更多,也在https://security.stackexchange.com参考:
bouncycastle/java asn1-key-structures-in-der-and-pem RFC3447 RFC4716 rsa_key_breakdown【问题讨论】:
你能用Bouncycastle library吗? 我可以,但我正在努力减少额外的库、包和 jar 文件。 Jsch 包含一个KeyPairRSA
类,其中包含 RSA 密钥的写入和解析。唯一的问题是它不包含很多吸气剂。否则,您可以简单地将底层字节数组字段转换为 positive BigInteger 值并使用简单的RSAPrivateKeySpec
或RSAPrivateCrtKeySpec
和"RSA"
KeyFactory
(抱歉,没有时间给出完整答案)
com.jcraft.jsch.KeyPairRSA 和 KeyPair
有 load
和 getPublicKeyBlob
两者都不兼容 javax.crypto.Cipher
作为“键”。所以我的问题的答案基本上是“不”,com.jcraft.jsch 似乎无论如何都没有提供将其对象转换为java.security.Key
对象,或X.509 用于公钥,或PKCS#8 用于私有键。
【参考方案1】:
static KeyPair demo(InputStream pub, InputStream pvt) throws IOException, GeneralSecurityException
KeyFactory f = KeyFactory.getInstance("RSA");
RSAPublicKeySpec pubspec = decodeRSAPublicSSH(readAllBase64Bytes(pub));
RSAPrivateCrtKeySpec pvtspec = decodeRSAPrivatePKCS1(readAllBase64Bytes(pvt));
return new KeyPair(f.generatePublic(pubspec), f.generatePrivate(pvtspec));
static RSAPublicKeySpec decodeOpenSSH(byte[] input)
String[] fields = new String(input, StandardCharsets.US_ASCII).split(" ");
if ((fields.length < 2) || (!fields[0].equals("ssh-rsa"))) throw new IllegalArgumentException("Unsupported type");
byte[] std = Base64.getDecoder().decode(fields[1]);
return decodeRSAPublicSSH(std);
static RSAPublicKeySpec decodeRSAPublicSSH(byte[] encoded)
ByteBuffer input = ByteBuffer.wrap(encoded);
String type = string(input);
if (!"ssh-rsa".equals(type)) throw new IllegalArgumentException("Unsupported type");
BigInteger exp = sshint(input);
BigInteger mod = sshint(input);
if (input.hasRemaining()) throw new IllegalArgumentException("Excess data");
return new RSAPublicKeySpec(mod, exp);
static RSAPrivateCrtKeySpec decodeRSAPrivatePKCS1(byte[] encoded)
ByteBuffer input = ByteBuffer.wrap(encoded);
if (der(input, 0x30) != input.remaining()) throw new IllegalArgumentException("Excess data");
if (!BigInteger.ZERO.equals(derint(input))) throw new IllegalArgumentException("Unsupported version");
BigInteger n = derint(input);
BigInteger e = derint(input);
BigInteger d = derint(input);
BigInteger p = derint(input);
BigInteger q = derint(input);
BigInteger ep = derint(input);
BigInteger eq = derint(input);
BigInteger c = derint(input);
return new RSAPrivateCrtKeySpec(n, e, d, p, q, ep, eq, c);
private static String string(ByteBuffer buf)
return new String(lenval(buf), Charset.forName("US-ASCII"));
private static BigInteger sshint(ByteBuffer buf)
return new BigInteger(+1, lenval(buf));
private static byte[] lenval(ByteBuffer buf)
byte[] copy = new byte[buf.getInt()];
buf.get(copy);
return copy;
private static BigInteger derint(ByteBuffer input)
int len = der(input, 0x02);
byte[] value = new byte[len];
input.get(value);
return new BigInteger(+1, value);
private static int der(ByteBuffer input, int exp)
int tag = input.get() & 0xFF;
if (tag != exp) throw new IllegalArgumentException("Unexpected tag");
int n = input.get() & 0xFF;
if (n < 128) return n;
n &= 0x7F;
if ((n < 1) || (n > 2)) throw new IllegalArgumentException("Invalid length");
int len = 0;
while (n-- > 0)
len <<= 8;
len |= input.get() & 0xFF;
return len;
private static byte[] readAllBase64Bytes(InputStream input) throws IOException
ByteArrayOutputStream output = new ByteArrayOutputStream();
BufferedReader r = new BufferedReader(new InputStreamReader(input, StandardCharsets.US_ASCII));
Decoder decoder = Base64.getDecoder();
while (true)
String line = r.readLine();
if (line == null) break;
if (line.startsWith("-----")) continue;
output.write(decoder.decode(line));
return output.toByteArray();
【讨论】:
这似乎有效,因为我得到了一个“1024 位 Sun RSA 公钥,其指数为 65537 和 1024 位长模数,但我没有根据私钥检查它。我有剥离页眉/页脚线和 cmets。(顺便说一句,我不能使用 jdk7,只能使用 jdk6,所以我不得不解决一些 jdk7 库,比如java.nio.Files
。)还剩下 13 个字节在模数之后,页脚之前。 它们不是文本,作为 base64 字符串,它们读取为“bRDQ0kh9j1ASyAihGA==”。你知道这些字节的用途吗?
@MarkMikofski 当我使用您在问题中提供的样本时,模数后没有多余的数据。您评论中的 base64 编码数据片段不会出现在您提供的示例中。如果没有完整的样本,我无法猜测问题出在哪里。
它有效,谢谢!我将它与使用openssl从SSH密钥生成的pem公钥进行了比较,十六进制与Java密钥相同!但一路走来,我犯了几个错误。 (1) 我在阅读时对每一行进行解码。我使用的是 jdk6,所以我使用了BufferedReader.readLine
,它确实省略了换行符,但是在编码之前必须先读取整个字符串。 (2) 出于某种原因,将每一行的字节存储在一个字节数组中,然后解码,也不起作用,我不得不使用 StringBuilder。 (3)笨蛋,我没有删除RFC4716的最后一行---- END SSH2 PUBLIC KEY ----
私钥与公钥格式不同,IE:不以<4-byte int> <key-type> <4-byte int> <exponent> <4-byte int> <modulus>
开头
@MarkMikofski 请用私钥和公钥更新我的答案。以上是关于将 openSSH rsa 密钥转换为 javax.crypto.Cipher 兼容格式的主要内容,如果未能解决你的问题,请参考以下文章
Node.js:如何将 RSA 公钥转换为 OpenSSH 格式?
Airflow SSH 操作员错误:遇到 RSA 密钥,应为 OPENSSH 密钥
如何在 Java 中将 OpenSSH 私钥转换为 RSA 私钥?
使用 PuTTY 进行身份验证时,“无法使用密钥文件“...\id_rsa”(OpenSSH SSH-2 私钥)”[关闭]