File: keys.rst

package info (click to toggle)
python-pgpy 0.6.0-1.4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,212 kB
  • sloc: python: 8,448; makefile: 155; sh: 10
file content (153 lines) | stat: -rw-r--r-- 6,155 bytes parent folder | download | duplicates (2)
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
Keys
====

Generating Keys
---------------

PGPy can generate most types keys as defined in the standard.

Generating Primary Keys
^^^^^^^^^^^^^^^^^^^^^^^

It is possible to generate most types of keys with PGPy now. The process is mostly straightforward::

    from pgpy.constants import PubKeyAlgorithm, KeyFlags, HashAlgorithm, SymmetricKeyAlgorithm, CompressionAlgorithm

    # we can start by generating a primary key. For this example, we'll use RSA, but it could be DSA or ECDSA as well
    key = pgpy.PGPKey.new(PubKeyAlgorithm.RSAEncryptOrSign, 4096)

    # we now have some key material, but our new key doesn't have a user ID yet, and therefore is not yet usable!
    uid = pgpy.PGPUID.new('Abraham Lincoln', comment='Honest Abe', email='abraham.lincoln@whitehouse.gov')

    # now we must add the new user id to the key. We'll need to specify all of our preferences at this point
    # because PGPy doesn't have any built-in key preference defaults at this time
    # this example is similar to GnuPG 2.1.x defaults, with no expiration or preferred keyserver
    key.add_uid(uid, usage={KeyFlags.Sign, KeyFlags.EncryptCommunications, KeyFlags.EncryptStorage},
                hashes=[HashAlgorithm.SHA256, HashAlgorithm.SHA384, HashAlgorithm.SHA512, HashAlgorithm.SHA224],
                ciphers=[SymmetricKeyAlgorithm.AES256, SymmetricKeyAlgorithm.AES192, SymmetricKeyAlgorithm.AES128],
                compression=[CompressionAlgorithm.ZLIB, CompressionAlgorithm.BZ2, CompressionAlgorithm.ZIP, CompressionAlgorithm.Uncompressed])

Specifying key expiration can be done using the ``key_expiration`` keyword when adding the user id. Expiration can be specified
using a :py:obj:`datetime.datetime` or a :py:obj:`datetime.timedelta` object::

    from datetime import timedelta

    # in this example, we'll use fewer preferences for the sake of brevity, and set the key to expire in 1 year
    key = pgpy.PGPKey.new(PubKeyAlgorithm.RSAEncryptOrSign, 4096)
    uid = pgpy.PGPUID.new('Nikola Tesla')  # comment and email are optional

    # the key_expires keyword accepts a :py:obj:`datetime.datetime`
    key.add_uid(uid, usage={KeyFlags.Sign}, hashes=[HashAlgorithm.SHA512, HashAlgorithm.SHA256],
                ciphers=[SymmetricKeyAlgorithm.AES256, SymmetricKeyAlgorithm.Camellia256],
                compression=[CompressionAlgorithm.BZ2, CompressionAlgorithm.Uncompressed],
                key_expiration=timedelta(days=365))

Generating Sub Keys
^^^^^^^^^^^^^^^^^^^

Generating a subkey is similar to the process above, except that it requires an existing primary key::

    # assuming we already have a primary key, we can generate a new key and add it as a subkey thusly:
    subkey = pgpy.PGPKey.new(PubKeyAlgorithm.RSAEncryptOrSign, 4096)

    # preferences that are specific to the subkey can be chosen here
    # any preference(s) needed for actions by this subkey that not specified here
    # will seamlessly "inherit" from those specified on the selected User ID
    key.add_subkey(subkey, usage={KeyFlags.Authentication})

Loading Keys
------------

There are two ways to load keys: individually, or in a keyring.

Loading Keys Individually
^^^^^^^^^^^^^^^^^^^^^^^^^

Keys can be loaded individually into PGPKey objects::

    # A new, empty PGPkey object can be instantiated, but this is not very useful
    # by itself.
    # ASCII or binary data can be parsed into an empty PGPKey object with the .parse()
    # method
    empty_key = pgpy.PGPKey()
    empty_key.parse(keyblob)

    # A key can be loaded from a file, like so:
    key, _ = pgpy.PGPKey.from_file('path/to/key.asc')

    # or from a text or binary string/bytes/bytearray that has already been read in:
    key, _ = pgpy.PGPKey.from_blob(keyblob)

Loading Keys Into a Keyring
^^^^^^^^^^^^^^^^^^^^^^^^^^^

If you intend to maintain multiple keys in memory for extended periods, using a PGPKeyring may be more appropriate::

    # These two methods are mostly equivalent
    kr = pgpy.PGPKeyring(glob.glob(os.path.expanduser('~/.gnupg/*ring.gpg')))

    # the only advantage to doing it this way, is the .load method returns a set containing
    #  the fingerprints of all keys and subkeys that were loaded this time
    kr = pgpy.PGPKeyring()
    loaded = kr.load(glob.glob(os.path.expanduser('~/.gnupg/*ring.gpg')))

Key Operations
--------------

Once you have one or more keys generated or loaded, there are some things you may need or want to do before they can be used.

Passphrase Protecting Secret Keys
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

It is usually recommended to passphrase-protect private keys. Adding a passphrase to a key is simple::

    # key.is_public is False
    # key.is_protected is False
    key.protect("C0rrectPassphr@se", SymmetricKeyAlgorithm.AES256, HashAlgorithm.SHA256)
    # key.is_protected is now True

Unlocking Protected Secret Keys
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

If you have a key that is protected with a passphrase, you will need to unlock it first. PGPy handles this using
a context manager block, which also removes the unprotected key material from the object once execution exits that block.

Key unlocking is quite simple::

    # enc_key.is_public is False
    # enc_key.is_protected is True
    # enc_key.is_unlocked is False
    # Note that this context manager yields self, so while you can supply `as cvar`, it isn't strictly required
    # If the passphrase given is incorrect, this will raise PGPDecryptionError
    with enc_key.unlock("C0rrectPassphr@se"):
        # enc_key.is_unlocked is now True
        ...

    # This form works equivalently, but may be more semantically clear in some cases:
    with enc_key.unlock("C0rrectPassphr@se") as ukey:
        # ukey is just a reference to enc_key in this case
        ...

Exporting Keys
^^^^^^^^^^^^^^

Keys can be exported in OpenPGP compliant binary or ASCII-armored formats.

In Python 3::

    # binary
    keybytes = bytes(key)

    # ASCII armored private key
    keystr = str(key)
    
    # ASCII armored public key
    keystr = str(key.pubkey)

in Python 2::

    # binary
    keybytes = key.__bytes__()

    # ASCII armored
    keystr = str(key)