From fa1a4e0b25faf995ff1fbadd54917de23da59ebd Mon Sep 17 00:00:00 2001 From: Ben McGinnes Date: Thu, 28 Jun 2018 18:33:51 +1000 Subject: [PATCH] docs: python bindings howto * Updated official doc (the org-mode file) with the instructions on importing and exporting both public and secret keys. --- lang/python/docs/GPGMEpythonHOWTOen.org | 358 ++++++++++++++++++++++++ 1 file changed, 358 insertions(+) diff --git a/lang/python/docs/GPGMEpythonHOWTOen.org b/lang/python/docs/GPGMEpythonHOWTOen.org index 3325c086..6a3f9db0 100644 --- a/lang/python/docs/GPGMEpythonHOWTOen.org +++ b/lang/python/docs/GPGMEpythonHOWTOen.org @@ -454,6 +454,364 @@ 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. + + The following example retrieves one or more keys 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. + + #+begin_src python + import gpg + import os.path + import requests + + c = gpg.Context() + url = "https://sks-keyservers.net/pks/lookup" + pattern = input("Enter the pattern to search for key or user IDs: ") + payload = { "op": "get", "search": pattern } + + r = requests.get(url, verify=True, params=payload) + result = c.key_import(r.content) + + if result is not None and hasattr(result, "considered") is False: + print(result) + elif result is not None and hasattr(result, "considered") is True: + num_keys = len(result.imports) + new_revs = result.new_revocations + new_sigs = result.new_signatures + new_subs = result.new_sub_keys + new_uids = result.new_user_ids + new_scrt = result.secret_imported + nochange = result.unchanged + print(""" + The total number of keys considered for import was: {0} + + Number of keys revoked: {1} + Number of new signatures: {2} + Number of new subkeys: {3} + Number of new user IDs: {4} + Number of new secret keys: {5} + Number of unchanged keys: {6} + + The key IDs for all considered keys were: + """.format(num_keys, new_revs, new_sigs, new_subs, new_uids, new_scrt, + nochange)) + for i in range(num_keys): + print(result.imports[i].fpr) + print("") + else: + pass + #+end_src + + *NOTE:* When searching for a key ID of any length or a fingerprint + (without spaces), the SKS servers require the the leading =0x= + indicative of hexadecimal be included. Also note that the old short + key IDs (e.g. =0xDEADBEEF=) should no longer be used due to the + relative ease by which such key IDs can be reproduced, as + demonstrated by the Evil32 Project in 2014 (which was subsequently + exploited in 2016). + + +** Exporting keys + :PROPERTIES: + :CUSTOM_ID: howto-export-key + :END: + + Exporting keys remains a reasonably simple task, but has been + separated into three different functions for the OpenPGP + cryptographic engine. Two of those functions are for exporting + public keys and the third is for exporting secret keys. + + +*** Exporting public keys + :PROPERTIES: + :CUSTOM_ID: howto-export-public-key + :END: + + There are two methods of exporting public keys, both of which are + very similar to the other. The default method, =key_export()=, + will export a public key or keys matching a specified pattern as + normal. The alternative, the =key_export_minimal()= method, will + do the same thing except producing a minimised output with extra + signatures and third party signatures or certifications removed. + + #+begin_src python + import gpg + import os.path + import sys + + print(""" + This script exports one or more public keys. + """) + + c = gpg.Context(armor=True) + + if len(sys.argv) >= 4: + keyfile = sys.argv[1] + logrus = sys.argv[2] + homedir = sys.argv[3] + elif len(sys.argv) == 3: + keyfile = sys.argv[1] + logrus = sys.argv[2] + homedir = input("Enter the GPG configuration directory path (optional): ") + elif len(sys.argv) == 2: + keyfile = sys.argv[1] + logrus = input("Enter the UID matching the key(s) to export: ") + homedir = input("Enter the GPG configuration directory path (optional): ") + else: + keyfile = input("Enter the path and filename to save the secret key to: ") + logrus = input("Enter the UID matching the key(s) to export: ") + homedir = input("Enter the GPG configuration directory path (optional): ") + + 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 + + try: + result = c.key_export(pattern=logrus) + except: + result = c.key_export(pattern=None) + + if result is not None: + with open(keyfile, "wb") as f: + f.write(result) + else: + pass + #+end_src + + It is important to note that the result will only return =None= + when a pattern has been entered for =logrus=, but it has not + matched any keys. When the search pattern itself is set to =None= + this triggers the exporting of the entire public keybox. + + #+begin_src python + import gpg + import os.path + import sys + + print(""" + This script exports one or more public keys in minimised form. + """) + + c = gpg.Context(armor=True) + + if len(sys.argv) >= 4: + keyfile = sys.argv[1] + logrus = sys.argv[2] + homedir = sys.argv[3] + elif len(sys.argv) == 3: + keyfile = sys.argv[1] + logrus = sys.argv[2] + homedir = input("Enter the GPG configuration directory path (optional): ") + elif len(sys.argv) == 2: + keyfile = sys.argv[1] + logrus = input("Enter the UID matching the key(s) to export: ") + homedir = input("Enter the GPG configuration directory path (optional): ") + else: + keyfile = input("Enter the path and filename to save the secret key to: ") + logrus = input("Enter the UID matching the key(s) to export: ") + homedir = input("Enter the GPG configuration directory path (optional): ") + + 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 + + try: + result = c.key_export_minimal(pattern=logrus) + except: + result = c.key_export_minimal(pattern=None) + + if result is not None: + with open(keyfile, "wb") as f: + f.write(result) + else: + pass + #+end_src + + +*** Exporting secret keys + :PROPERTIES: + :CUSTOM_ID: howto-export-secret-key + :END: + + Exporting secret keys is, functionally, very similar to exporting + public keys; save for the invocation of =pinentry= via =gpg-agent= + in order to securely enter the key's passphrase and authorise the + export. + + The following example exports the secret key to a file which is + then set with the same permissions as the output files created by + the command line secret key export options. + + #+begin_src python + import gpg + import os + import os.path + import sys + + print(""" + This script exports one or more secret keys. + + The gpg-agent and pinentry are invoked to authorise the export. + """) + + c = gpg.Context(armor=True) + + if len(sys.argv) >= 4: + keyfile = sys.argv[1] + logrus = sys.argv[2] + homedir = sys.argv[3] + elif len(sys.argv) == 3: + keyfile = sys.argv[1] + logrus = sys.argv[2] + homedir = input("Enter the GPG configuration directory path (optional): ") + elif len(sys.argv) == 2: + keyfile = sys.argv[1] + logrus = input("Enter the UID matching the secret key(s) to export: ") + homedir = input("Enter the GPG configuration directory path (optional): ") + else: + keyfile = input("Enter the path and filename to save the secret key to: ") + logrus = input("Enter the UID matching the secret key(s) to export: ") + homedir = input("Enter the GPG configuration directory path (optional): ") + + 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 + + try: + result = c.key_export_secret(pattern=logrus) + except: + result = c.key_export_secret(pattern=None) + + if result is not None: + with open(keyfile, "wb") as f: + f.write(result) + os.chmod(keyfile, 0o600) + else: + pass + #+end_src + + Alternatively the approach of the following script can be + used. This longer example saves the exported secret key(s) in + files in the GnuPG home directory, in addition to setting the file + permissions as only readable and writable by the user. It also + exports the secret key(s) twice in order to output both GPG binary + (=.gpg=) and ASCII armoured (=.asc=) files. + + #+begin_src python + import gpg + import os + import os.path + import subprocess + import sys + + print(""" + This script exports one or more secret keys as both ASCII armored and binary + file formats, saved in files within the user's GPG home directory. + + The gpg-agent and pinentry are invoked to authorise the export. + """) + + if sys.platform == "win32": + gpgconfcmd = "gpgconf.exe --list-dirs homedir" + else: + gpgconfcmd = "gpgconf --list-dirs homedir" + + a = gpg.Context(armor=True) + b = gpg.Context() + c = gpg.Context() + + if len(sys.argv) >= 4: + keyfile = sys.argv[1] + logrus = sys.argv[2] + homedir = sys.argv[3] + elif len(sys.argv) == 3: + keyfile = sys.argv[1] + logrus = sys.argv[2] + homedir = input("Enter the GPG configuration directory path (optional): ") + elif len(sys.argv) == 2: + keyfile = sys.argv[1] + logrus = input("Enter the UID matching the secret key(s) to export: ") + homedir = input("Enter the GPG configuration directory path (optional): ") + else: + keyfile = input("Enter the filename to save the secret key to: ") + logrus = input("Enter the UID matching the secret key(s) to export: ") + homedir = input("Enter the GPG configuration directory path (optional): ") + + 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 + + if c.home_dir is not None: + if c.home_dir.endswith("/"): + gpgfile = "{0}{1}.gpg".format(c.home_dir, keyfile) + ascfile = "{0}{1}.asc".format(c.home_dir, keyfile) + else: + gpgfile = "{0}/{1}.gpg".format(c.home_dir, keyfile) + ascfile = "{0}/{1}.asc".format(c.home_dir, keyfile) + else: + if os.path.exists(os.environ["GNUPGHOME"]) is True: + hd = os.environ["GNUPGHOME"] + else: + hd = subprocess.getoutput(gpgconfcmd) + gpgfile = "{0}/{1}.gpg".format(hd, keyfile) + ascfile = "{0}/{1}.asc".format(hd, keyfile) + + try: + a_result = a.key_export_secret(pattern=logrus) + b_result = b.key_export_secret(pattern=logrus) + except: + a_result = a.key_export_secret(pattern=None) + b_result = b.key_export_secret(pattern=None) + + if a_result is not None: + with open(ascfile, "wb") as f: + f.write(a_result) + os.chmod(ascfile, 0o600) + else: + pass + + if b_result is not None: + with open(gpgfile, "wb") as f: + f.write(b_result) + os.chmod(gpgfile, 0o600) + else: + pass + #+end_src + + * Basic Functions :PROPERTIES: :CUSTOM_ID: howto-the-basics