python bindings howto: dita version

* Drafts of instructions for exporting public and secret keys ready,
  along in addition to the code.
This commit is contained in:
Ben McGinnes 2018-06-28 18:02:43 +10:00
parent 34308fe1fc
commit dda54cc851
12 changed files with 873 additions and 13 deletions

View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<div>Copyright © Benjamin D. McGinnes, 2018<br />for the <a href="https://gnupg.org/" target="_blank">GnuPG Project</a></div>
</body>
</html>

View File

@ -58,6 +58,11 @@
<topicref id="key-count" href="howto/part03/key-counting.dita"/> <topicref id="key-count" href="howto/part03/key-counting.dita"/>
</topicref> </topicref>
<topicref id="get-key" href="howto/part03/get-key.dita"/> <topicref id="get-key" href="howto/part03/get-key.dita"/>
<topicref id="import-key" href="howto/part03/importing.dita"/>
<topicref id="export-key" href="howto/part03/exporting.dita">
<topicref id="export-key-pub" href="howto/part03/exporting-pubkeys.dita"/>
<topicref id="export-key-sec" href="howto/part03/exporting-seckeys.dita"/>
</topicref>
</part> </part>
<part id="part-4" href="howto/part-4.dita"> <part id="part-4" href="howto/part-4.dita">
<topicref id="basics" href="howto/part04/basic-functions.dita"/> <topicref id="basics" href="howto/part04/basic-functions.dita"/>
@ -87,4 +92,7 @@
<part id="part-6" href="howto/part-6.dita"> <part id="part-6" href="howto/part-6.dita">
<topicref id="groups" href="howto/part06/group-lines.dita"/> <topicref id="groups" href="howto/part06/group-lines.dita"/>
</part> </part>
<backmatter>
<mapref id="resources" href="resources.ditamap" processing-role="resource-only"/>
</backmatter>
</bookmap> </bookmap>

View File

@ -3,4 +3,5 @@
<map> <map>
<title>GPGME Python Bindings</title> <title>GPGME Python Bindings</title>
<mapref id="howto-map" href="gpgme-python-howto.ditamap"/> <mapref id="howto-map" href="gpgme-python-howto.ditamap"/>
<mapref id="resources" href="resources.ditamap" processing-role="resource-only"/>
</map> </map>

View File

