Skip to main content
deleted 74 characters in body
Source Link

Now you need to use gpg to edit the key to get it ready for actual use. A new user ID needs to be added, the old (unsigned) user ID needs to be deleted, key usage needs to be set to only "SC" (sign, certify), a password needs to be set, and an encryption subkey needs to be created. These changes then need to be saved, then the key is ready to be exported so you can import it to your Windows gpg.

Now you need to use gpg to edit the key to get it ready for actual use. A new user ID needs to be added, the old (unsigned) user ID needs to be deleted, key usage needs to be set to only "SC" (sign, certify), a password needs to be set, and an encryption subkey needs to be created. These changes need to be saved, then the key is ready to be exported so you can import it to your Windows gpg.

Now you need to use gpg to edit the key to get it ready for actual use. A new user ID needs to be added, the old (unsigned) user ID needs to be deleted, key usage needs to be set to only "SC" (sign, certify), a password needs to be set, and an encryption subkey needs to be created. These changes then need to be saved.

Heavily edited because I made a different python script that worked in Windows and automated most of the process. This made the bulk of my previous answer obsolete.
Source Link

I'm not sure there's a good way to do this in Windows. The best I can think of would be a script that incorporates batch generation of each key to eliminate the need for user interaction, ie gpg --batch --pinentry-mode loopback --passphrase 'YourPassword' --quick-gen-key "USERNAME" rsaI'm not sure there's a good way to do this in Windows.

Unfortunately the full fingerprint isn't shown in the key generation output, so another call to gpg would be required (ie gpg -k) to produce output containing the fingerprint. If the generated key does not have the desired fingerprint, "dumping" the key requires two steps: deleting the contents of .gnupg/private-keys-v1.d then deleting the public key from the keyring (Remove-Item C:\pathto.gnupg folder\private-keys-v1.d\* then gpg --yes --batch --delete-key USERNAME). The contents of private-keys-v1.d has to be deleted on each unsuccessful attempt because this is where the secret keys are stored and there's no way to delete secret keys without user interaction in gpg. Also note that the secret key must be deleted before the public key.

Because the string you want has a length of 16 bits, you can expect to find a match within 2^16 or 65,536 attempts, which will take a while - maybe days - because of the bottleneck at RSA key generation.

But there is a MUCH faster way (in Linux)EDIT

This method requires the use ofI ended up tinkering with this some more just for fun and came up with a tool callednew python script that will work in both Linux and Windows. It does away with the need for gpgsplit and, unfortunately, I don't thinkby reading the gpgsplit program is available for Windows. But honestly, this method is so fast you should just boot an Ubuntu Live CD or USB to do itexported keys directly. I'm no hacker, but my clunky Python code below produced a match in less than 1 second even in cases whenThe script also runs all the number of iterations ran to over 200commands including generating,000.

The fingerprint is just a SHA1 hash of exporting, and importing the public key packet - and this packet contains a timestamp. This means you can generate new fingerprints fromeverything except the same key by incrementally changing only--edit-key part at the timestamp. Once you have a match, mergeend of the public key packet withprocess which the secret key and import ituser has to gpgdo manually.

The details might sound a little complicated, but it's very fast and easy All you have to do is run the script then edit the key.

Follow these steps:

  1. Generate a single key pair. Create only a primary key pair with no subkey or passphrase (add those later when the desired key is ready). Also don't worry about the username for now, just copy/paste the commands (don't copy paste the "$" sign) because the user ID used here will eventually be deleted before the final key is produced:
$ gpg --batch --pinentry-mode loopback --passphrase '' --quick-gen-key "USERNAME" rsa
  1. Now export the entire key and use gpgsplit to separate it into its constituent parts:
$ gpg --export USERNAME | gpgsplit -p /tmp/
$ gpg --export-secret-keys USERNAME | gpgsplit -p /tmp/

This will produce 4 files: 000001-006.public_key , 000001-005.secret_key , 000002-013.user_id Python 3 isn't installed in Windows, 000003-002.sigso you'll need to install it. These will be used byYou can get it from the Python script and a couple of terminal commands. Also I use /tmp/ for all pathnames refrenced because as far as I know this is consistent across all *nix systems so the python script and bash commands will all work as isMicrosoft Store or you can download it from the Python website.

  1. Now that the data is exported, delete the key pair in gpg:

Just save save the script below as keychanger.py in any directory you want, then open a terminal or powershell in that same directory and enter python keychanger.py. It prompts for user input to tell it what hex string you want the fingerprint to start with and the key algorithm to use (prompts include some helpful info). There will be prompts from gpg when the created key is being deleted (you will have to confirm deletion 3 times; no way around this).

$ gpg --delete-secret-and-public-keys USERNAME

