如何获取具有“罕见”指纹的 PGP RSA 密钥对,例如以“ABBA”开头的指纹?(Windows 10)

如何获取具有“罕见”指纹的 PGP RSA 密钥对,例如以“ABBA”开头的指纹?(Windows 10)

在 Windows 10 上,如何自动生成 PGP RSA 密钥对,如果 40 个十六进制字符的指纹未开始ABBA(例如),则转储每个密钥对,当生成的对满足该标准时停止,然后以标准 ASCII 装甲形式导出公钥和私钥?假设所有指纹都是等概率的,在这个例子中,预计要生成的对数约为 45,000 个,太多了,无法手动处理。

答案1

我不确定在 Windows 中是否有好的方法可以做到这一点。

编辑

我最终为了好玩而对此进行了一些修改,并想出了一个新的 Python 脚本,该脚本可以在 Linux 和 Windows 中运行。它gpgsplit通过直接读取导出的密钥来消除需要。该脚本还运行所有命令,包括生成、导出和导入密钥 - 除了--edit-key流程末尾用户必须手动执行的部分之外的所有命令。您所要做的就是运行脚本,然后编辑密钥。

Windows 中未安装 Python 3,因此你需要安装它。你可以从Microsoft Store或者你可以从 Python 网站下载

只需将下面的脚本保存为keychanger.py您想要的任何目录,然后在同一目录中打开终端或 powershell 并输入python keychanger.py。它会提示用户输入您希望指纹以哪个十六进制字符串开头以及要使用的密钥算法(提示包含一些有用的信息)。删除创建的密钥时,gpg 会发出提示(您必须确认删除 3 次;没有办法解决这个问题)。

请注意,Windows 似乎按字面意思解释了该--passphrase ''选项,该密钥不再没有密码,而是以两个单引号作为密码。如果在脚本运行时看到弹出 pinentry,请输入''密码。脚本运行后,您可以在编辑密钥时更改此密码。我在脚本输出中包含了编辑密钥的说明,以便您在需要时可以在屏幕上查看。

此外,Powershell 和命令提示符都不允许复制/粘贴命令,除非你通过右键单击其标题栏、选择“首选项”并选中“使用 Ctrl+Shift+V/C 进行复制/粘贴“。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# keychanger.py - a script to create PGP keys with fingerprints begining
# with a custom hex string.
# Requires GnuPG 2.2.x or later and Python 3.4 or later
#
#
# Max number of iterations to try before giving up:
limit = 1209600

# If the script fails to find a match for your target, try increasing
# this number. Note that this is equivalent to the maximum number of
# seconds to subtract from the timestamp. The default of 1209600 is 2
# weeks and should be sufficient to find any 4 character or less hex
# string. One year is 31557600 seconds. Longer string lengths require a
# greater number of iterations.

########################################################################
#  ! ! !      DON'T CHANGE ANYTHING FROM THIS POINT ON        !  !  !  #
#  ! ! !      UNLESS YOU REALLY KNOW WHAT YOU'RE DOING.       !  !  !  #
########################################################################