@ -1,20 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="20.0"> <project version="20.1">
<meta> <meta>
<filters directoryPatterns="" filePatterns="gpgmePython.xpr" positiveFilePatterns="" showHiddenFiles="false"/> <filters directoryPatterns="" filePatterns="gpgmePython.xpr" positiveFilePatterns="" showHiddenFiles="false"/>
<options> <options>
<serialized version="20.0" xml:space="preserve"> <serialized version="20.1" xml:space="preserve">
<serializableOrderedMap> <serializableOrderedMap>
<entry> <entry>
<String>scenario.associations</String> <String>scenario.associations</String>
<scenarioAssociation-array> <scenarioAssociation-array>
<scenarioAssociation>
<field name="url">
<String>gpgme-python.ditamap</String>
</field>
<field name="scenarioIds">
<list>
<String>DITA Map WebHelp - TS/HC - GPGME</String>
</list>
</field>
<field name="scenarioTypes">
<list>
<String>DITAMAP</String>
</list>
</field>
</scenarioAssociation>
<scenarioAssociation> <scenarioAssociation>
<field name="url"> <field name="url">
<String>gpgme-python-howto.ditamap</String> <String>gpgme-python-howto.ditamap</String>
</field> </field>
<field name="scenarioIds"> <field name="scenarioIds">
<list> <list>
<String>gpgme-python-howto (WebHelp Responsive)</String> <String>DITA Map WebHelp - TS/HC - GPGME</String>
</list> </list>
</field> </field>
<field name="scenarioTypes"> <field name="scenarioTypes">
@ -28,6 +43,455 @@
<entry> <entry>
<String>scenarios</String> <String>scenarios</String>
<scenario-array> <scenario-array>
<ditaScenario>
<field name="useXEP">
<Boolean>false</Boolean>
</field>
<field name="useAntennaHouse">
<Boolean>false</Boolean>
</field>
<field name="baseDir">
<String>${cfd}</String>
</field>
<field name="outputDir">
<String>${cfd}/out/webhelp-${ddt}</String>
</field>
<field name="tempDir">
<String>${cfd}/temp/webhelp-${ddt}</String>
</field>
<field name="transtype">
<String>webhelp</String>
</field>
<field name="filters">
<ditavalFilters>
<field name="useDitavalFilePath">
<Boolean>false</Boolean>
</field>
<field name="useAppliedConditionSet">
<Boolean>true</Boolean>
</field>
<field name="appliedConditionSet">
<null/>
</field>
<field name="ditavalFilePath">
<null/>
</field>
<field name="simpleFiltersList">
<list/>
</field>
</ditavalFilters>
</field>
<field name="addOxygenJars">
<Boolean>true</Boolean>
</field>
<field name="skinCssFile">
<String>file:/usr/local/oXygenXML/custom/webhelp-skins/high-contrast-skin.css</String>
</field>
<field name="lastCustomSkinCssPath">
<String>file:/usr/local/oXygenXML/custom/webhelp-skins/high-contrast-skin.css</String>
</field>
<field name="webhelpResponsiveTemplate">
<null/>
</field>
<field name="publishingTemplateDataPO">
<null/>
</field>
<field name="additionalAntArgs">
<String></String>
</field>
<field name="buildTarget">
<String></String>
</field>
<field name="buildFilePath">
<String></String>
</field>
<field name="ditaParams">
<list>
<ditaParameter>
<field name="name">
<String>args.xhtml.classattr</String>
</field>
<field name="description">
<null/>
</field>
<field name="value">
<String>yes</String>
</field>
<field name="defaultValue">
<String>yes</String>
</field>
<field name="type">
<Integer>4</Integer>
</field>
<field name="possibleValues">
<String-array>
<String>yes</String>
<String>no</String>
<null/>
</String-array>
</field>
<field name="possibleValuesDescriptions">
<String-array>
<String></String>
<String></String>
</String-array>
</field>
</ditaParameter>
<ditaParameter>
<field name="name">
<String>webhelp.footer.include</String>
</field>
<field name="description">
<null/>
</field>
<field name="value">
<String>yes</String>
</field>
<field name="defaultValue">
<String>yes</String>
</field>
<field name="type">
<Integer>4</Integer>
</field>
<field name="possibleValues">
<String-array>
<String>yes</String>
<String>no</String>
<null/>
</String-array>
</field>
<field name="possibleValuesDescriptions">
<String-array>
<String>If the "webhelp.footer.file" parameter has a value, the content of that file is used as footer. If "webhelp.footer.file" has no value, the default Oxygen footer is inserted in each Webhelp page.</String>
<String>No footer is added to the Webhelp pages.</String>
</String-array>
</field>
</ditaParameter>
<ditaParameter>
<field name="name">
<String>webhelp.footer.file</String>
</field>
<field name="description">
<null/>
</field>
<field name="value">
<String>/Users/ben/dev/hgit/mine/gnupg/dita/gpgme/python/gpgme-python-howto-footer.xhtml</String>
</field>
<field name="defaultValue">
<null/>
</field>
<field name="type">
<Integer>2</Integer>
</field>
<field name="possibleValues">
<null/>
</field>
<field name="possibleValuesDescriptions">
<null/>
</field>
</ditaParameter>
<ditaParameter>
<field name="name">
<String>webhelp.copyright</String>
</field>
<field name="description">
<null/>
</field>
<field name="value">
<String>Copyright © Benjamin D. McGinnes, 2018</String>
</field>
<field name="defaultValue">
<null/>
</field>
<field name="type">
<Integer>0</Integer>
</field>
<field name="possibleValues">
<null/>
</field>
<field name="possibleValuesDescriptions">
<null/>
</field>
</ditaParameter>
</list>
</field>
<field name="jvmArgs">
<String>-Xmx384m</String>
</field>
<field name="useCustomJavaHome">
<Boolean>false</Boolean>
</field>
<field name="customJavaHomeDir">
<String></String>
</field>
<field name="useCustomANTHome">
<Boolean>false</Boolean>
</field>
<field name="customANTHomeDir">
<String></String>
</field>
<field name="workingDir">
<null/>
</field>
<field name="showConsoleAlways">
<Boolean>true</Boolean>
</field>
<field name="advancedOptionsMap">
<null/>
</field>
<field name="name">
<String>DITA Map WebHelp - TS/HC - GPGME</String>
</field>
<field name="baseURL">
<null/>
</field>
<field name="footerURL">
<null/>
</field>
<field name="fOPMethod">
<null/>
</field>
<field name="fOProcessorName">
<null/>
</field>
<field name="headerURL">
<null/>
</field>
<field name="inputXSLURL">
<null/>
</field>
<field name="inputXMLURL">
<null/>
</field>
<field name="defaultScenario">
<Boolean>false</Boolean>
</field>
<field name="isFOPPerforming">
<Boolean>false</Boolean>
</field>
<field name="type">
<String>DITAMAP</String>
</field>
<field name="saveAs">
<Boolean>true</Boolean>
</field>
<field name="openInBrowser">
<Boolean>true</Boolean>
</field>
<field name="outputFile">
<null/>
</field>
<field name="outputResource">
<null/>
</field>
<field name="openOtherLocationInBrowser">
<Boolean>false</Boolean>
</field>
<field name="locationToOpenInBrowserURL">
<String></String>
</field>
<field name="openInEditor">
<Boolean>false</Boolean>
</field>
<field name="showInHTMLPane">
<Boolean>false</Boolean>
</field>
<field name="showInXMLPane">
<Boolean>false</Boolean>
</field>
<field name="showInSVGPane">
<Boolean>false</Boolean>
</field>
<field name="showInResultSetPane">
<Boolean>false</Boolean>
</field>
<field name="useXSLTInput">
<Boolean>false</Boolean>
</field>
<field name="xsltParams">
<list/>
</field>
<field name="cascadingStylesheets">
<String-array/>
</field>
<field name="xslTransformer">
<String>DITA-OT</String>
</field>
<field name="extensionURLs">
<String-array/>
</field>
</ditaScenario>
<ditaScenario>
<field name="useXEP">
<Boolean>false</Boolean>
</field>
<field name="useAntennaHouse">
<Boolean>false</Boolean>
</field>
<field name="baseDir">
<String>${cfd}</String>
</field>
<field name="outputDir">
<String>${cfd}/out/html5-${ddt}</String>
</field>
<field name="tempDir">
<String>${cfd}/temp/html5-${ddt}</String>
</field>
<field name="transtype">
<String>html5</String>
</field>
<field name="filters">
<ditavalFilters>
<field name="useDitavalFilePath">
<Boolean>false</Boolean>
</field>
<field name="useAppliedConditionSet">
<Boolean>true</Boolean>
</field>
<field name="appliedConditionSet">
<null/>
</field>
<field name="ditavalFilePath">
<null/>
</field>
<field name="simpleFiltersList">
<list/>
</field>
</ditavalFilters>
</field>
<field name="addOxygenJars">
<Boolean>true</Boolean>
</field>
<field name="skinCssFile">
<String>file:/usr/local/oXygenXML/custom/webhelp-skins/high-contrast-skin.css</String>
</field>
<field name="lastCustomSkinCssPath">
<String>file:/usr/local/oXygenXML/custom/webhelp-skins/high-contrast-skin.css</String>
</field>
<field name="webhelpResponsiveTemplate">
<null/>
</field>
<field name="publishingTemplateDataPO">
<null/>
</field>
<field name="additionalAntArgs">
<String></String>
</field>
<field name="buildTarget">
<String></String>
</field>
<field name="buildFilePath">
<String></String>
</field>
<field name="ditaParams">
<list/>
</field>
<field name="jvmArgs">
<String>-Xmx384m</String>
</field>
<field name="useCustomJavaHome">
<Boolean>false</Boolean>
</field>
<field name="customJavaHomeDir">
<String></String>
</field>
<field name="useCustomANTHome">
<Boolean>false</Boolean>
</field>
<field name="customANTHomeDir">
<String></String>
</field>
<field name="workingDir">
<null/>
</field>
<field name="showConsoleAlways">
<Boolean>true</Boolean>
</field>
<field name="advancedOptionsMap">
<null/>
</field>
<field name="name">
<String>gpgme-python (HTML5)</String>
</field>
<field name="baseURL">
<null/>
</field>
<field name="footerURL">
<null/>
</field>
<field name="fOPMethod">
<null/>
</field>
<field name="fOProcessorName">
<null/>
</field>
<field name="headerURL">
<null/>
</field>
<field name="inputXSLURL">
<null/>
</field>
<field name="inputXMLURL">
<null/>
</field>
<field name="defaultScenario">
<Boolean>false</Boolean>
</field>
<field name="isFOPPerforming">
<Boolean>false</Boolean>
</field>
<field name="type">
<String>DITAMAP</String>
</field>
<field name="saveAs">
<Boolean>true</Boolean>
</field>
<field name="openInBrowser">
<Boolean>true</Boolean>
</field>
<field name="outputFile">
<null/>
</field>
<field name="outputResource">
<null/>
</field>
<field name="openOtherLocationInBrowser">
<Boolean>false</Boolean>
</field>
<field name="locationToOpenInBrowserURL">
<String></String>
</field>
<field name="openInEditor">
<Boolean>false</Boolean>
</field>
<field name="showInHTMLPane">
<Boolean>false</Boolean>
</field>
<field name="showInXMLPane">
<Boolean>false</Boolean>
</field>
<field name="showInSVGPane">
<Boolean>false</Boolean>
</field>
<field name="showInResultSetPane">
<Boolean>false</Boolean>
</field>
<field name="useXSLTInput">
<Boolean>false</Boolean>
</field>
<field name="xsltParams">
<list/>
</field>
<field name="cascadingStylesheets">
<String-array/>
</field>
<field name="xslTransformer">
<String>DITA-OT</String>
</field>
<field name="extensionURLs">
<String-array/>
</field>
</ditaScenario>
<ditaScenario> <ditaScenario>
<field name="useXEP"> <field name="useXEP">
<Boolean>false</Boolean> <Boolean>false</Boolean>

