SHA-512 密码存储为非十六进制字符串

SHA-512 密码存储为非十六进制字符串

我觉得这太明显了,在互联网上搜索没有显示任何关于我的问题的结果。我正在查看 中的 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 或其他一些安全方法。

相关内容