在Python中从公钥派生SSH指纹
我想弄明白如何将一个OpenSSH的公钥,比如下面这个:
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAqmEmDTNBC6O8HGCdu0MZ9zLCivDsYSttrrmlq87/YsEBpvwUTiF3UEQuFLaq5Gm+dtgxJewg/UwsZrDFxzpQhCHB6VmqrbKN2hEIkk/HJvCnAmR1ehXv8n2BWw3Jlw7Z+VgWwXAH50f2HWYqTaE4qP4Dxc4RlElxgNmlDPGXw/dYBvChYBG/RvIiTz1L+pYzPD4JR54IMmTOwjcGIJl7nk1VjKvl3D8Wgp6qejv4MfZ7Htdc99SUKcKWAeHYsjPXosSk3GlwKiS/sZi51Yca394GE7T4hZu6HTaXeZoD8+IZ7AijYn89H7EPjuu0iCAa/cjVzBsFHGszQYG+U5KfIw==
然后把它转换成一个标准的指纹,像这样:
2048 49:d3:cb:f6:00:d2:93:43:a6:27:07:ca:12:fd:5d:98 id_rsa.pub (RSA)
我试着去研究OpenSSH的源代码来理解这个过程,但我觉得太复杂了。我的第一个想法是对这个公钥文本做一个简单的MD5哈希,但结果和上面的输出不匹配。
3 个回答
如果你有一个使用 cryptography
库的公钥对象,这里有一个解决方案,可以生成一个 SHA256 指纹(这是现在推荐的选择)。
为了找到正确的一组字节来生成指纹,以匹配 ssh-keygen -l
给出的指纹,我花了很多时间进行故障排除。你首先需要将公钥格式化为 OpenSSH 风格,然后从这个格式化的样式中解码出 base 64 编码的字节,最后对这些字节进行哈希处理——仅仅对原始字节进行哈希处理是行不通的。
import base64
from cryptography.hazmat.primitives import hashes, serialization
def get_public_key_fingerprint(public_key):
formatted = public_key.public_bytes(
encoding=serialization.Encoding.OpenSSH,
format=serialization.PublicFormat.OpenSSH,
)
parts = formatted.split(b" ")
key_bytes = base64.b64decode(parts[1])
digest = hashes.Hash(hashes.SHA256())
digest.update(key_bytes)
fingerprint = base64.b64encode(digest.finalize()).rstrip(b"=").decode("utf-8")
return f"SHA256:{fingerprint}"
这里有一个链接,你可以在这个地址找到相关的代码和信息:https://github.com/ojarva/sshpubkeys
如果你想安装这个工具,可以在你的命令行中输入:pip install sshpubkeys
使用方法如下:
import sshpubkeys
key = sshpubkeys.SSHKey("ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAqmEmDTNBC6O8H" +
"GCdu0MZ9zLCivDsYSttrrmlq87/YsEBpvwUTiF3UEQuFLaq5Gm+dtgxJewg/UwsZrDFxz" +
"pQhCHB6VmqrbKN2hEIkk/HJvCnAmR1ehXv8n2BWw3Jlw7Z+VgWwXAH50f2HWYqTaE4qP4" +
"Dxc4RlElxgNmlDPGXw/dYBvChYBG/RvIiTz1L+pYzPD4JR54IMmTOwjcGIJl7nk1VjKvl" +
"3D8Wgp6qejv4MfZ7Htdc99SUKcKWAeHYsjPXosSk3GlwKiS/sZi51Yca394GE7T4hZu6H" +
"TaXeZoD8+IZ7AijYn89H7EPjuu0iCAa/cjVzBsFHGszQYG+U5KfIw== user@host")
print(key.bits) # 2048
print(key.hash()) # '49:d3:cb:f6:00:d2:93:43:a6:27:07:ca:12:fd:5d:98'
这是经过Base64编码的密钥的MD5值:
import base64
import hashlib
def lineToFingerprint(line):
key = base64.b64decode(line.strip().split()[1].encode('ascii'))
fp_plain = hashlib.md5(key).hexdigest()
return ':'.join(a+b for a,b in zip(fp_plain[::2], fp_plain[1::2]))