View File

@ -14,15 +14,16 @@
<ol id="ol_k3b_wrx_5db"> <ol id="ol_k3b_wrx_5db">
<li>A <xref href="https://dev.gnupg.org/T3977" format="html" scope="external">bug</xref> <li>A <xref href="https://dev.gnupg.org/T3977" format="html" scope="external">bug</xref>
in either Org-Mode or Babel prevented the more complex examples included in the HOWTO in either Org-Mode or Babel prevented the more complex examples included in the HOWTO
from displaying correctly.</li> from displaying correctly while also retaining syntax highlighting.</li>
<li>To demonstrate some of the advantages of DITA XML over existing documentation <li>To demonstrate some of the advantages of DITA XML over existing documentation
production software used in the project (particularly Texinfo and LaTeX).</li> production software used in the project (particularly Texinfo and LaTeX).</li>
</ol> </ol>
</p> </p>
<p>The XML format definitely supports displaying all the more complex Python code correctly, <p>The XML format definitely supports displaying all the more complex Python code correctly
as well as being designed to produce standards compliant print and HTML output. Whereas with syntax highlighting, as well as being designed to produce standards compliant print and
currently the existing tools utilised by the GnuPG Project can't display the example code in HTML output. Whereas currently the existing tools utilised by the GnuPG Project can't
a way which would actually pass the project's own git commit ruleset.</p> display the example code in a way which would actually pass the project's own git commit
ruleset.</p>
<p> </p> <p> </p>
</body> </body>
</topic> </topic>

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd">
<dita xml:lang="en-GB">
<topic id="exporting-pubkeys">
<title>Exporting Public Keys</title>
<body>
<p>There are two methods of exporting public keys, both of which are very similar to the
other. The default method, <codeph>key_export()</codeph> will export a public key or keys
matching a specified pattern as normal. The alternative, the
<codeph>key_export_minimal()</codeph> method will do the same thing except producing a
minimised output with extra signatures and third party signatures or certifications
removed.</p>
<p>
<codeblock id="export-pubkey-01" outputclass="language-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
</codeblock>
</p>
<p>It is important to note that the <codeph>result</codeph> will only return
<codeph>None</codeph> when a pattern has been entered for <varname>logrus</varname>, but
it has not matched any keys. When the search pattern itself is set to <codeph>None</codeph>
this triggers the exporting of the entire public keybox.</p>
<p>
<codeblock id="export-pubkey-02" outputclass="language-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
</codeblock>
</p>
<p/>
</body>
</topic>
</dita>