Note that Windows seems to interpret the --passphrase '' option literally and instead of the key having no password, it will have two single quotes as the password. If you see a pinentry pop up while the script is running, enter '' for the password. You can change this password when you edit the key after the script has run. I included the instructions for editing the key in the script output so it will be viewable on screen when you need it.

  1. Save the code below as keychange.py in the default home directory. On the line that reads target = 'abba', change 'abba' to whatever 4 character hex string you want the fingerprint to start with (it MUST be exactly 4 hex digits and any letters have to be lowercase). Save the file again after you change the target string.

Also, neither Powershell nor Command Prompt allow copy/pasting commands unless you enable it by right-clicking on its title bar, selecting "Preferences" and checking the box that says "Use Ctrl+Shift+V/C as Copy/Paste".

#!/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
with
# openSet user ID and generated file names to random strings.
# This is to ensure none of them already exist
user_id = str('/tmp/000001uuid.uuid4())
public_key = str(uuid.uuid4())
secret_key = str(uuid.uuid4())

# Commands to generate, export, then delete the generated keys
cmd1 = ["gpg -006-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.public_key'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 = bytearraybytes(f.read())
fingerprintkey = hashlibsplitpackets(key)
key = b''.sha1join([x[1] for x in key if x[0] in {5,13}])
fingerprintwith open(secret_key, 'wb') as f:
    f.updatewrite(key)
target
# Get public_key packet
with open(public_key,'rb') as f:
    key = 'abba'bytes(f.read())
current
# 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.hexdigest 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...)[0
    if key[0]&64 == 0:4]
i        hlen = 02
        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=Falsesigned=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=Falsesigned=True)
    fingerprint.update(key)
    current = fingerprint.hexdigest()[0:4]L]

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

if current == target:
    # Success :)
    with open('/tmp/000001-005.secret_key'secret_key,'r+b') as f:
        # Go to start of timestamp in exported secret_key file
        f.seek(4hlen+1)
        # Overwrite timestamp with the one found in the while loop
        f.write(bytes(key[4:8]))
    print('Done"\nDone '+str"+str(i)+'+" iterations'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()

The script works by incrementing the key packet timestamp backwards by 1 then feeding the resulting packet into sha1 to check if the fingerprint will begin with 'abba' for each iteration. When it findsis just a fingerprint that starts with 4 hex characters matching the target string, it stops iterating and overwrites the timestamp inSHA1 hash of the public key packet contained within the 000001-005.secret_key file with the and this packet contains a timestamp that will produce the desired fingerprint. Upon completion it writes to stdout "Done" andThis means you can generate new fingerprints from the number of iterations it tooksame key by incrementally changing only the timestamp.

  1. Open a terminal active in the same directory keychange.py is saved in (by default terminal opens in your home directory). and enter these 3 commands, one line at a time.

The details might sound a little complicated, but it's very fast and easy to do.

$ python3 keychange.py
$ cat /tmp/000001-005.secret_key /tmp/000002-013.user_id > /tmp/key
$ gpg --allow-non-selfsigned-uid --import /tmp/key

The script works by incrementing the key packet timestamp backwards by 1 then feeding the resulting packet into sha1 to check if the fingerprint will begin with start with the chosen hex string for each iteration.

Now you can see the imported key and verify itsWhen it finds a fingerprint beginsthat starts with hex characters matching the specifiedtarget string, it stops iterating and overwrites the timestamp in the key with the timestamp that will produce the desired fingerprint.

It then imports the new key to gpg and deletes the files created by the script.

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

Look at the script output for the uuid. It will show the full edit-key command which you can copy/paste.

  • Delete the old unsigned userid. Enter 1 to select the first user ID ("USERNAME"). Then enter:

This returns you to the command prompt. You can do this at any time during the key edit process. You now have a fully functional custom fingerprint key. But since you're (probably) using a Live CD/USB, you need to export it either to another removable drive, or save it to an online account you have (like in an email draft). Basically save it somewhere you can access from your Windows account.

I know it's not a solution for Windows and it doesn't involve automatingThis returns you to the generation of keys, but I hope it will prove useful anywaycommand prompt. You can do this at any time during the key edit process. You now have a fully functional custom fingerprint key.

I'm not sure there's a good way to do this in Windows. The best I can think of would be a script that incorporates batch generation of each key to eliminate the need for user interaction, ie gpg --batch --pinentry-mode loopback --passphrase 'YourPassword' --quick-gen-key "USERNAME" rsa

Unfortunately the full fingerprint isn't shown in the key generation output, so another call to gpg would be required (ie gpg -k) to produce output containing the fingerprint. If the generated key does not have the desired fingerprint, "dumping" the key requires two steps: deleting the contents of .gnupg/private-keys-v1.d then deleting the public key from the keyring (Remove-Item C:\pathto.gnupg folder\private-keys-v1.d\* then gpg --yes --batch --delete-key USERNAME). The contents of private-keys-v1.d has to be deleted on each unsuccessful attempt because this is where the secret keys are stored and there's no way to delete secret keys without user interaction in gpg. Also note that the secret key must be deleted before the public key.

Because the string you want has a length of 16 bits, you can expect to find a match within 2^16 or 65,536 attempts, which will take a while - maybe days - because of the bottleneck at RSA key generation.

But there is a MUCH faster way (in Linux)

This method requires the use of a tool called gpgsplit and, unfortunately, I don't think the gpgsplit program is available for Windows. But honestly, this method is so fast you should just boot an Ubuntu Live CD or USB to do it. I'm no hacker, but my clunky Python code below produced a match in less than 1 second even in cases when the number of iterations ran to over 200,000.

The fingerprint is just a SHA1 hash of the public key packet - and this packet contains a timestamp. This means you can generate new fingerprints from the same key by incrementally changing only the timestamp. Once you have a match, merge the public key packet with the secret key and import it to gpg.

The details might sound a little complicated, but it's very fast and easy to do.

Follow these steps:

  1. Generate a single key pair. Create only a primary key pair with no subkey or passphrase (add those later when the desired key is ready). Also don't worry about the username for now, just copy/paste the commands (don't copy paste the "$" sign) because the user ID used here will eventually be deleted before the final key is produced:
