From 167847f1bcfb1b573bd40fe897f39d4ee4167176 Mon Sep 17 00:00:00 2001 From: Ben McGinnes Date: Thu, 7 Jun 2018 09:46:56 +1000 Subject: 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). --- lang/python/docs/GPGMEpythonHOWTOen.org | 79 +++++++++++++++++++++++++++ lang/python/examples/howto/import-key-file.py | 70 ++++++++++++++++++++++++ lang/python/examples/howto/import-keys.py | 78 ++++++++++++++++++++++++++ lang/python/src/core.py | 22 ++++++++ 4 files changed, 249 insertions(+) create mode 100755 lang/python/examples/howto/import-key-file.py create mode 100755 lang/python/examples/howto/import-keys.py diff --git a/lang/python/docs/GPGMEpythonHOWTOen.org b/lang/python/docs/GPGMEpythonHOWTOen.org index 3325c086..31929fa3 100644 --- a/lang/python/docs/GPGMEpythonHOWTOen.org +++ b/lang/python/docs/GPGMEpythonHOWTOen.org @@ -454,6 +454,85 @@ 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 :PROPERTIES: :CUSTOM_ID: howto-the-basics diff --git a/lang/python/examples/howto/import-key-file.py b/lang/python/examples/howto/import-key-file.py new file mode 100755 index 00000000..26ec5242 --- /dev/null +++ b/lang/python/examples/howto/import-key-file.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, unicode_literals + +# Copyright (C) 2018 Ben McGinnes +# +# 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 +# . + +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 diff --git a/lang/python/examples/howto/import-keys.py b/lang/python/examples/howto/import-keys.py new file mode 100755 index 00000000..455b8e0c --- /dev/null +++ b/lang/python/examples/howto/import-keys.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, unicode_literals + +# Copyright (C) 2018 Ben McGinnes +# +# 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 +# . + +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 diff --git a/lang/python/src/core.py b/lang/python/src/core.py index bd95d231..10db3d69 100644 --- a/lang/python/src/core.py +++ b/lang/python/src/core.py @@ -509,6 +509,28 @@ class Context(GpgmeWrapper): 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, mode=constants.keylist.mode.LOCAL, source=None): -- cgit v1.2.1