summaryrefslogtreecommitdiff
path: root/examples/make-8192-bit-key.py
blob: c533fc9262ceed22d6dff0a35def3fdbc7d0cb74 (plain)
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""Create a new 8192-bit GnuPG keypair.

:authors: Isis <isis@patternsinthevoid.net> 0xa3adb67a2cdb8b35
:license: MIT license
:copyright: (c) 2013 Isis Agora Lovecruft
"""

from __future__ import print_function
from __future__ import absolute_import
from __future__ import unicode_literals

import os
import logging

import gnupg

from gnupg import _logger

# Set up logging:
log = _logger.create_logger(9)
log.setLevel(9)


#―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
# Settings
#
# You probably want to edit the following variables. Ones which are currently
# set to strings are necessary; the ones which are set to `None` are optional.

# The directory to use as the homedir for GnuPG (it will contain the
# secring.gpg and pubring.gpg, etc.)
NEWKEY_DIR = './8192-bit-key'

# The name you go by, as it should appear in the primary keyid, i.e. "Evey
# Hammond":
NAME = 'Someone'

# The comment which goes in parantheses after the name and before the email
# address on the key's primary uid. Leave as None to not have one.
NAME_COMMENT = None

# The email address for the primary UID (You *should* actually be able to put
# whatever you want here, like a domain or something, because the GnuPG
# `--allow-freeform-uid` option will be used. I've not actually tested this
# though.)
NAME_EMAIL = 'someone@example.com'

# Expiration date for the new key. To use the default expiration of one year,
# set to None.
#EXPIRE_DATE = '1999-09-19'
EXPIRE_DATE = None


# GnuPG-1.4.x allows the automated creation of passphraseless keys. If using
# GnuPG-1.4.x, and you don't specify the passphrase, you can of course set it
# later with `$ gpg --edit-key` and then at the prompt typing `password`. If
# using a GnuPG from the 2.x series, you *must* specify a password here
# (though you can still change it afterward).
PASSPHRASE = None

# Type of key, i.e. 'RSA' or 'DSA' or something else. I've only tested
# 8192-bit keys with RSA.
KEY_TYPE = 'RSA'

# Uses for the key. Can be things like 'cert,sign' or 'cert' or 'cert,auth'.
KEY_USAGE = 'cert'

# Key bitlength. You likely want 8192, if you're using this script.
#
# It *is* possible to create 16834-bit keys, though it requires modifying and
# recompiling GnuPG. Doing this is a bit janky due to internal GnuPG buffers
# in several parts of the codebase being limited to 8192-bits, the key cannot
# be handled by *most* keyservers (there appears to be only one public
# keyserver which supports 16384-bit keys being uploaded to it), and the
# 16834-bit key will likely require the modified GnuPG to work with it (even
# then some operations, such as removal of the primary secret key, but not the
# primary public key, from the keychain will be badly broken).
KEY_LENGTH = 8192

# Type of subkey. None to skip subkey generation. You can add keys later
# through `$ gpg --edit-key`. For compatibility with people who aren't doing
# crazy things with their keys, you maybe probably want to use `--edit-key` to
# create some nice, normal, "overly-paranoid" 4096-bit keys.
SUBKEY_TYPE = 'RSA'

# Same as KEY_USAGE.
#SUBKEY_USAGE = None
SUBKEY_USAGE = 'sign'

# Same as KEY_LENGTH.
#SUBKEY_LENGTH = None
SUBKEY_LENGTH = 4096

# The default keyserver for the key, which is embedded into the key, telling
# other people's GnuPGs to fetch (and send updates) to this URL:
KEYSERVER = None

# Set the cipher, hash, and compression preference values for this key. This
# expects the same type of string as the sub-command ‘setpref’ in the
# --edit-key menu. The default preferences are given in
# ``gnupg.GPG.default_preference_list``.
PREFERENCES = None
#―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――


gpg = gnupg.GPG(homedir=NEWKEY_DIR)
allparams = {'name_real': NAME,
             'name_comment': NAME_COMMENT,
             'name_email': NAME_EMAIL,
             'expire_date': EXPIRE_DATE,
             'passphrase': PASSPHRASE,
             'key_type': KEY_TYPE,
             'key_usage': KEY_USAGE,
             'key_length': KEY_LENGTH,
             'subkey_type': SUBKEY_TYPE,
             'subkey_usage': SUBKEY_USAGE,
             'subkey_length': SUBKEY_LENGTH,
             'keyserver': KEYSERVER,
             'preferences': PREFERENCES}

def createBatchfile(keyparams=allparams):
    """Create the batchfile for our new key.

    :params dict keyparams: A dictionary of arguments for creating the key. It
                            should probably be ``allparams``.
    :rtype: str
    :returns: A string containing the entire GnuPG batchfile.
    """
    useparams = {}
    for key, value in keyparams.items():
        if value:
            useparams.update({key: value})
    batchfile = gpg.gen_key_input(separate_keyring=True,
                                  save_batchfile=True,
                                  **useparams)
    log.info("Generated GnuPG batch file:\n%s" % batchfile)
    return batchfile

def createKey(batchfile):
    """Create a new keypair from a **batchfile**.

    Writes the new keys into keyrings named after ``NAME_EMAIL`` inside the
    ``NEWKEY_DIR``.

    :params str batchfile: A GnuPG batchfile. See :func:`createBatchfile`.
    """
    key = gpg.gen_key(batchfile)
    fingerprint = key.fingerprint

    if not fingerprint:
        log.error("Key creation seems to have failed: %s" % key.status)
        return None, None
    return key, fingerprint

def displayNewKey(key):
    """Use ``gnupg.GPG.list_keys()`` to display details of the new key."""

    if key.keyring:
        gpg.keyring = key.keyring
    if key.secring:
        gpg.secring = key.secring

    # Using '--fingerprint' twice will display subkey fingerprints too:
    gpg.options = ['--fingerprint', '--fingerprint']
    keylist = gpg.list_keys(secret=True)

    # `result` is a `gnupg._parsers.ListKeys`, which is list-like, so iterate
    # over all the keys and display their info:
    for gpgkey in keylist:
        for k, v in gpgkey.items():
            log.info("%s: %s" % (k.capitalize(), v))

    return keylist

def exportNewKey(fingerprint):
    """Export the new keys into .asc files.

    :param str fingerprint: A full key fingerprint.
    """
    log.info("Exporting key: %s" % fingerprint)

    keyfn = os.path.join(gpg.homedir,
                         fingerprint + '-8192-bit-key') + os.path.extsep

    pubkey = gpg.export_keys(fingerprint)
    seckey = gpg.export_keys(fingerprint, secret=True)
    subkey = gpg.export_keys(fingerprint, secret=True, subkeys=True)

    with open(keyfn + 'pub' + os.path.extsep + 'asc', 'w') as fh:
        fh.write(pubkey)
    with open(keyfn + 'sec' + os.path.extsep + 'asc', 'w') as fh:
        fh.write(seckey)
    with open(keyfn + 'sub' + os.path.extsep + 'asc', 'w') as fh:
        fh.write(subkey)


if __name__ == '__main__':
    if (NAME == 'Someone') or (NAME_EMAIL == 'someone@example.com'):
        log.info("Please edit the settings variables within this script.")
        log.info("Exiting...")
        exit(1)
    else:
        try:
            batchfile = createBatchfile()
            key, fingerprint = createKey(batchfile)
            log.info("New key with fingerprint %r created" % fingerprint)
            displayNewKey(key)
            exportNewKey(fingerprint)

        except Exception as error:
            log.error(error)