python bindings: import keys

* Adapted from prior submissions by Tobias Mueller and Jacob Adams.
* key_import function added to gpg.core.Context().
* Two example scripts added to to examples/howto: import-key-file.py
  imports keys from a local file and import-keys.py accesses the SKS
  keyserver pool using the requests module to search for keys
  (includes check for key IDs which may not include the leading 0x).
* Added documentation demonstrating the use of the key_import()
  function with a large number of keys matching one domain (eff.org;
  the example shows how EFF staff are following their own advice
  issued last month).
This commit is contained in:
Ben McGinnes 2018-06-07 09:46:56 +10:00
parent 2c4c569247
commit 167847f1bc
4 changed files with 249 additions and 0 deletions

View File

@ -454,6 +454,85 @@
literals with the fingerprint when getting a key in this way. literals with the fingerprint when getting a key in this way.
** Importing keys
:PROPERTIES:
:CUSTOM_ID: howto-import-key
:END:
Importing keys is possible with the =key_import()= method and takes
one argument which is a bytes literal object containing either the
binary or ASCII armoured key data for one or more keys.
In the following example a key will be retrieved from the SKS
keyservers via the web using the requests module. Since requests
returns the content as a bytes literal object, we can then use that
directly to import the resulting data into our keybox. In order to
demonstrate multiple imports this example searches for all the keys
of users at a particular domain name; in this case the EFF.
#+begin_src python
import gpg
import os.path
import requests
c = gpg.Context()
homedir = input("Enter the GPG configuration directory path (optional): ")
pattern = input("The pattern to search for in key or user IDs: ")
url = "https://sks-keyservers.net/pks/lookup"
payload = { "op": "get", "search": pattern }
if homedir.startswith("~"):
if os.path.exists(os.path.expanduser(homedir)) is True:
c.home_dir = os.path.expanduser(homedir)
else:
pass
elif os.path.exists(homedir) is True:
c.home_dir = homedir
else:
pass
r = requests.get(url, verify=True, params=payload)
incoming = c.key_import(r.content)
summary = """
Total number of keys: {0}
Total number imported: {1}
Number of version 3 keys ignored: {2}
Number of imported key objects or updates: {3}
Number of unchanged keys: {4}
Number of new signatures: {5}
Number of revoked keys: {6}
""".format(incoming.considered, len(incoming.imports),
incoming.skipped_v3_keys, incoming.imported, incoming.unchanged,
incoming.new_signatures, incoming.new_revocations)
print(summary)
#+end_src
The resulting output in that case, where the search pattern entered
was =@eff.org= was:
#+begin_src shell
Total number of keys: 272
Total number imported: 249
Number of version 3 keys ignored: 23
Number of imported key objects or updates: 180
Number of unchanged keys: 66
Number of new signatures: 7
Number of revoked keys: 0
#+end_src
The examples for this document in =lang/python/examples/howto/ now
include to variations of this; one for searching the SKS keyserver
pool and the other for importing from a local file.
The example above and the corresponding executable script included
in the examples requires Kenneth Reitz's excellent [[http://docs.python-requests.org/en/master/][Requests module]].
* Basic Functions * Basic Functions
:PROPERTIES: :PROPERTIES:
:CUSTOM_ID: howto-the-basics :CUSTOM_ID: howto-the-basics

View File

@ -0,0 +1,70 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, unicode_literals
# Copyright (C) 2018 Ben McGinnes <ben@gnupg.org>
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation; either version 2.1 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU
# Lesser General Public Licensefor more details.
#
# You should have received a copy of the GNU General Public License and the GNU
# Lesser General Public along with this program; if not, see
# <http://www.gnu.org/licenses/>.
import gpg
import os.path
print("""
This script imports a key or keys matching a pattern from the SKS keyserver
pool.
""")
c = gpg.Context()
homedir = input("Enter the GPG configuration directory path (optional): ")
keyfile = input("Enter the path and filename to the file of key(s): ")
if homedir.startswith("~"):
if os.path.exists(os.path.expanduser(homedir)) is True:
c.home_dir = os.path.expanduser(homedir)
else:
pass
elif os.path.exists(homedir) is True:
c.home_dir = homedir
else:
pass
with open(keyfile, "rb") as f:
kdata = f.read()
incoming = c.key_import(kdata)
summary = """
Total number of keys: {0}
Total number imported: {1}
Number of version 3 keys ignored: {2}
Number of imported key objects or updates: {3}
Number of unchanged keys: {4}
Number of new signatures: {5}
Number of revoked keys: {6}
""".format(incoming.considered, len(incoming.imports),
incoming.skipped_v3_keys, incoming.imported, incoming.unchanged,
incoming.new_signatures, incoming.new_revocations)
print(summary)
# EOF

View File

@ -0,0 +1,78 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, unicode_literals
# Copyright (C) 2018 Ben McGinnes <ben@gnupg.org>
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation; either version 2.1 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU
# Lesser General Public Licensefor more details.
#
# You should have received a copy of the GNU General Public License and the GNU
# Lesser General Public along with this program; if not, see
# <http://www.gnu.org/licenses/>.
import gpg
import os.path
import requests
print("""
This script imports a key or keys matching a pattern from the SKS keyserver
pool.
Uses the requests module.
""")
c = gpg.Context()
homedir = input("Enter the GPG configuration directory path (optional): ")
pattern = input("The pattern to search for in key or user IDs: ")
url = "https://sks-keyservers.net/pks/lookup"
payload = { "op": "get", "search": pattern }
hexload = { "op": "get", "search": "0x{0}".format(pattern) }
if homedir.startswith("~"):
if os.path.exists(os.path.expanduser(homedir)) is True:
c.home_dir = os.path.expanduser(homedir)
else:
pass
elif os.path.exists(homedir) is True:
c.home_dir = homedir
else:
pass
resp = requests.get(url, verify=True, params=payload)
if resp.ok is False:
rhex = requests.get(url, verify=True, params=hexload)
incoming = c.key_import(rhex.content)
else:
incoming = c.key_import(resp.content)
summary = """
Total number of keys: {0}
Total number imported: {1}
Number of version 3 keys ignored: {2}
Number of imported key objects or updates: {3}
Number of unchanged keys: {4}
Number of new signatures: {5}
Number of revoked keys: {6}
""".format(incoming.considered, len(incoming.imports),
incoming.skipped_v3_keys, incoming.imported, incoming.unchanged,
incoming.new_signatures, incoming.new_revocations)
print(summary)
# EOF

View File

@ -509,6 +509,28 @@ class Context(GpgmeWrapper):
return results return results
def key_import(self, keydata):
"""Importing keys
Arguments:
keydata -- Binary or ASCII armored key(s) to be imported
Returns
-- an object describing the results of keys imported or
updated
Raises:
GPGMEError -- as signaled by the underlying library
"""
if keydata is not None:
try:
self.op_import(keydata)
result = self.op_import_result()
except GPGMEError as e:
result = e
else:
result = "No keys found."
def keylist(self, pattern=None, secret=False, def keylist(self, pattern=None, secret=False,
mode=constants.keylist.mode.LOCAL, mode=constants.keylist.mode.LOCAL,
source=None): source=None):