OpenSSH 公钥文件格式?

OpenSSH 公钥文件格式?

我在解析 OpenSSH 公钥文件时遇到问题。我相信(但我不确定)RFC 4253 中详细说明了格式,安全外壳 (SSH) 传输层协议6.6 节,公钥算法。

对于 RSA 密钥,RFC 规定:

“ssh-rsa”密钥格式具有以下特定编码:

string    "ssh-rsa"
mpint     e
mpint     n

这里的“e”和“n”参数构成了签名密钥 blob。

问题就出在这里。该文档没有提供语法,也没有定义什么是stringmpint。这导致:

$ cat rsa.ssh.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDSNM6RVVmwN3y0NurIQnmZgjcx5K5zzZu9nDqopW4J
In/mr8OYZI3heSJShnIM8EThvwVGXXXyyJVRQAvRHYFO4DxS6bufSNWr3BxBGaGYlYxI9mgvQnT6+MzE
3oZyEMdQNPlV5VfbileXlrPoAk1TkGdVdhwdLJMI2B4KUyMf+Q== jwalton@test

进而:

$ echo 'AAAAB3NzaC1yc2EAAAADAQABAAAAgQDSNM6RVVmwN3y0NurIQnmZgjcx5K5zzZu9nDqopW4
JIn/mr8OYZI3heSJShnIM8EThvwVGXXXyyJVRQAvRHYFO4DxS6bufSNWr3BxBGaGYlYxI9mgvQnT6+M
zE3oZyEMdQNPlV5VfbileXlrPoAk1TkGdVdhwdLJMI2B4KUyMf+Q==' | base64 -d > rsa.bin

最后:

$ hexdump -C rsa.bin
00000000  00 00 00 07 73 73 68 2d  72 73 61 00 00 00 03 01  |....ssh-rsa.....|
00000010  00 01 00 00 00 81 00 d2  34 ce 91 55 59 b0 37 7c  |........4..UY.7||
00000020  b4 36 ea c8 42 79 99 82  37 31 e4 ae 73 cd 9b bd  |.6..By..71..s...|
00000030  9c 3a a8 a5 6e 09 22 7f  e6 af c3 98 64 8d e1 79  |.:..n.".....d..y|
00000040  22 52 86 72 0c f0 44 e1  bf 05 46 5d 75 f2 c8 95  |"R.r..D...F]u...|
00000050  51 40 0b d1 1d 81 4e e0  3c 52 e9 bb 9f 48 d5 ab  |[email protected].<R...H..|
00000060  dc 1c 41 19 a1 98 95 8c  48 f6 68 2f 42 74 fa f8  |..A.....H.h/Bt..|
00000070  cc c4 de 86 72 10 c7 50  34 f9 55 e5 57 db 8a 57  |....r..P4.U.W..W|
00000080  97 96 b3 e8 02 4d 53 90  67 55 76 1c 1d 2c 93 08  |.....MS.gUv..,..|
00000090  d8 1e 0a 53 23 1f f9                              |...S#..|
00000097

因此,公钥文件中似乎存在未记录的字段。RFC 似乎没有引用其他文档来定义这些字段。RFC 也没有记录私钥文件。我目前陷入了困境。

OpenSSH 在哪里定义其密钥文件中使用的字段?

答案1

因此,公钥文件中似乎存在未记录的字段。RFC 似乎没有引用其他文档来定义这些字段。

它们在 RFC 4251“安全外壳 (SSH) 协议架构”中定义,第 5 节

RFC 也未能记录私钥文件。

SSH 协议没有记录任何文件格式。它仅定义在公钥作为 SSH 协议的一部分发送时(例如,当客户端发送 SSH_MSG_USERAUTH 以提供其公钥时)的序列化。

因此,由于私钥永远不会作为 SSH 协议的一部分通过网络发送,因此其序列化也不需要成为规范的一部分——只有签名由该密钥制作的命令需要具有定义的格式。

OpenSSH 在哪里定义其密钥文件中使用的字段?