algos = {'rsa', 'rsa1024', 'rsa2048', 'rsa3072', 'rsa4096', 'dsa', 'dsa1024', 'dsa2048', 'ed25519', 'nistp256', 'nistp384', 'nistp521', 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1', 'secp256k1'}

# Message prompts for user input
P = ["\nEnter the target hex string you want the key fingerprint to start with.\nIf you want to match a string longer than 4 characters, you'll need to increase to iteration 'limit' value in the script.\n\nEnter hex string target> ","\nChoose the algorithm for the signing key. Valid signing key algorithms are:\n  rsa  rsa1024  rsa2048  rsa3072  rsa4096\n  dsa  dsa1024  dsa2048  dsa3072\nGnuPG also allows elliptic curve key algorithms:\n  ed25519\n  nistp256  nistp384  nistp521\n  brainpoolP256r1  brainpoolP384r1  brainpoolP512r1\n  secp256k1\n(note: some elliptic curve algorithms might not be available in your version of GnuPG)\n\nEnter key algorithm> "]

def hexcheck(target):
    digits = '0123456789abcdef'
    target = target.lower()
    return set(list(target)).issubset(list(digits))

target = input(P[0])
while hexcheck(target) == False:
    print("String contains non-hex digits. Try again.")
    target = input(P[0])
target = target.lower()
algo = input(P[1])
while algo not in algos:
    print("Algorithm not in list. Try again.\n")
    algo = input(P[1])

import hashlib, time, subprocess, uuid, pathlib

# Set user ID and generated file names to random strings.
# This is to ensure none of them already exist
user_id = str(uuid.uuid4())
public_key = str(uuid.uuid4())
secret_key = str(uuid.uuid4())

# Commands to generate, export, then delete the generated keys
cmd1 = ["gpg --quiet --batch --pinentry-mode loopback --passphrase '' --quick-gen-key "+user_id+" "+algo, "gpg --quiet -o "+public_key+" --export "+user_id,"gpg --quiet -o "+secret_key+" --export-secret-keys "+user_id, "gpg --quiet --yes --delete-secret-and-public-keys "+user_id]
for c in cmd1:
    subprocess.call(c, shell=True)


# Functions to read gpg exported key data and list individual packets
def header_info(x):
    if x[0]&64 == 64:
        tag = x[0]&63
        if x[1] < 192: [hlen,plen] = [2,x[1]]
        elif x[1] <= 223: [hlen,plen] = [3,((x[1] - 192) << 8) + x[2] + 192]
        elif x[1] == 255: [hlen,plen] = [5,int.from_bytes(x[2:6],byteorder='big',signed=False)]
    else:
        tag = (x[0]&60)>>2
        if x[0]&3 == 0: [hlen,plen] = [2,x[1]]
        elif x[0]&3 == 1: [hlen,plen] = [3,int.from_bytes(x[1:3],byteorder='big',signed=False)]
        elif x[0]&3 == 2: [hlen,plen] = [5,int.from_bytes(x[1:5],byteorder='big',signed=False)]
    return [tag, hlen, plen]

def splitpackets(x):
    packets = []
    i = 0
    while i < (len(x)-1):
        h = header_info(x[i:i+5])
        tag = h[0]
        packet = x[i:i+h[1]+h[2]]
        i += (h[1]+h[2])
        packets.append([tag,packet])
    return packets


# Rewrite secret_key file without signature packet
with open(secret_key, 'rb') as f:
    key = bytes(f.read())
key = splitpackets(key)
key = b''.join([x[1] for x in key if x[0] in {5,13}])
with open(secret_key, 'wb') as f:
    f.write(key)

# Get public_key packet
with open(public_key,'rb') as f:
    key = bytes(f.read())

# Define header and packet length (subject to adjustment in next step).
hlen = 3
plen = int.from_bytes(key[1:3],byteorder='big',signed=False)

# Make sure public key header is properly formed for calculating the
# fingerprint by verifying the first byte is 0x99. In practice the only
# times this wouldn't be the case is for rsa1024 and elliptic curve
# algorithms whose smaller key size means the packet length can be
# encoded in a single byte.
if key[0] != 0x99:
    # Check that header is "old" format (should be, but just in case...)
    if key[0]&64 == 0:
        hlen = 2
        plen = key[1]
    else:
        # "New" format header. Irregular but not invalid format for a
        # key packet header.
        if key[1] < 192:
            hlen = 2
            plen=key[2]
        elif key[1] <= 223:
            plen = ((key[1] - 192) << 8) + key[2] + 192

# Canonicalize header for fingerprinting. Fingerprint is calculated
# over the packet data with a 3 byte header consisting of 0x99 and a 2-
# byte big endian number that is a count of bytes in the packet data.
h = b'\x99'+plen.to_bytes(2,byteorder='big',signed=False)
key = bytearray(h+key[hlen:hlen+plen])

# Extract current timestamp
timestamp = int.from_bytes(key[4:8],byteorder='big',signed=True)

# Set length of string to match
L = len(target)

# Set the initial hash value and iteration counter.
fingerprint = hashlib.sha1()
fingerprint.update(key)
current = fingerprint.hexdigest()[0:L]
i = 0

# Timer to measure duration of search.
start = time.time()

# Begin search for fingerprint string match
while current != target and i < limit:
    fingerprint = hashlib.sha1()
    timestamp -= 1
    i += 1
    key[4:8] = timestamp.to_bytes(4,byteorder='big',signed=True)
    fingerprint.update(key)
    current = fingerprint.hexdigest()[0:L]

# Stop time for timer.
end = time.time()

if current == target:
    # Success :)
    with open(secret_key,'r+b') as f:
        # Go to start of timestamp in exported secret_key file
        f.seek(hlen+1)
        # Overwrite timestamp with the one found in the while loop
        f.write(bytes(key[4:8]))
    print("\nDone "+str(i)+" iterations. Total time: "+str(end-start)+" seconds\n")
    # List of commands to import key and display fingerprint
    cmd2 = ["gpg --quiet --allow-non-selfsigned-uid --import "+secret_key, "gpg -K "+user_id]
    for c in cmd2:
        subprocess.call(c, shell=True)
    print("\nYOU NEED TO EDIT THIS KEY.\nEnter 'gpg --edit-key "+user_id+"' then use the following commands after the 'gpg>' prompt in the order shown:\n  gpg> adduid       ...add a new user ID by following the prompts.\n  gpg> 1            ...selects the '"+user_id+"' user id\n  gpg> deluid       ...delete the selected user ID then confirm.\n  gpg> change-usage ...toggle usage options until it shows 'Current allowed actions: Sign Certify' then enter Q.\n  gpg> passwd       ...set a password\n  gpg> addkey       ...follow prompts to add encryption subkey\n  gpg> save         ...save the changes made in editing mode")
else:
    # Failure :(
    print("Failed to match "+str(target)+" in "+str(i)+" iterations. Total time: "+str(end-start)+" seconds")

# Remove generated key files
pathlib.Path(public_key).unlink()
pathlib.Path(secret_key).unlink()

指纹只是公钥数据包的 SHA1 哈希值 - 并且该数据包包含时间戳。这意味着您可以通过逐步更改时间戳来从同一密钥生成新指纹。

细节可能听起来有点复杂,但做起来非常快而且容易。

该脚本的工作原理是将密钥数据包的时间戳向后增加 1,然后将生成的数据包输入到 sha1 中,以检查指纹是否以每次迭代选择的十六进制字符串开头。

当它找到以与目标字符串匹配的十六进制字符开头的指纹时,它会停止迭代并用产生所需指纹的时间戳覆盖密钥中的时间戳。

然后,它将新密钥导入 gpg 并删除脚本创建的文件。

快完成了

现在您需要使用 gpg 编辑密钥以使其准备好实际使用。需要添加新的用户 ID,需要删除旧的(未签名的)用户 ID,需要将密钥用途设置为仅“SC”(签名、认证),需要设置密码,并且需要创建加密子密钥。然后需要保存这些更改。

$ gpg --edit-key <some long uuid>

查看 uuid 的脚本输出。它将显示您可以复制/粘贴的完整 edit-key 命令。

所有后面的命令都是在编辑键菜单上下文中输入的。

  • 通过在 gpg> 提示符下输入以下内容来添加您的用户 ID:

adduid

它会提示您输入“真实姓名”、“电子邮件地址”和“注释”(即,与 gpg 通常生成密钥时所做的内容相同)。按照提示操作,并在o系统提示您接受输入的用户 ID 数据时输入。

  • 删除旧的未签名用户 ID。输入1以选择第一个用户 ID。然后输入:

deluid

它会要求您确认删除用户 ID。输入y

  • adduid通过在 gpg> 提示符下输入以下内容来更改密钥用法(请注意,您必须在执行此操作之前执行此步骤):

change-usage

它会显示Current allowed actions: Sign Certify Encrypt Authenticate,因为这是主密钥,所以它应该只签名和认证。输入e以关闭加密功能,然后输入a以关闭身份验证功能。现在它应该显示Current allowed actions: Sign Certify我们想要的内容,因此输入q

  • 设置密码。

passwd

将会弹出一个密码输入窗口 - 输入您要使用的密码。

  • 通过输入以下内容添加加密子密钥:

addkey

然后选择密钥的类型,然后在提示时输入密码。

  • 现在,最重要的是保存更改你在编辑密钥时输入

save

这将返回到命令提示符。您可以在密钥编辑过程中随时执行此操作。现在,您拥有了一个功能齐全的自定义指纹密钥。

相关内容