View File

@ -0,0 +1,161 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd">
<dita xml:lang="en-GB">
<topic id="exporting-seckeys">
<title>Exporting Secret Keys</title>
<body>
<p>Exporting secret keys is, functionally, very similar to exporting public keys; save for the
invocation of <cmdname>pinentry</cmdname> via <cmdname>gpg-agent</cmdname> in order to
securely enter the key's passphrase and authorise the export.</p>
<p>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.</p>
<p>
<codeblock id="export-seckey-01" outputclass="language-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
</codeblock>
</p>
<p>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 (<codeph>.gpg</codeph>) and ASCII armoured
(<codeph>.asc</codeph>) files.</p>
<p>
<codeblock id="export-seckey-02" outputclass="language-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
</codeblock>
</p>
<p/>
</body>
</topic>
</dita>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd">
<dita xml:lang="en-GB">
<topic id="exporting-keys">
<title>Exporting Keys</title>
<body>
<p>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.</p>
</body>
</topic>
</dita>

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd">
<dita xml:lang="en-GB">
<topic id="importing-keys">
<title>Importing keys</title>
<body>
<p>Importing keys is possible with the <codeph>key_import()</codeph> 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.</p>
<p>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.</p>
<p>
<codeblock id="key-import-01" outputclass="language-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
</codeblock>
</p>
<p>
<note>When searching for a key ID of any length or a fingerprint (without spaces), the SKS
servers require the the leading <codeph>0x</codeph> indicative of hexadecimal be included.
Also note that the old short key IDs (e.g. <codeph>0xDEADBEEF</codeph>) should no longer
be used due to the relative ease by which such key IDs can be reproduced, as demonstrated
by the <xref href="https://evil32.com/" format="html" scope="external">Evil32
Project</xref> in 2014 (which was subsequently exploited in 2016).</note>
</p>
<p/>
</body>
</topic>
</dita>