$ gpg --batch --pinentry-mode loopback --passphrase '' --quick-gen-key "USERNAME" rsa
  1. Now export the entire key and use gpgsplit to separate it into its constituent parts:
$ gpg --export USERNAME | gpgsplit -p /tmp/
$ gpg --export-secret-keys USERNAME | gpgsplit -p /tmp/

This will produce 4 files: 000001-006.public_key , 000001-005.secret_key , 000002-013.user_id , 000003-002.sig. These will be used by the Python script and a couple of terminal commands. Also I use /tmp/ for all pathnames refrenced because as far as I know this is consistent across all *nix systems so the python script and bash commands will all work as is.

  1. Now that the data is exported, delete the key pair in gpg:
$ gpg --delete-secret-and-public-keys USERNAME
  1. Save the code below as keychange.py in the default home directory. On the line that reads target = 'abba', change 'abba' to whatever 4 character hex string you want the fingerprint to start with (it MUST be exactly 4 hex digits and any letters have to be lowercase). Save the file again after you change the target string.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import hashlib
with open('/tmp/000001-006.public_key','rb') as f:
    key = bytearray(f.read())
fingerprint = hashlib.sha1()
fingerprint.update(key)
target = 'abba'
current = fingerprint.hexdigest()[0:4]
i = 0
timestamp = int.from_bytes(key[4:8],byteorder='big',signed=False)
while current != target:
    fingerprint = hashlib.sha1()
    timestamp -= 1
    i += 1
    key[4:8] = timestamp.to_bytes(4,byteorder='big',signed=False)
    fingerprint.update(key)
    current = fingerprint.hexdigest()[0:4]
with open('/tmp/000001-005.secret_key','r+b') as f:
    f.seek(4)
    f.write(bytes(key[4:8]))
print('Done '+str(i)+' iterations')

The script works by incrementing the key packet timestamp backwards by 1 then feeding the resulting packet into sha1 to check if the fingerprint will begin with 'abba' for each iteration. When it finds a fingerprint that starts with 4 hex characters matching the target string, it stops iterating and overwrites the timestamp in the public key packet contained within the 000001-005.secret_key file with the timestamp that will produce the desired fingerprint. Upon completion it writes to stdout "Done" and the number of iterations it took.

  1. Open a terminal active in the same directory keychange.py is saved in (by default terminal opens in your home directory). and enter these 3 commands, one line at a time.
$ python3 keychange.py
$ cat /tmp/000001-005.secret_key /tmp/000002-013.user_id > /tmp/key
$ gpg --allow-non-selfsigned-uid --import /tmp/key

Now you can see the imported key and verify its fingerprint begins with the specified string.

$ gpg --edit-key USERNAME
  • Delete the old unsigned userid. Enter 1 to select the first user ID ("USERNAME"). Then enter:

This returns you to the command prompt. You can do this at any time during the key edit process. You now have a fully functional custom fingerprint key. But since you're (probably) using a Live CD/USB, you need to export it either to another removable drive, or save it to an online account you have (like in an email draft). Basically save it somewhere you can access from your Windows account.

I know it's not a solution for Windows and it doesn't involve automating the generation of keys, but I hope it will prove useful anyway.

I'm not sure there's a good way to do this in Windows.

EDIT

I ended up tinkering with this some more just for fun and came up with a new python script that will work in both Linux and Windows. It does away with the need for gpgsplit by reading the exported keys directly. The script also runs all the commands including generating, exporting, and importing the key - everything except the --edit-key part at the end of the process which the user has to do manually. All you have to do is run the script then edit the key.