对于公钥,OpenSSH 很可能选择重新使用相同的 RFC 4253 格式将其存储在文件中,因为这是最方便的选择(即它已经有序列化代码)。规范并不要求这样做,事实上大多数其他客户端都有自己的格式。

由于 OpenSSH 使用 OpenSSL 来存储加密代码(算法、密钥生成),因此 OpenSSH 的早期版本仅使用 OpenSSL 函数提供的任何格式来存储私钥 - 大多数情况下是 DER 序列化的 PKCS#1“RSAPrivateKey”格式(通常也称为 PEM 格式)。请参阅RFC 3447用于 ASN.1 格式的定义。

(OpenSSL 本身现在倾向于以 PKCS#8 格式存储私钥,这意味着 OpenSSH 也可以加载这些密钥,尽管它不会写入它们。请参阅RFC 5208用于容器格式的 ASN.1 定义。

您可以通过“BEGIN RSA PRIVATE KEY”标头识别 PKCS#1 格式,通过“BEGIN PRIVATE KEY”标头识别 PKCS#8 格式。您可以使用dumpasn1openssl asn1parse来调查其内容,以及openssl rsaopenssl pkey

OpenSSH 的最新版本发明了一种新的、自定义的私钥文件格式。容器格式记录在协议密钥,并且各个密钥格式可能与ssh 代理,记录在draft-miller-ssh-代理。此格式使用 RFC 4251 数据类型。


其他 SSH 软件通常具有不同的格式。例如,PuTTY 使用“PPK”格式(我记得在某处有文档记录,但奇怪的是我找不到在哪里)来存储公钥和私钥。其Public-Lines字段存储相同的 RFC 4253 公钥,而私有字段是自定义的。

还有RFC 4716,声称是“SSH 公钥格式”,但它通常不被认为是 SSH 的组成部分。(SSH.COM、SecureCRT 和 MultiNet SSH 可能都使用此格式。)

答案2

我只是解释如何从二进制内容中获取(e,n):

    $ hexdump -C rsa.bin
    00000000  00 00 00 07 73 73 68 2d  72 73 61 00 00 00 03 01  |....ssh-rsa.....|
    00000010  00 01 00 00 00 81 00 d2  34 ce 91 55 59 b0 37 7c  |........4..UY.7||
    00000020  b4 36 ea c8 42 79 99 82  37 31 e4 ae 73 cd 9b bd  |.6..By..71..s...|
    00000030  9c 3a a8 a5 6e 09 22 7f  e6 af c3 98 64 8d e1 79  |.:..n.".....d..y|
    00000040  22 52 86 72 0c f0 44 e1  bf 05 46 5d 75 f2 c8 95  |"R.r..D...F]u...|
    00000050  51 40 0b d1 1d 81 4e e0  3c 52 e9 bb 9f 48 d5 ab  |[email protected].<R...H..|
    00000060  dc 1c 41 19 a1 98 95 8c  48 f6 68 2f 42 74 fa f8  |..A.....H.h/Bt..|
    00000070  cc c4 de 86 72 10 c7 50  34 f9 55 e5 57 db 8a 57  |....r..P4.U.W..W|
    00000080  97 96 b3 e8 02 4d 53 90  67 55 76 1c 1d 2c 93 08  |.....MS.gUv..,..|
    00000090  d8 1e 0a 53 23 1f f9                              |...S#..|
    00000097

“00 00 00 07”:字符串长度 = 7 个字节;

“73 73 68 2d 72 73 61”:字符串内容为“ssh-rsa”;

“00 00 00 03”:mpint长度=3个字节;

“01 00 01”:mpint值 = 0x010001 = 65537(十进制),这是公钥的“e”;

“00 00 00 81”:mpint 长度 = 0x81 = 129 字节;

“00 d2 34 ... 1f f9”:mpint值=0x00d234 ... 1ff9;这是公钥的“n”;

由于“n”的第一个字节是“00”,这是强制整数为正数的前导字节,因为(0xd2 & 0x80)不为零(0xd2 的最高有效位是“1”),所以“n”的实际位数是 128 字节,即“n”有(128 字节)*(8 位/字节)= 1024 位。

相关内容