View File

@ -30,9 +30,20 @@ else:
pass pass
</codeblock> </codeblock>
</p> </p>
<p>The data available in <codeph>plaintext</codeph> in this example is the decrypted content <p>The data available in <codeph>plaintext</codeph> following a successful decryption in this
as a byte object, the recipient key IDs and algorithms in <codeph>result</codeph> and the example is the decrypted content as a byte object, the recipient key IDs and algorithms in
results of verifying any signatures of the data in <codeph>verify_result</codeph>.</p> <codeph>result</codeph> and the results of verifying any signatures of the data in
<codeph>verify_result</codeph>.</p>
<p>
<note>The graceful handling of <codeph>GPGMEError</codeph> with the try/except statement is
to handle the decryption error message produced if the file <codeph>ciphertext</codeph>,
and thus <codeph>cfile</codeph>, are encrypted with deprecated and insecure methods.
Particularly without MDC integrity checks or utilising deprecated encryption algorithms.
Messages and files encrypted with these are not decrypted with GPGME at all and any user
requiring archival access will need to access it manually with pre-GnuPG 2.3 versions of
the software which meets the requirements of the specific use case.</note>
</p>
<p/>
</body> </body>
</topic> </topic>
</dita> </dita>

View File

@ -44,6 +44,10 @@ for i in range(len(group_lists)):
<p> <p>
<codeblock id="groups-2" outputclass="language-python">from groups import group_lists</codeblock> <codeblock id="groups-2" outputclass="language-python">from groups import group_lists</codeblock>
</p> </p>
<p>A demonstration of using the <filepath>groups.py</filepath> module is also available in
the form of the executable <cmdname>mutt-groups.py</cmdname> script. This second script
reads all the group entries in a user's <filepath>gpg.conf</filepath> file and converts them
into crypt-hooks suitable for use with the Mutt and Neomutt mail clients.</p>
</body> </body>
</topic> </topic>
</dita> </dita>

View File

@ -4,10 +4,12 @@
<topic id="topic_vnz_nn2_vdb"> <topic id="topic_vnz_nn2_vdb">
<title>Documentation Version</title> <title>Documentation Version</title>
<body> <body>
<p><b>Version:</b> 0.1.1</p> <p><b>Version:</b> 0.1.2-DRAFT</p>
<p><b>Author:</b> Ben McGinnes &lt;<xref href="mailto:ben@gnupg.org" format="html" <p><b>Author:</b> Ben McGinnes &lt;<xref href="mailto:ben@gnupg.org" format="html"
scope="external">ben@gnupg.org</xref>></p> scope="external">ben@gnupg.org</xref>></p>
<p><b>Author GPG Key ID:</b> DB4724E6FA4286C92B4E55C4321E4E2373590E5D</p> <p><b>Author GPG Key ID:</b>
<xref href="http://files.au.adversary.org/crypto/ben-key.asc" format="text" scope="external"
>DB4724E6FA4286C92B4E55C4321E4E2373590E5D</xref></p>
<p><b>Language:</b> Australian English, British English</p> <p><b>Language:</b> Australian English, British English</p>
</body> </body>
</topic> </topic>