Python 3 isn't installed in Windows, so you'll need to install it. You can get it from the Microsoft Store or you can download it from the Python website.

Just save save the script below as keychanger.py in any directory you want, then open a terminal or powershell in that same directory and enter python keychanger.py. It prompts for user input to tell it what hex string you want the fingerprint to start with and the key algorithm to use (prompts include some helpful info). There will be prompts from gpg when the created key is being deleted (you will have to confirm deletion 3 times; no way around this).

Note that Windows seems to interpret the --passphrase '' option literally and instead of the key having no password, it will have two single quotes as the password. If you see a pinentry pop up while the script is running, enter '' for the password. You can change this password when you edit the key after the script has run. I included the instructions for editing the key in the script output so it will be viewable on screen when you need it.

Also, neither Powershell nor Command Prompt allow copy/pasting commands unless you enable it by right-clicking on its title bar, selecting "Preferences" and checking the box that says "Use Ctrl+Shift+V/C as Copy/Paste".

#!/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()

The fingerprint is just a SHA1 hash of the public key packet - and this packet contains a timestamp. This means you can generate new fingerprints from the same key by incrementally changing only the timestamp.

The details might sound a little complicated, but it's very fast and easy to do.

The script works by incrementing the key packet timestamp backwards by 1 then feeding the resulting packet into sha1 to check if the fingerprint will begin with start with the chosen hex string for each iteration.

When it finds a fingerprint that starts with hex characters matching the target string, it stops iterating and overwrites the timestamp in the key with the timestamp that will produce the desired fingerprint.

It then imports the new key to gpg and deletes the files created by the script.

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

Look at the script output for the uuid. It will show the full edit-key command which you can copy/paste.

  • Delete the old unsigned userid. Enter 1 to select the first user ID. Then enter:

This returns you to the command prompt. You can do this at any time during the key edit process. You now have a fully functional custom fingerprint key.

changed order of edit-key commands - a user ID needs to be added before "change-usage" will work.
Source Link

Now you need to use gpg to edit the key to get it ready for actual use. The usage needs to be set to only "SC", aA new user ID needs to be added, the old (unsigned) user ID needs to be deleted, key usage needs to be set to only "SC" (sign, certify), a password needs to be set, and an encryption subkey needs to be created. These changes need to be saved, then the key is ready to be exported so you can import it to your Windows gpg.

  • Change key usage by entering the following at the gpg> prompt:

change-usage

It will show Current allowed actions: Sign Certify Encrypt Authenticate and because this is a primary key it should only be Sign and Certify. Enter e to toggle the encrypt ability off, then enter a to toggle the Authenticate ability off. Now it should display Current allowed actions: Sign Certify which is what we want so enter q.

It will ask you to confirm deleting the user id. Enter y.

  • Change key usage (note that you have to do the adduid step before doing this) by entering the following at the gpg> prompt:

change-usage

It will show Current allowed actions: Sign Certify Encrypt Authenticate and because this is a primary key it should only be Sign and Certify. Enter e to toggle the encrypt ability off, then enter a to toggle the Authenticate ability off. Now it should display Current allowed actions: Sign Certify which is what we want so enter q.

Now you need to use gpg to edit the key to get it ready for actual use. The usage needs to be set to only "SC", a new user ID needs to be added, the old (unsigned) user ID needs to be deleted, a password needs to be set, and an encryption subkey needs to be created. These changes need to be saved, then the key is ready to be exported so you can import it to your Windows gpg.

  • Change key usage by entering the following at the gpg> prompt:

change-usage

It will show Current allowed actions: Sign Certify Encrypt Authenticate and because this is a primary key it should only be Sign and Certify. Enter e to toggle the encrypt ability off, then enter a to toggle the Authenticate ability off. Now it should display Current allowed actions: Sign Certify which is what we want so enter q.

It will ask you to confirm deleting the user id. Enter y.

Now you need to use gpg to edit the key to get it ready for actual use. A new user ID needs to be added, the old (unsigned) user ID needs to be deleted, key usage needs to be set to only "SC" (sign, certify), a password needs to be set, and an encryption subkey needs to be created. These changes need to be saved, then the key is ready to be exported so you can import it to your Windows gpg.

It will ask you to confirm deleting the user id. Enter y.

  • Change key usage (note that you have to do the adduid step before doing this) by entering the following at the gpg> prompt:

change-usage

It will show Current allowed actions: Sign Certify Encrypt Authenticate and because this is a primary key it should only be Sign and Certify. Enter e to toggle the encrypt ability off, then enter a to toggle the Authenticate ability off. Now it should display Current allowed actions: Sign Certify which is what we want so enter q.

Source Link
Loading