文章

一句话和6位PIN转换成强密码

一句话和6位PIN转换成强密码
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import argparse
import hashlib

# 固定字符集
LOWER = "abcdefghijklmnopqrstuvwxyz"
UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
DIGIT = "0123456789"
SYMB  = "@"
POOL  = LOWER + UPPER + DIGIT + SYMB

def phrase_to_password(phrase: str, pin: str, length: int = 10, rounds: int = 100_000) -> str:
    if length < 4:
        raise ValueError("length 至少为 4(需要覆盖四类字符)。")
    if not (pin.isdigit() and len(pin) == 6):
        raise ValueError("PIN 必须是 6 位数字。")

    # 派生足够多的字节
    dklen = max(length, 64)
    dk = hashlib.pbkdf2_hmac(
        "sha256",
        phrase.encode("utf-8"),
        pin.encode("utf-8"),  # 使用 6 位 PIN 作为 salt
        rounds,
        dklen=dklen
    )

    # 确保四类字符各一个
    picks = [
        LOWER[dk[0] % len(LOWER)],
        UPPER[dk[1] % len(UPPER)],
        DIGIT[dk[2] % len(DIGIT)],
        SYMB [dk[3] % len(SYMB )],
    ]

    # 剩余位置从全集补齐
    for i in range(4, length):
        picks.append(POOL[dk[i % len(dk)] % len(POOL)])

    # 洗牌
    j = 0
    for i in range(length - 1, 0, -1):
        j = (j + 1) % len(dk)
        k = dk[j] % (i + 1)
        picks[i], picks[k] = picks[k], picks[i]

    return "".join(picks)

def main():
    parser = argparse.ArgumentParser(description="把一句话 + 6位PIN 转换成强密码")
    parser.add_argument("phrase", help="输入的句子")
    parser.add_argument("pin", help="6位数字 PIN 作为 salt")
    parser.add_argument("--length", type=int, default=10, help="密码长度,默认10")
    args = parser.parse_args()

    pwd = phrase_to_password(args.phrase, args.pin, args.length)
    print(pwd)

if __name__ == "__main__":
    main()

如何运行脚本

保存成 genpwd.py

1
2
HISTFILE=/dev/null bash
python3 genpwd.py 我就记住这句话和一个6为PIN就能得到密码 123456

注意一定要使用HISTFILE=/dev/null bash,否则history里会记录密码导致泄露

加密脚本

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import argparse
import base64
import hashlib
import getpass
from cryptography.fernet import Fernet

# 从密码生成 Fernet key
def password_to_key(password: str) -> bytes:
    digest = hashlib.sha256(password.encode()).digest()
    return base64.urlsafe_b64encode(digest)

# 加密文件
def encrypt_file(filename: str, key: bytes):
    fernet = Fernet(key)
    with open(filename, "rb") as file:
        original = file.read()
    encrypted = fernet.encrypt(original)
    with open(filename + ".enc", "wb") as encrypted_file:
        encrypted_file.write(encrypted)
    print(f"加密完成: {filename}{filename}.enc")

