我想在 Bash 中创建多个源的哈希值。
我知道我可以:
echo -n "STRING" | sha256sum
或者
sha256sum [FILE]
我需要的是:
STRING + FILE
FILE + FILE
STRING + STRING
STRING + FILE + STRING
例如STRING + FILE
将 的哈希值保存
STRING
在变量中,并将 的哈希值保存[FILE]
在变量中。计算并创建总和的哈希值。将 的哈希值保存
STRING
在一个文件中,并将 的哈希值保存[FILE]
在同一个文件中,并创建该文件的哈希值。
我可以使用单个命令创建哈希吗?
例如:echo "STRING" + [FILE] | sha256sum
我怎样才能做到这一点,推荐或正确的方法是什么?
更新
根据罗密欧·尼诺夫的回答,实施例1:
echo -n "STRING" && cat [FILE] | sha256sum
当我做:
实施例2:
echo $(echo -n "STRING" | sha256sum) $(sha256sum [FILE]) | sha256sum
我应该用什么?我得到不同的结果。实现这一目标的正确方法是什么?
答案1
您可以创建一个像这样的脚本来散列多个文件,然后对它们的散列的串联进行散列。像这样的两部分散列而不是首先连接所有数据应该可以防止混淆,因为连接会丢失输入之间边界的信息(例如ab
+ c
!= a
+ bc
)。
#!/bin/bash
# function to get the hashes
H() {
sha256sum "$@" |
LC_ALL=C sed '
s/[[:blank:]].*//; # retain only the hash
s/^\\//; # remove a leading \ that GNU sha256sum at least
# inserts for file names where it escapes some
# characters (such as CR, LF or backslash).'
}
# workaround for command substitution removing final newlines
hashes=$(H "$@"; echo .)
hashes=${hashes%.}
# just for clarity
printf "%s\n" "----"
printf "%s" "$hashes"
printf "%s\n" "----"
# hash the hashes
final=$(printf "%s" "$hashes" | H)
echo "final hash of $# files: $final"
有两个文件的示例:
$ echo hello > hello.txt
$ echo world > world.txt
$ bash hash.sh hello.txt world.txt
----
5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03
e258d248fda94c63753607f7c4494ee0fcbe92f1a76bfdac795c9d84101eb317
----
final hash of 2 files: 27201be8016b0793d29d23cb0b1f3dd0c92783eaf5aa7174322c95ebe23f9fe8
您还可以使用进程替换来插入字符串,这应该给出相同的输出:
$ bash hash.sh hello.txt <(echo world)
[...]
final hash of 2 files: 27201be8016b0793d29d23cb0b1f3dd0c92783eaf5aa7174322c95ebe23f9fe8
给相同的输入数据 ( hello\nworld\n
) 不同的分隔会得到不同的哈希值:
$ bash hash.sh <(printf h) <(printf "ello\nworld\n")
[...]
final hash of 2 files: 0453f1e6ba45c89bf085b77f3ebb862a4dbfa5c91932eb077f9a554a2327eb8f
当然,改变输入文件的顺序也会改变哈希值。
输出中破折号之间的部分只是为了清楚起见,它显示了进入最终sha256sum
.您可能应该将其删除以供实际使用。
上面,我曾经sed
从sha256sum
.如果删除该| sed ...
部分,文件名将被包含在内,例如hash.sh hello.txt world.txt
将散列字符串
5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03 hello.txt
e258d248fda94c63753607f7c4494ee0fcbe92f1a76bfdac795c9d84101eb317 world.txt
子哈希是相同的,但最终哈希的输入不同,给出的f27b5175dec88c76dc6a7b368167cd18875da266216506e10c503a56befd7e14
结果是不同的。显然,更改文件名(包括从 到hello.txt
)./hello.txt
会更改哈希值。此外,使用进程替换在这里不太有用,因为它们会显示奇怪的依赖于实现的文件名(就像/dev/fd/63
Linux 上的 Bash)。
在上面,最终哈希的输入是十六进制编码输入元素的哈希值,每个元素都以换行符结尾。我不思考您需要比这更多的分离,并且从技术上讲甚至可以删除换行符,因为无论如何散列都有固定的长度(但我们免费获得换行符,它们使人类更容易阅读)。
但请注意,它只sha256sum
给出简单的哈希值。如果您正在寻找生成身份验证标签的工具,您可能应该研究 HMAC 等,并警惕长度扩展攻击(直截了当的攻击H(key + data)
可能容易受到攻击)等。
答案2
在收到所有信息和评论后,我认为一种可能的解决方案如下:
- 分别对每个源进行哈希处理。
- 避免连接源,除非它们事先已单独散列。
- 对源进行哈希处理时,请考虑使用分隔符或盐。
- 对于进一步的处理和存储,例如在带有区块的分类账中,最好的方法是使用哈希树(Merkle 哈希树),类似于当前大多数私有和公共区块链的操作方式。
例子:
相同的哈希结果:
HASH_OF((abc) + (def))
HASH_OF((ab) + (cdef))
HASH_OF((abcde) + (f))
不同的哈希结果:
HASH_OF( (HASH_OF(abc)) + (HASH_OF(def)) )
HASH_OF( (HASH_OF(ab)) + (HASH_OF(cdef)) )
HASH_OF( (HASH_OF(abcde)) + (HASH_OF(f)) )
我目前的方法,结合分隔符/盐,如下:
HASH_OF( (HASH_OF(abcde + [delimters/salt])) + (HASH_OF(f + [delimters/salt])) )
我将继续并扩展这个示例以满足我的特定要求。
如果用脚本来实现会更方便、更清晰。
echo $(echo -n "STRING1" | sha256sum)$(echo -n "STRING2" | sha256sum) | sha256sum