这里https://security.stackexchange.com/a/52564您可以看到较新的 OpenSSH 版本使用 bcrypt 来保护密钥文件。bcrypt 的安全性取决于成本因素,请参阅https://security.stackexchange.com/questions/139721/estimate-the-time-to-crack-passwords-using-bcrypt/201965#201965
根据https://crypto.stackexchange.com/questions/58536/how-does-openssh-use-bcrypt-to-set-ivs/58543#58543默认的 bcrypt 轮数为 16。这将是一种很好的安全性。但是如何获取轮数/成本因子?
我目前所做的工作:密钥看起来像(为了使其更短,这里只有一个弱的 1024 位密钥)
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABBLF8sO2Q
hcLXI43z96e1hiAAAAEAAAAAEAAACXAAAAB3NzaC1yc2EAAAADAQABAAAAgQC0gBWeZpej
9ILT/59bEb0/lSvXx0WfZqP2lXRDbuY+gluuWyT+REQcVTR2BxSx9F/P20mLTnupzY+XE3
xEu+SIJlwKIAH3fed62+QBzDrPsl9kyfoIGIvi/28ZftqVN/kg0GSOaAqu4Px+vNVX1VKn
PNV5VVCZWL4ZPlGQZ48UJwAAAhCwDkueKT9oq8E0qtD92/4DSAD2eTI7bd6jBGUxugEw85
6xWbRYnFQZdwO2ZCNV0aTHViD1FRKlC9cBHDoSORKcM/9dY9Msy6lZj7Tp5s8r7x2pOrJi
TVRbv5/cI732I+l/vYvssJEhZpeSw4JKh9tyPpifVmzBxqtqwkBrTuLCMqkwLmrcxReFUq
aA/RIZy3L616CJsAvx2ezEc49D6SbJ9i9OlKuv73a1baS4RpMvFzWGLE2NBvvtQpEnJFoL
Kyjz+two4doT6SZ7UtiVGyCtO5WQEoeAgjhkbZzOPtM2AvoV+hNLRIX2/52jOB5A1bNQ0v
qW64aj2YNe8vWfj5xtA/8BlyEG7gwhu+0HgbgMDxw7o/0qVkHM/Hv3YgBTRygsH+8h4wsR
kxA292NOKKaD18tv1j3atR80q0XQVcQH20uX8tSqXtKfDtkUc/EPbFCNp3xQJG/F81USKh
YAmjxEeDkZZ/LkEOEJKvFRCL3gFlH4rqF5/pRk6HmB99xceD4irbazm+BWfPAf5Q0zdB5L
/yei3sqA4G48yRXIkaELtYNEeTYHMp3PGz1b3CP3l+ZGZp6XNaM+sMfdICbI3Zae5bnxKg
VXEE2UMdi7DEXbqEzSlfcIf5QzXHMQJm0ZL+iLoaEmakamAxCKk6jJ+QzHzGADZEIRXrj3
5Nhhd0jsToEMsXmmawt2qxy0cIHET1M=
-----END OPENSSH PRIVATE KEY-----
PW 是test
然后让我们解码 base64。因此必须删除以“-----”开头的第一行和最后一行
cat key | tail -n +2 | head -n -1 | base64 -d > text.txt
现在在 Notepad++ 中打开 text.txt,结果显示
但现在我不知道如何从那里读取回合数。你能帮忙吗?
答案1
取出 base64,将其解码为十六进制:
6f 70 65 6e 73 73 68 2d 6b 65 79 2d 76 31 00 00 00 00 0a 61 65 73 32 35 36 2d 63 74 72 00 00 00 06
62 63 72 79 70 74 00 00 00 18 00 00 00 10 4b 17 cb 0e d9 08 5c 2d 72 38 df 3f 7a 7b 58 62 00 00 00
10 00 00 00 01 00 00 00 97 00 00 00 07 73 73 68 2d 72 73 61 00 00 00 03 01 00 01 00 00 00 81 00 b4
80 15 9e 66 97 a3 f4 82 d3 ff 9f 5b 11 bd 3f 95 2b d7 c7 45 9f 66 a3 f6 95 74 43 6e e6 3e 82 5b ae
5b 24 fe 44 44 1c 55 34 76 07 14 b1 f4 5f cf db 49 8b 4e 7b a9 cd 8f 97 13 7c 44 bb e4 88 26 5c 0a
20 01 f7 7d e7 7a db e4 01 cc 3a cf b2 5f 64 c9 fa 08 18 8b e2 ff 6f 19 7e da 95 37 f9 20 d0 64 8e
68 0a ae e0 fc 7e bc d5 57 d5 52 a7 3c d5 79 55 50 99 58 be 19 3e 51 90 67 8f 14 27 00 00 02 10 b0
0e 4b 9e 29 3f 68 ab c1 34 aa d0 fd db fe 03 48 00 f6 79 32 3b 6d de a3 04 65 31 ba 01 30 f3 9e b1
59 b4 58 9c 54 19 77 03 b6 64 23 55 d1 a4 c7 56 20 f5 15 12 a5 0b d7 01 1c 3a 12 39 12 9c 33 ff 5d
63 d3 2c cb a9 59 8f b4 e9 e6 cf 2b ef 1d a9 3a b2 62 4d 54 5b bf 9f dc 23 bd f6 23 e9 7f bd 8b ec
b0 91 21 66 97 92 c3 82 4a 87 db 72 3e 98 9f 56 6c c1 c6 ab 6a c2 40 6b 4e e2 c2 32 a9 30 2e 6a dc
c5 17 85 52 a6 80 fd 12 19 cb 72 fa d7 a0 89 b0 0b f1 d9 ec c4 73 8f 43 e9 26 c9 f6 2f 4e 94 ab af
ef 76 b5 6d a4 b8 46 93 2f 17 35 86 2c 4d 8d 06 fb ed 42 91 27 24 5a 0b 2b 28 f3 fa dc 28 e1 da 13
e9 26 7b 52 d8 95 1b 20 ad 3b 95 90 12 87 80 82 38 64 6d 9c ce 3e d3 36 02 fa 15 fa 13 4b 44 85 f6
ff 9d a3 38 1e 40 d5 b3 50 d2 fa 96 eb 86 a3 d9 83 5e f2 f5 9f 8f 9c 6d 03 ff 01 97 21 06 ee 0c 21
bb ed 07 81 b8 0c 0f 1c 3b a3 fd 2a 56 41 cc fc 7b f7 62 00 53 47 28 2c 1f ef 21 e3 0b 11 93 10 36
f7 63 4e 28 a6 83 d7 cb 6f d6 3d da b5 1f 34 ab 45 d0 55 c4 07 db 4b 97 f2 d4 aa 5e d2 9f 0e d9 14
73 f1 0f 6c 50 8d a7 7c 50 24 6f c5 f3 55 12 2a 16 00 9a 3c 44 78 39 19 67 f2 e4 10 e1 09 2a f1 51
08 bd e0 16 51 f8 ae a1 79 fe 94 64 e8 79 81 f7 dc 5c 78 3e 22 ad b6 b3 9b e0 56 7c f0 1f e5 0d 33
74 1e 4b ff 27 a2 de ca 80 e0 6e 3c c9 15 c8 91 a1 0b b5 83 44 79 36 07 32 9d cf 1b 3d 5b dc 23 f7
97 e6 46 66 9e 97 35 a3 3e b0 c7 dd 20 26 c8 dd 96 9e e5 b9 f1 2a 05 57 10 4d 94 31 d8 bb 0c 45 db
a8 4c d2 95 f7 08 7f 94 33 5c 73 10 26 6d 19 2f e8 8b a1 a1 26 6a 46 a6 03 10 8a 93 a8 c9 f9 0c c7
cc 60 03 64 42 11 5e b8 f7 e4 d8 61 77 48 ec 4e 81 0c b1 79 a6 6b 0b 76 ab 1c b4 70 81 c4 4f 53
这规格定义此数据的格式。然后我们可以将数据分开:
byte[] AUTH_MAGIC = "openssh-key-v1\n"
string ciphername
string kdfname
string kdfoptions
int number of keys N
string publickey1
string publickey2
...
string publickeyN
string encrypted, padded list of private keys
6f 70 65 6e 73 73 68 2d 6b 65 79 2d 76 31 00 AUTH_MAGIC "openssh-key-v1\n"
00 00 00 0a CipherName string length. 0x0000000a = 10 characters
61 65 73 32 35 36 2d 63 74 72 CipherName "aes256-ctr"
00 00 00 06 KdfName length prefix 0x00000006 = 6 characters
62 63 72 79 70 74 KdfName "bcrypt"
00 00 00 18 KdfOptions string length prefix. 0x00000018 = 24 characters
00 00 00 10 4b 17 cb 0e d9 08 5c 2d
72 38 df 3f 7a 7b 58 62 00 00 00 10
00 00 00 01 Number of keys. 0x00000001 = 1;
00 00 00 97 publicKey1 string length 0x00000097
00 00 00 07 73 73 68 2d 72 73 61 00 00 00 03 01 publicKey1
00 01 00 00 00 81 00 b4 80 15 9e 66 97 a3 f4 82
d3 ff 9f 5b 11 bd 3f 95 2b d7 c7 45 9f 66 a3 f6
95 74 43 6e e6 3e 82 5b ae 5b 24 fe 44 44 1c 55
34 76 07 14 b1 f4 5f cf db 49 8b 4e 7b a9 cd 8f
97 13 7c 44 bb e4 88 26 5c 0a 20 01 f7 7d e7 7a
db e4 01 cc 3a cf b2 5f 64 c9 fa 08 18 8b e2 ff
6f 19 7e da 95 37 f9 20 d0 64 8e 68 0a ae e0 fc
7e bc d5 57 d5 52 a7 3c d5 79 55 50 99 58 be 19
3e 51 90 67 8f 14 27
00 00 02 10 publicKey2 string length 0x00000210 = frazillion characters
b0 0e 4b 9e 29 3f 68 ab c1 34 aa d0 fd db fe 03
48 00 f6 79 32 3b 6d de a3 04 65 31 ba 01 30 f3
9e b1 59 b4 58 9c 54 19 77 03 b6 64 23 55 d1 a4
c7 56 20 f5 15 12 a5 0b d7 01 1c 3a 12 39 12 9c
33 ff 5d 63 d3 2c cb a9 59 8f b4 e9 e6 cf 2b ef
1d a9 3a b2 62 4d 54 5b bf 9f dc 23 bd f6 23 e9
7f bd 8b ec b0 91 21 66 97 92 c3 82 4a 87 db 72
3e 98 9f 56 6c c1 c6 ab 6a c2 40 6b 4e e2 c2 32
a9 30 2e 6a dc c5 17 85 52 a6 80 fd 12 19 cb 72
fa d7 a0 89 b0 0b f1 d9 ec c4 73 8f 43 e9 26 c9
f6 2f 4e 94 ab af ef 76 b5 6d a4 b8 46 93 2f 17
35 86 2c 4d 8d 06 fb ed 42 91 27 24 5a 0b 2b 28
f3 fa dc 28 e1 da 13 e9 26 7b 52 d8 95 1b 20 ad
3b 95 90 12 87 80 82 38 64 6d 9c ce 3e d3 36 02
fa 15 fa 13 4b 44 85 f6 ff 9d a3 38 1e 40 d5 b3
50 d2 fa 96 eb 86 a3 d9 83 5e f2 f5 9f 8f 9c 6d
03 ff 01 97 21 06 ee 0c 21 bb ed 07 81 b8 0c 0f
1c 3b a3 fd 2a 56 41 cc fc 7b f7 62 00 53 47 28
2c 1f ef 21 e3 0b 11 93 10 36 f7 63 4e 28 a6 83
d7 cb 6f d6 3d da b5 1f 34 ab 45 d0 55 c4 07 db
4b 97 f2 d4 aa 5e d2 9f 0e d9 14 73 f1 0f 6c 50
8d a7 7c 50 24 6f c5 f3 55 12 2a 16 00 9a 3c 44
78 39 19 67 f2 e4 10 e1 09 2a f1 51 08 bd e0 16
51 f8 ae a1 79 fe 94 64 e8 79 81 f7 dc 5c 78 3e
22 ad b6 b3 9b e0 56 7c f0 1f e5 0d 33 74 1e 4b
ff 27 a2 de ca 80 e0 6e 3c c9 15 c8 91 a1 0b b5
83 44 79 36 07 32 9d cf 1b 3d 5b dc 23 f7 97 e6
46 66 9e 97 35 a3 3e b0 c7 dd 20 26 c8 dd 96 9e
e5 b9 f1 2a 05 57 10 4d 94 31 d8 bb 0c 45 db a8
4c d2 95 f7 08 7f 94 33 5c 73 10 26 6d 19 2f e8
8b a1 a1 26 6a 46 a6 03 10 8a 93 a8 c9 f9 0c c7
cc 60 03 64 42 11 5e b8 f7 e4 d8 61 77 48 ec 4e
81 0c b1 79 a6 6b 0b 76 ab 1c b4 70 81 c4 4f 53
KDF 选项字符串
你想要的部分是字符串kdf选项:
00 00 00 10 4b 17 cb 0e d9 08 5c 2d 72 38 df 3f 7a 7b 58 62 00 00 00 10
其中规格解释:
string salt
uint32 rounds
换句话说:
00 00 00 10 Salt string length 0x00000010 = 16 characters
4b 17 cb 0e d9 08 5c 2d 72 38 df 3f 7a 7b 58 62 Salt
00 00 00 10 Number of rounds. 0x00000010 = 16
我怀疑他们的意思“回合”。我想他们的意思是成本因素。
- 成本系数 16 ⇒ 发数 = 2 16 = 65,535 发
- 成本系数 4 ⇒ 发数 = 2 4 = 16 发
但它就在那里。
Bcrypt 不是 KDF
BCrypt 不是密钥派生函数;它是一个密码存储函数。您不能使用 bcrypt 生成“钥匙”。例如,如果你想“派生”AES-256 位密钥:bcrypt 无法做到这一点。
这是因为 bcrypt 不是密钥派生函数。BCrypt
是密码哈希函数。
以这种方式滥用 bcrypt 是令人憎恶的行为,也是反人类罪行。
有关 bcrypt 作为密码哈希函数而不是密钥派生函数的更多信息,请参见此处https://crypto.stackexchange.com/a/70783
答案2
一些附加信息。总之,新 ssh-keys 的密码保护非常安全。
创建密钥或更改密码时,您可以使用-a <number of rounds>
。
time ssh-keygen -f 2.key -p -a 500 -P b -N b #needs 10 seconds
time ssh-keygen -f 2.key -p -a 1000 -P b -N b #needs 15 seconds
time ssh-keygen -f 2.key -p -a 1000 -P b -N b #needs 20 seconds
time ssh-keygen -f 2.key -p -a 1000 -P b -N b #needs 20 seconds, 10 to encrypt, 10 to decrypt
这是在旧款 i3-3220 上完成的,在 Ryzen 7 5700U 上需要的时间约为其一半。因此轮次确实是轮次,而不是成本因素。
要查明您的密钥是否是新格式,请使用cat key | tail -n +2 | head -n -1 | base64 -d | head -n 2
如果您看到aes256-ctrbcrypt
则您的密钥是新格式。
为了评估它对暴力破解密码的安全性,我使用了 John the ripper。Hashcat 目前(2022 年 3 月)不支持这种格式,请参阅https://hashcat.net/forum/thread-10662.html John the ripper 的预构建二进制文件可能也不包含破解这种新型 ssh 密钥格式所需的模块。
编译完 John the ripper 后,进入run
目录并通过以下方式从 SSH 密钥文件创建哈希python3 ssh2john.py <keyfile> > hash.txt
现在通过 启动 John the ripper ./john hash.txt
。程序将使用已发送的密码列表。在 AMD Ryzen 7 5700U 上,它可以尝试大约 132 PW/s (c/s),这是一个非常低的数字。这仅适用于 CPU。当通过 更改轮次为 32 时,-a 32
数字会减半(65 PW/s),正如预期的那样。
为了估算 GPU 的可能性,我还使用了 MD5 加密哈希,有了它,John the Ripper 每秒可以尝试大约 712,000 个密码,速度提高了大约 5400 倍。
与 GTX1080 相比,GTX1080 每秒能够使用 MD5Crypt 处理约 1000 万个密码https://gist.github.com/epixoip/a83d38f412b4737e99bbef804a270c40 因此,如果这是线性的,GTX 1080 比 Ryzen 7 5700U 快 14 倍,所以只能达到大约 1850 PW/s。
所以目前的sshkey加密对于离线攻击来说是相当安全的。
最后,一个小的 Python 脚本会接收filename="test.key"
你的 ssh 密钥,如果它是 bcrypt 格式,它会显示盐和成本因素。
#!/usr/bin/python3
import binascii
import base64
import re
filename="2.key"
with open(filename, 'r') as f:
content = f.read()
content = content.split("\n")
content=content[1:-1]
content="".join(content)
#print(content)
decoded=base64.b64decode(content)
#print(decoded)
hex=binascii.hexlify(decoded)
#print(type(hex))
#print(hex)
bycryptPattern="626372797074"
begin=str(hex).find(bycryptPattern)
if begin == -1:
print("No encryption based on bcrypt. Exiting...")
exit(1)
begin+=len(bycryptPattern)
content=str(hex)[begin:]
kdfOptionsLength=content[:8]
content=content[8:]
kdfOptionsLength=int(kdfOptionsLength,16)
kdfPart=content[:kdfOptionsLength*2]
saltLen=int(kdfPart[:8],16)
kdfPart=kdfPart[8:]
salt=re.findall("..",kdfPart[:saltLen*2])
salt=" ".join(salt)
print("Salt:", salt)
kdfPart=kdfPart[saltLen*2:]
rounds=int(kdfPart,16)
print(f"encryption rounds: {rounds} ")