我觉得这太明显了,在互联网上搜索没有显示任何关于我的问题的结果。我正在查看 中的 root 密码/etc/shadow
,它看起来像:
$6$Etg2ExUZ$F9NTP7omafhKIlqaBMqng1.....
密码是第二个$
符号后面的内容。它使用 sha512 哈希,但不显示为十六进制。我的问题是通常为十六进制的 sha512 哈希的输出如何转换为以下字符串:
F9NTP7omafhKIlqaBMqng1....
带有非十六进制字符?
答案1
sha512(或任何散列)的输出是一个位序列——在本例中为 512 个位——编码一个(非常大的)数字,并且可以分成字节或任何其他所需的划分。
十六进制表示将每个四位块转换为十六进制数字,是向用户表示哈希值的常用方式,但不是哈希函数本身所固有的。它也可以是原始字节、十进制或一长串 0 和 1。重要的是作者和读者就数字的编码方式达成一致。
在这种情况下,原始字节将不起作用,因为shadow
文件使用换行符来分隔记录,因此该字节不可用,并且如果文件中包含其他奇怪字符,则人们将很难阅读或复制。这就是为什么 ASCII 编码通常用于您可能看到的哈希值。
然而,ASCII 表示的十六进制、十进制和二进制都是相当低效的机制:十六进制使原始字节的大小加倍(4 位输入到一个 ASCII 输出字节)。对于较短的哈希值来说,这不是一个问题:MD5 只有 128 位、16 个字节,即 32 个十六进制数字,这是可以管理的,但对于较长的哈希值,它会很快变得难以处理。不过,对于像这样的 512 位哈希值,仅哈希值就需要 128 个字节。十进制或二进制会更糟,尽管可能不是极大地在这种情况下,只要每个人都同意,这些时间有多长很重要。
对于这个具体案例,man 3 crypt
说:
“salt”和“encrypted”中的字符是从集合中提取的
[a-zA-Z0-9./]
。
a-z
(26)、A-Z
(26)、0-9
(10)、.
(1)、/
(1)总共有26+26+10+1+1=64个可用字符,所以以 64 为基数表示听起来好像正在使用。这意味着每个 ASCII 字节代表数据的 6 位 (2^6 = 64):base64 的四个字节(32 位)保存原始数据的三个字节(24 位),因此它仅在起始位置扩展了 33%。在此编码中,512 位值需要 86 个字节来存储。
当以下情况时,Base64 是一个很好的默认值:a) 您需要在 ASCII 内存储或传输任意二进制数据,b) 没有人需要大声读出它。这两点都在这里,所以这是一个明智的选择。当您可能必须手动读取或检查哈希值时,十六进制表示很方便,因为大小写并不重要,并且没有那么多不同的值。还有一种很少使用但标准的,Base32编码位于中间(全部大写和数字),但没有太多理由在这里使用它。
你可能有一个base64
工具安装后,它将在两个方向上为您进行这些转换。它可能在末尾使用不同的字节crypt
- 例如,MIME base64 编码使用+
and/
而不是.
and /
- 但您可以看到它如何将任意输入转换为稍长的 ASCII 编码输出。还有在线编码和解码工具,但对于密码哈希,您可能会得到无法打印的字节和无效的字节序列,因此它可能没有多大帮助。
答案2
哈希的输出是一系列字节而不是字母。由于某些字节值对大多数文本程序具有特殊含义,因此这些字节通常以某种方式进行编码。常见的哈希编码是十六进制,每个半字节一个字母(半个字节)。其他也是可能的(而且也很常见)。影子文件中使用的类似于基数 64而是使用一个转换字符串,./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
将散列输出中的每 6 位转换为该列表中的一个字母。
512 位哈希(64 字节)将在类 Base64 编码中使用 86 个字母(固定长度),而不是使用较长的 128 个字母进行十六进制编码。
有一个直接的 C 函数可以执行 crypt 编码。您不需要对其进行解码,只需将以下程序的输出与内部的实际值进行比较即可/etc/shadow
。
文件:passwd-sha512.c
#define _XOPEN_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
if ( argc < 3 || (int) strlen(argv[2]) > 16 ) {
printf("usage: %s password salt\n", argv[0]);
printf("--salt must not larger than 16 characters\n");
return;
}
char salt[21];
sprintf(salt, "$6$%s$", argv[2]);
printf("%s\n", crypt((char*) argv[1], (char*) salt));
return;
}
编译:
$ /usr/bin/gcc -lcrypt -o passwd-sha512 passwd-sha512.c
用法:
$ passwd-sha512 <password> <salt (16 chars max)>
从您的示例字符串$6$Etg2ExUZ$F9NTP7omafhKIlqaBMqng1.....
:
$ ./passwd-sha512 password Etg2ExUZ
$6$Etg2ExUZ$k01JYPOzptT0enZUP........
请记住,命令行中的密码可能会被其他用户读取,请调整 C 代码以使用 stdin 或其他一些安全方法。