加密SSH私钥的密码安全:如何读取bcrypt的整数或costfactor

加密SSH私钥的密码安全:如何读取bcrypt的整数或costfactor

这里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} ")

相关内容