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:
parent
2c4c569247
commit
167847f1bc
@ -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
|
||||||
|
70
lang/python/examples/howto/import-key-file.py
Executable file
70
lang/python/examples/howto/import-key-file.py
Executable 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
|
78
lang/python/examples/howto/import-keys.py
Executable file
78
lang/python/examples/howto/import-keys.py
Executable 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
|
@ -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):
|
||||||
|
Loading…
Reference in New Issue
Block a user