生成临时密码
会生成类似如下的字符串 6/4tsW7PR4bYjdY+zzZWEGIsuUz8RIwrNc8FTeQLoeouGO/C3RK/JeqNi8E6nR1l
1
| tr -dc 'A-Za-z0-9!?%=' < /dev/urandom | head -c 16; echo
|
这样生成出来的字符串更适合需要特殊字符的场景, 例如 SYqx!6J3=M8jrUeh
流式加解密
1 2 3 4 5 6 7 8 9 10
|
cat input.raw | openssl enc -aes-256-cbc -e -pbkdf2 -iter 100000 -salt > output.raw enter AES-256-CBC encryption password: Verifying - enter AES-256-CBC encryption password:
cat output.raw | openssl enc -aes-256-cbc -d -pbkdf2 -iter 100000 -salt > decrypted.raw enter AES-256-CBC decryption password:
|
分块传输(对网盘上传比较友好)
1 2 3 4 5 6 7 8 9 10
|
cat input.raw | openssl enc -aes-256-cbc -e -pbkdf2 -iter 100000 -salt | split -b 1G -d -a 3 - chunk_ enter AES-256-CBC encryption password: Verifying - enter AES-256-CBC encryption password:
cat chunk_* | openssl enc -aes-256-cbc -d -pbkdf2 -iter 100000 -salt > decrypted.raw enter AES-256-CBC decryption password:
|
CBC加密后分块可以配合tar命令使用, tar可以保证缺块的情况下仍然能够最大限度的提取出可用的文件.
split 命令, -d 为使用数字结尾(而不是aaa,aab这样), -a 2 表示补全到两位, 需要预估总输入大小确保不会出现 01, 02, ..., 99, 100 这样的情况. 否则 cat 的时候可能会错乱.
基于GPG的对称流式加解密
gpg能做非对称加解密已经是老生常谈了, 但是对称加密之前用到的不多
1 2 3 4 5 6 7 8 9 10 11
| passphrase=$(openssl rand -base64 128 | tr -d '\n') echo "Passphrase: $passphrase" exec {passfd}<<<"$passphrase" tar -cvf - input_dir | pv | gpg --symmetric --s2k-cipher-algo AES256 --s2k-digest-algo SHA512 --s2k-count 65536 --no-compress --batch --pinentry-mode loopback --passphrase-fd $passfd | split -b 1G -d -a 3 - chunk_ unset passphrase exec $passfd>&-
exec {passfd}<<<"$passphrase" cat chunk_* | gpg --decrypt --batch --pinentry-mode loopback --passphrase-fd $passfd | pv | tar -xvf -
|
GnuPG使用的是 aes-256-cfb 模式进行的加密(不能调整模式). exec {passfd}<<<"$passphrase" 是从字符串创建一个fd, 需要注意的是bash/zsh实际上会创建一个 /tmp/tmp.XXXXXX 的文件然后立刻删除. exec $passfd>&- 会关闭这个fd. 可以使用这个命令查看当前shell打开的fd: ls -l /proc/$$/fd
需要注意要加 --no-compress 选项. gpg默认是开启压缩的, 但是内部的压缩组件似乎有问题, 会导致莫名其妙的报错比如: gpg: Fatal: zlib inflate problem: invalid distance code. 在网上翻了一下没看到有什么特别好的解法, 大部分都是说数据本身出现了错乱才会报错. 在流式加密的场景里, 可以换成其他的压缩方式, 例如 tar ... | pv | zstd -19 -T0 | gpg ...
如果磁盘空间不能同时容纳 tar chunks 和提取出来的东西, 但是足够容纳部分 tar chunks 和全部提取出来的东西, 可以考虑FIFO:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| mkfifo $(mktemp -u | tee /dev/tty)
file /tmp/tmp.iblsCdwJJo
cat /tmp/tmp.iblsCdwJJo | gpg --decrypt --batch --pinentry-mode loopback --passphrase-fd $passfd | pv | tar -xvf -
exec {chunkfd}>/tmp/tmp.iblsCdwJJo cat chunk_000 >&3 cat chunk_001 >&3 cat chunk_002 >&3 ...
exec $chunkfd>&-
|
注意不能直接 cat chunk_000 > pipe 因为cat结束之后会关闭STDOUT, 进而导致读端收到EOF.
手动cat chunk这个过程可以使用一个简单的python脚本来半自动化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import os import time
print("Opening pipe...") pout = open("/tmp/tmp.iblsCdwJJo", "wb")
for i in range(0, 115): while True: fname = "chunk_{:03d}".format(i) if os.path.exists(fname.format(i)): print("chunk {} exists".format(i)) time.sleep(3) print("reading chunk {}...".format(i)) with open(fname, "rb") as f: content = f.read() print("{} bytes read".format(len(content))) pout.write(content) print("chunk {} written".format(i)) break else: print("chunk {} not exists, wait...".format(fname)) time.sleep(5)
pout.close()
|
参考资料
split(1) — Linux manual page
GnuPG - ArchWiki
GPG(1) manpage