# 解密文件
def decrypt_file(filename: str, key: bytes):
    fernet = Fernet(key)
    with open(filename, "rb") as enc_file:
        encrypted = enc_file.read()
    try:
        decrypted = fernet.decrypt(encrypted)
    except Exception:
        print("错误:无法解密文件。可能是密码错误、文件损坏或该文件不是有效的加密文件。")
        exit(1)
    out_file = filename[:-4]  # 去掉 .enc
    with open(out_file, "wb") as dec_file:
        dec_file.write(decrypted)
    print(f"解密完成: {filename}{out_file}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="文件加解密工具 (基于 cryptography.Fernet)")
    parser.add_argument("filename", help="要加密/解密的文件")
    args = parser.parse_args()

    # 安全地输入密码(不会显示在屏幕、history、ps 里)
    password = getpass.getpass("请输入密码: ")
    key = password_to_key(password)

    if args.filename.endswith(".enc"):
        decrypt_file(args.filename, key)
    else:
        encrypt_file(args.filename, key)

保存成 encrypt.py

1
python3 encrypt.py 要加/解密的文件

一句话和6位PIN转换成12/24助记词

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import argparse
from hashlib import sha256
from mnemonic import Mnemonic

def check_pin(pin_str):
    """验证 PIN 是否是 6 位数字"""
    if not pin_str.isdigit() or len(pin_str) != 6:
        raise argparse.ArgumentTypeError("PIN 必须是 6 位数字")
    return pin_str

def generate_mnemonic(sentence, pin, length=12, lang="english"):
    """
    根据一句话和 PIN 生成固定助记词
    :param sentence: 输入句子
    :param pin: PIN码(6位数字)
    :param length: 助记词长度 12 或 24
    :param lang: 语言,默认英文
    :return: 助记词字符串
    """
    if length not in (12, 24):
        raise ValueError("助记词长度只能是 12 或 24")

    combined = f"{sentence}-{pin}"
    hash_bytes = sha256(combined.encode('utf-8')).digest()
    
    mnemo = Mnemonic(lang)
    entropy_bytes = hash_bytes[:16] if length == 12 else hash_bytes
    mnemonic_phrase = mnemo.to_mnemonic(entropy_bytes)
    return mnemonic_phrase

def main():
    parser = argparse.ArgumentParser(description="根据一句话和 6 位 PIN 生成 BIP39 助记词")
    parser.add_argument("sentence", help="一句话")
    parser.add_argument("pin", type=check_pin, help="6 位数字 PIN")
    parser.add_argument("--length", type=int, default=24, choices=[12,24], help="助记词长度,12 或 24")
    parser.add_argument("--lang", default="english", choices=["english","chinese_simplified"], help="助记词语言")
    
    args = parser.parse_args()
    
    mnemonic_phrase = generate_mnemonic(args.sentence, args.pin, args.length, args.lang)
    print("生成的助记词:")
    print(mnemonic_phrase)

if __name__ == "__main__":
    main()

使用密码混淆单词句子

助记词使用这个程序混淆后再抄写到纸上。只有纸条也没什么用。需要加上密码和函数才能解密真的助记词

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import random

class HashTool:
    def __init__(self, pin=None):
        """
        初始化工具类,可以选择传入一个 PIN。
        如果未传入 PIN,则会随机生成一个。
        """
        self.pin = pin if pin else self.generate_pin()

    def generate_pin(self, length=8):
        """
        生成一个随机 PIN,默认长度为 8。
        """
        return ''.join(random.choices('0123456789', k=length))

    def encrypt(self, word):
        """
        使用 PIN 对输入的单词进行加密。
        :param word: 原始单词
        :return: 加密后的单词
        """
        random.seed(self.pin)  # 使用 PIN 作为随机种子
        mapping = self._generate_mapping()
        return ''.join(mapping[char] if char in mapping else char for char in word)

    def decrypt(self, word):
        """
        使用 PIN 对加密后的单词进行解密。
        :param word: 加密后的单词
        :return: 解密后的原始单词
        """
        random.seed(self.pin)  # 使用 PIN 作为随机种子
        mapping = self._generate_mapping()
        reverse_mapping = {v: k for k, v in mapping.items()}
        return ''.join(reverse_mapping[char] if char in reverse_mapping else char for char in word)

    def _generate_mapping(self):
        """
        根据随机种子生成字符映射表。
        :return: 字符映射表
        """
        chars = list('abcdefghijklmnopqrstuvwxyz')
        shuffled = chars[:]
        random.shuffle(shuffled)
        return dict(zip(chars, shuffled))

if __name__ == "__main__":
    import getpass

    print("欢迎使用可逆哈希工具!")

    while True:
        pin = getpass.getpass("请输入密码(不会显示在屏幕上): ")
        confirm_pin = getpass.getpass("请再次输入密码以确认: ")

        if pin == confirm_pin:
            print("密码设置成功!")
            break
        else:
            print("两次输入的密码不一致,请重试!")

    tool = HashTool(pin=pin)

    try:
        while True:
            word = input("\n请输入单词(加密/解密): ")

            encrypted = tool.encrypt(word)
            decrypted = tool.decrypt(word)

            print(f"加密结果: {encrypted}")
            print(f"解密结果: {decrypted}")
    except KeyboardInterrupt:
        print("\n已退出程序,感谢使用!")
本文由作者按照 CC BY 4.0 进行授权