每次我们通过ssh协议连接到服务器时,ssh客户端都会检查该服务器的公钥是否与上次的公钥相匹配(至少建议使用ssh标准)。 在OpenSSH中,已知服务器密钥的列表存储在known_hosts文件中。 在katom下方简要介绍了其中存储的内容以及存储的方式。
所有实验均在Linux(Debian / Mint / Ubuntu)上进行。 我不能保证其他操作系统中文件的位置和内容。首次连接ssh服务器时,我们会看到类似以下内容:
无法建立主机“ 192.168.0.2(192.168.0.2)”的真实性。
RSA密钥指纹为SHA256:kd9mRkEGLo + RBBNpxKp7mInocF3 / Yl / 0fXRsGJ2JfYg。
您确定要继续连接(是/否)吗?
如果您同意,则将以下行添加到〜/ .ssh / known_hosts文件中:
| 1 | CuXixZ + EWfgz40wpkMugPHPalyk = | KNoVhur7z5NAZmNndtwWq0kN1SQ = SSH-RSA AAAAB3NzaC1yc2EAAAADAQABAAABAQCeiF4OOOUhWvOYrh / e4q91 + IZ + i9S0s3M2LPq + GAhRlhKt5vKyEVd6x6m26cc98Y + SQXnCB9GWeVYk8jlFHEXnY4YWeWLDwXIhHBJYt5yz3j5Wkg95x + mPvO9FLSBk / Al2GbH5q6F + hZIlLmO6ciISmX4TtcG1sw4SwoTADrrhdM0OJd + c5CU8iqCbc6PznYbLZXCvqPZTWeSbTLUcUu1Ti + 7xGwT8DF + tIyLFcU + zxd0QnwJIbNvewkHs0LsMOWFVPz / Nd0XiVXimX + ugCDBZ / 4q8NUwH9SGzCMAvnnr + D1I8X2vhSuRsTsQXL5P3vf8elDxPdDrMJzNtlBCbLWzV
在这里,三个元素用空格写成:代表服务器的哈希,所用非对称算法的名称以及服务器的公钥。 让我们把它们分开。
如果您阅读了说明实际上,根据
Ubunt的
手册,可以有2个以上的字段,也用空格分隔:
- 在该行的开头,可能会有一个标记“ @ cert-authority”或“ @revoked”,分别表示在此行中写入了CA公共密钥,或者该密钥已被撤销并且无法使用。
- 该行的末尾可能有任意注释
服务器名称
在该示例中,代表服务器(主机)的哈希看起来像这样:
| 1 | CuXixZ + EWfgz40wpkMugPHPalyk = | KNoVhur7z5NAZmNndtwWq0kN1SQ =
实际上,可以在此处编写明文形式的主机名或指定有效名称集的掩码。 但是我的默认哈希名称已保存。 记录由符号“ |”分为3部分。 第一部分是哈希算法。 “ 1”对应于HMAC-SHA1(我没有看到其他人)。 第二部分是盐(HMAC的密钥)。 第三部分是哈希本身(HMAC输出)。
检查一下from base64 import b64decode import hmac salt = b64decode("CuXixZ+EWfgz40wpkMugPHPalyk=") host = b'192.168.0.2' hash = hmac.HMAC(salt, host, 'sha1').digest() print(b64encode(hash).decode())
>'KNoVhur7z5NAZmNndtwWq0kN1SQ ='
非对称算法
RFC-4253列出了4种非对称算法:ssh-dss(标准强制性,但自OpenSSH7.0起被认为是弱的并且默认情况下已关闭),ssh-rsa(推荐),pgp-sign-rsa(可选),pgp- sign-dss(可选)。 默认情况下,前两种类型的密钥是在Linux中为RFC中未提及的椭圆曲线算法生成的。 后者是首选,但是客户端可以使用HostKeyAlgorithms选项选择算法。
如何检查所需的(不是默认的)按键指纹例如,当您第一次进入服务器时要检查密钥的指纹,而您仅知道ssh-rsa密钥的指纹时,此功能很有用。 然后,您可以使用以下命令进行连接:
ssh root@192.168.0.2 -o HostKeyAlgorithms = ssh-rsa
如果还需要指定密钥哈希算法,则可以使用FingerprintHash选项。 例如,如果从ssh-rsa仅知道md5,则可以这样连接:
ssh root@192.168.0.2 -o HostKeyAlgorithms = ssh-rsa -o FingerprintHash = md5
公钥
known_hosts中的公钥与服务器上/etc/ssh/ssh_host_rsa_key.pub文件中记录的公钥相同(替换为所用算法的名称,而不是rsa)。 如果您删除了Base64编码,那么里面将再次是算法的名称和实际的关键组成部分。
为什么不删除Base64 b'\x00\x00\x00\x07ssh-rsa\x00\x00\x00\x03\x01\x00\x01\x00\x00\x01\x01\x00\x9e\x88^\x0e8\xe5!Z\xf3\x98\xae\x1f\xde\xe2\xafu\xfa,\xfe\x8b\xd4\xb4\xb3s6,\xfa\xbe\x18\x08Q\x96\x12\xad\xe6\xf2\xb2\x11Wz\xc7\xa9\xb6\xe9\xc7=\xf1\x8f\x92Ay\xc2\x07\xd1\x96yV$\xf29E\x1cE\xe7c\x86\x16yb\xc3\xc1r!\x1c\x12X\xb7\x9c\xb3\xde>V\x92\x0fy\xc7\xe9\x8f\xbc\xefE- d\xfc\tv\x19\xb1\xf9\xab\xa1~\x85\x92%.c\xbar"\x12\x99~\x13\xb5\xc1\xb5\xb3\x0e\x12\xc2\x84\xc0\x0e\xba\xe1t\xcd\x0e%\xdf\x9c\xe4%<\x8a\xa0\x9bs\xa3\xf3\x9d\x86\xcbep\xaf\xa8\xf6SY\xe4\x9bL\xb5\x1cR\xedS\x8b\xee\xf1\x1b\x04\xfc\x0c_\xad#"\xc5qO\xb3\xc5\xdd\x10\x9f\x02Hl\xdb\xde\xc2A\xec\xd0\xbb\x0c9aU??\xcdwE\xe2Ux\xa6_\xeb\xa0\x080Y\xff\x8a\xbc5L\x07\xf5!\xb3\x08\xc0/\x9ez\xfe\x0fR<_k\xe1J\xe4lN\xc4\x17/\x93\xf7\xbd\xff\x1e\x94<Ot:\xcc\'3m\x94\x10\x9b-l\xd5'
可以看出,有4个字节用于写入字段的长度,然后是字段本身,依此类推。 第一个字段是算法的名称,其余字段取决于特定的算法。 在上面的键中,3个字段:
b'ssh-rsa' - b'\x01\x00\x01' - b'\x00\x9e\x88^\x0e8\xe5!Z\xf3\x98\xae\x1f\xde\xe2\xafu\xfa,\xfe\x8b\xd4\xb4\xb3s6,\xfa\xbe\x18\x08Q\x96\x12\xad\xe6\xf2\xb2\x11Wz\xc7\xa9\xb6\xe9\xc7=\xf1\x8f\x92Ay\xc2\x07\xd1\x96yV$\xf29E\x1cE\xe7c\x86\x16yb\xc3\xc1r!\x1c\x12X\xb7\x9c\xb3\xde>V\x92\x0fy\xc7\xe9\x8f\xbc\xefE- d\xfc\tv\x19\xb1\xf9\xab\xa1~\x85\x92%.c\xbar"\x12\x99~\x13\xb5\xc1\xb5\xb3\x0e\x12\xc2\x84\xc0\x0e\xba\xe1t\xcd\x0e%\xdf\x9c\xe4%<\x8a\xa0\x9bs\xa3\xf3\x9d\x86\xcbep\xaf\xa8\xf6SY\xe4\x9bL\xb5\x1cR\xedS\x8b\xee\xf1\x1b\x04\xfc\x0c_\xad#"\xc5qO\xb3\xc5\xdd\x10\x9f\x02Hl\xdb\xde\xc2A\xec\xd0\xbb\x0c9aU??\xcdwE\xe2Ux\xa6_\xeb\xa0\x080Y\xff\x8a\xbc5L\x07\xf5!\xb3\x08\xc0/\x9ez\xfe\x0fR<_k\xe1J\xe4lN\xc4\x17/\x93\xf7\xbd\xff\x1e\x94<Ot:\xcc\'3m\x94\x10\x9b-l\xd5' - N (0x101 * 8 = 2048 )
指纹识别
提议在第一个连接上进行验证的密钥的指纹是来自最后一段的公共密钥和/etc/ssh/ssh_host_rsa_key.pub的对应散列(在示例中为SHA256),该散列以base64编码(用于SHA系列功能的哈希),或以十六进制表示(对于MD5)。
我们考虑 from hashlib import sha256 from base64 import b64decode, b64encode pub_key_bin = b64decode("AAAAB3NzaC1yc2EAAAADAQABAAABAQCeiF4OOOUhWvOYrh/e4q91+iz+i9S0s3M2LPq+GAhRlhKt5vKyEVd6x6m26cc98Y+SQXnCB9GWeVYk8jlFHEXnY4YWeWLDwXIhHBJYt5yz3j5Wkg95x+mPvO9FLSBk/Al2GbH5q6F+hZIlLmO6ciISmX4TtcG1sw4SwoTADrrhdM0OJd+c5CU8iqCbc6PznYbLZXCvqPZTWeSbTLUcUu1Ti+7xGwT8DF+tIyLFcU+zxd0QnwJIbNvewkHs0LsMOWFVPz/Nd0XiVXimX+ugCDBZ/4q8NUwH9SGzCMAvnnr+D1I8X2vhSuRsTsQXL5P3vf8elDxPdDrMJzNtlBCbLWzV") hash = sha256(pub_key_bin).digest() fingerprint = b64encode(hash) print(fingerprint) > b'kd9mRkEGLo+RBBNpxKp7mInocF3/Yl/0fXRsGJ2JfYg='
+ IZ + i9S0s3M2LPq + GAhRlhKt5vKyEVd6x6m26cc98Y + SQXnCB9GWeVYk8jlFHEXnY4YWeWLDwXIhHBJYt5yz3j5Wkg95x + mPvO9FLSBk / Al2GbH5q6F + hZIlLmO6ciISmX4TtcG1sw4SwoTADrrhdM0OJd + c5CU8iqCbc6PznYbLZXCvqPZTWeSbTLUcUu1Ti + 7xGwT8DF + tIyLFcU + zxd0QnwJIbNvewkHs0LsMOWFVPz / Nd0XiVXimX + ugCDBZ / 4q8NUwH9SGzCMAvnnr + D1I8X2vhSuRsTsQXL5P3vf8elDxPdDrMJzNtlBCbLWzV”) from hashlib import sha256 from base64 import b64decode, b64encode pub_key_bin = b64decode("AAAAB3NzaC1yc2EAAAADAQABAAABAQCeiF4OOOUhWvOYrh/e4q91+iz+i9S0s3M2LPq+GAhRlhKt5vKyEVd6x6m26cc98Y+SQXnCB9GWeVYk8jlFHEXnY4YWeWLDwXIhHBJYt5yz3j5Wkg95x+mPvO9FLSBk/Al2GbH5q6F+hZIlLmO6ciISmX4TtcG1sw4SwoTADrrhdM0OJd+c5CU8iqCbc6PznYbLZXCvqPZTWeSbTLUcUu1Ti+7xGwT8DF+tIyLFcU+zxd0QnwJIbNvewkHs0LsMOWFVPz/Nd0XiVXimX+ugCDBZ/4q8NUwH9SGzCMAvnnr+D1I8X2vhSuRsTsQXL5P3vf8elDxPdDrMJzNtlBCbLWzV") hash = sha256(pub_key_bin).digest() fingerprint = b64encode(hash) print(fingerprint) > b'kd9mRkEGLo+RBBNpxKp7mInocF3/Yl/0fXRsGJ2JfYg='
我们看到哈希值与第一个连接(在本文开头的引用)期间显示的指纹完全匹配,直到末尾的“ =”符号为止。
这是一个用于在known_hosts文件中查找主机
的小程序,该程序在实验期间出现。