doc: python bindings howto
* deconstructing multi-recipient encryption.
This commit is contained in:
parent
64c5886132
commit
1779d7b9d6
@ -22,6 +22,7 @@
|
|||||||
This document provides basic instruction in how to use the GPGME
|
This document provides basic instruction in how to use the GPGME
|
||||||
Python bindings to programmatically leverage the GPGME library.
|
Python bindings to programmatically leverage the GPGME library.
|
||||||
|
|
||||||
|
|
||||||
** Python 2 versus Python 3
|
** Python 2 versus Python 3
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:CUSTOM_ID: py2-vs-py3
|
:CUSTOM_ID: py2-vs-py3
|
||||||
@ -53,6 +54,7 @@
|
|||||||
:CUSTOM_ID: gpgme-concepts
|
:CUSTOM_ID: gpgme-concepts
|
||||||
:END:
|
:END:
|
||||||
|
|
||||||
|
|
||||||
** A C API
|
** A C API
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:CUSTOM_ID: gpgme-c-api
|
:CUSTOM_ID: gpgme-c-api
|
||||||
@ -72,6 +74,7 @@
|
|||||||
languages. This is where the need for bindings in various
|
languages. This is where the need for bindings in various
|
||||||
languages stems.
|
languages stems.
|
||||||
|
|
||||||
|
|
||||||
** Python bindings
|
** Python bindings
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:CUSTOM_ID: gpgme-python-bindings
|
:CUSTOM_ID: gpgme-python-bindings
|
||||||
@ -88,6 +91,7 @@
|
|||||||
tied to the exact same version of GPGME used to generate that copy
|
tied to the exact same version of GPGME used to generate that copy
|
||||||
of =gpgme.h=.
|
of =gpgme.h=.
|
||||||
|
|
||||||
|
|
||||||
** Difference between the Python bindings and other GnuPG Python packages
|
** Difference between the Python bindings and other GnuPG Python packages
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:CUSTOM_ID: gpgme-python-bindings-diffs
|
:CUSTOM_ID: gpgme-python-bindings-diffs
|
||||||
@ -97,6 +101,7 @@
|
|||||||
over the years. Some of the most well known are listed here, along
|
over the years. Some of the most well known are listed here, along
|
||||||
with what differentiates them.
|
with what differentiates them.
|
||||||
|
|
||||||
|
|
||||||
*** The python-gnupg package maintained by Vinay Sajip
|
*** The python-gnupg package maintained by Vinay Sajip
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:CUSTOM_ID: diffs-python-gnupg
|
:CUSTOM_ID: diffs-python-gnupg
|
||||||
@ -116,6 +121,7 @@
|
|||||||
|
|
||||||
The python-gnupg package is available under the MIT license.
|
The python-gnupg package is available under the MIT license.
|
||||||
|
|
||||||
|
|
||||||
*** The gnupg package created and maintained by Isis Lovecruft
|
*** The gnupg package created and maintained by Isis Lovecruft
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:CUSTOM_ID: diffs-isis-gnupg
|
:CUSTOM_ID: diffs-isis-gnupg
|
||||||
@ -136,6 +142,7 @@
|
|||||||
The gnupg package is available under the GNU General Public
|
The gnupg package is available under the GNU General Public
|
||||||
License version 3.0 (or later).
|
License version 3.0 (or later).
|
||||||
|
|
||||||
|
|
||||||
*** The PyME package maintained by Martin Albrecht
|
*** The PyME package maintained by Martin Albrecht
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:CUSTOM_ID: diffs-pyme
|
:CUSTOM_ID: diffs-pyme
|
||||||
@ -168,6 +175,7 @@
|
|||||||
:CUSTOM_ID: gpgme-python-install
|
:CUSTOM_ID: gpgme-python-install
|
||||||
:END:
|
:END:
|
||||||
|
|
||||||
|
|
||||||
** No PyPI
|
** No PyPI
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:CUSTOM_ID: do-not-use-pypi
|
:CUSTOM_ID: do-not-use-pypi
|
||||||
@ -187,6 +195,7 @@
|
|||||||
bundled with it or a full implementation of C for each
|
bundled with it or a full implementation of C for each
|
||||||
architecture.
|
architecture.
|
||||||
|
|
||||||
|
|
||||||
** Requirements
|
** Requirements
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:CUSTOM_ID: gpgme-python-requirements
|
:CUSTOM_ID: gpgme-python-requirements
|
||||||
@ -201,6 +210,7 @@
|
|||||||
3. GPGME itself. Which also means that all of GPGME's dependencies
|
3. GPGME itself. Which also means that all of GPGME's dependencies
|
||||||
must be installed too.
|
must be installed too.
|
||||||
|
|
||||||
|
|
||||||
** Installation
|
** Installation
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:CUSTOM_ID: installation
|
:CUSTOM_ID: installation
|
||||||
@ -225,6 +235,7 @@
|
|||||||
For Python 3 it checks for these executables in this order:
|
For Python 3 it checks for these executables in this order:
|
||||||
=python3=, =python3.6=, =python3.5= and =python3.4=.
|
=python3=, =python3.6=, =python3.5= and =python3.4=.
|
||||||
|
|
||||||
|
|
||||||
*** Installing GPGME
|
*** Installing GPGME
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:CUSTOM_ID: install-gpgme
|
:CUSTOM_ID: install-gpgme
|
||||||
@ -243,6 +254,7 @@
|
|||||||
regarding GPGME's design which hold true whether you're dealing with
|
regarding GPGME's design which hold true whether you're dealing with
|
||||||
the C code directly or these Python bindings.
|
the C code directly or these Python bindings.
|
||||||
|
|
||||||
|
|
||||||
** No REST
|
** No REST
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:CUSTOM_ID: no-rest-for-the-wicked
|
:CUSTOM_ID: no-rest-for-the-wicked
|
||||||
@ -266,6 +278,7 @@
|
|||||||
direct bindings and it's this pythonic layer with which this HOWTO
|
direct bindings and it's this pythonic layer with which this HOWTO
|
||||||
deals with.
|
deals with.
|
||||||
|
|
||||||
|
|
||||||
** Context
|
** Context
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:CUSTOM_ID: howto-get-context
|
:CUSTOM_ID: howto-get-context
|
||||||
@ -294,6 +307,7 @@
|
|||||||
:CUSTOM_ID: howto-keys
|
:CUSTOM_ID: howto-keys
|
||||||
:END:
|
:END:
|
||||||
|
|
||||||
|
|
||||||
** Key selection
|
** Key selection
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:CUSTOM_ID: howto-keys-selection
|
:CUSTOM_ID: howto-keys-selection
|
||||||
@ -560,10 +574,11 @@
|
|||||||
if rpattern[i].can_encrypt == 1:
|
if rpattern[i].can_encrypt == 1:
|
||||||
logrus.append(rpattern[i])
|
logrus.append(rpattern[i])
|
||||||
|
|
||||||
cipher = c.encrypt(text, recipients=logrus, sign=False, always_trust=True)
|
ciphertext, result, sign_result = c.encrypt(text, recipients=logrus, sign=False,
|
||||||
|
always_trust=True)
|
||||||
|
|
||||||
with open("secret_plans.txt.asc", "wb") as afile:
|
with open("secret_plans.txt.asc", "wb") as afile:
|
||||||
afile.write(cipher[0])
|
afile.write(ciphertext)
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
All it would take to change the above example to sign the message
|
All it would take to change the above example to sign the message
|
||||||
@ -571,8 +586,9 @@
|
|||||||
be to change the =c.encrypt= line to this:
|
be to change the =c.encrypt= line to this:
|
||||||
|
|
||||||
#+begin_src python
|
#+begin_src python
|
||||||
cipher = c.encrypt(text, recipients=logrus, always_trust=True,
|
ciphertext, result, sign_result = c.encrypt(text, recipients=logrus,
|
||||||
add_encrypt_to=True)
|
always_trust=True,
|
||||||
|
add_encrypt_to=True)
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
The only keyword arguments requiring modification are those for
|
The only keyword arguments requiring modification are those for
|
||||||
@ -600,7 +616,7 @@
|
|||||||
logrus.append(rpattern[i])
|
logrus.append(rpattern[i])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cipher = c.encrypt(text, recipients=logrus, add_encrypt_to=True)
|
ciphertext, result, sign_result = c.encrypt(text, recipients=logrus, add_encrypt_to=True)
|
||||||
except gpg.errors.InvalidRecipients as e:
|
except gpg.errors.InvalidRecipients as e:
|
||||||
for i in range(len(e.recipients)):
|
for i in range(len(e.recipients)):
|
||||||
for n in range(len(logrus)):
|
for n in range(len(logrus)):
|
||||||
@ -609,12 +625,12 @@
|
|||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
cipher = c.encrypt(text, recipients=logrus, add_encrypt_to=True)
|
ciphertext, result, sign_result = c.encrypt(text, recipients=logrus, add_encrypt_to=True)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
with open("secret_plans.txt.asc", "wb") as afile:
|
with open("secret_plans.txt.asc", "wb") as afile:
|
||||||
afile.write(cipher[0])
|
afile.write(ciphertext)
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
This will attempt to encrypt to all the keys searched for, then
|
This will attempt to encrypt to all the keys searched for, then
|
||||||
@ -721,7 +737,7 @@
|
|||||||
text = text0.encode()
|
text = text0.encode()
|
||||||
|
|
||||||
c = gpg.Context(armor=True, signers=sig_src)
|
c = gpg.Context(armor=True, signers=sig_src)
|
||||||
signed = c.sign(text, mode=0)
|
signed = c.sign(text, mode=gpg.constants.sig.mode.NORMAL)
|
||||||
|
|
||||||
with open("/path/to/statement.txt.asc", "w") as afile:
|
with open("/path/to/statement.txt.asc", "w") as afile:
|
||||||
for line in signed[0]:
|
for line in signed[0]:
|
||||||
@ -740,7 +756,7 @@
|
|||||||
text = tfile.read()
|
text = tfile.read()
|
||||||
|
|
||||||
c = gpg.Context()
|
c = gpg.Context()
|
||||||
signed = c.sign(text, mode=0)
|
signed = c.sign(text, mode=gpg.constants.sig.mode.NORMAL)
|
||||||
|
|
||||||
with open("/path/to/statement.txt.sig", "wb") as afile:
|
with open("/path/to/statement.txt.sig", "wb") as afile:
|
||||||
afile.write(signed[0])
|
afile.write(signed[0])
|
||||||
@ -766,7 +782,7 @@
|
|||||||
text = text0.encode()
|
text = text0.encode()
|
||||||
|
|
||||||
c = gpg.Context(armor=True)
|
c = gpg.Context(armor=True)
|
||||||
signed = c.sign(text, mode=1)
|
signed = c.sign(text, mode=gpg.constants.sig.mode.DETACH)
|
||||||
|
|
||||||
with open("/path/to/statement.txt.asc", "w") as afile:
|
with open("/path/to/statement.txt.asc", "w") as afile:
|
||||||
for line in signed[0].splitlines():
|
for line in signed[0].splitlines():
|
||||||
@ -783,7 +799,7 @@
|
|||||||
text = tfile.read()
|
text = tfile.read()
|
||||||
|
|
||||||
c = gpg.Context(signers=sig_src)
|
c = gpg.Context(signers=sig_src)
|
||||||
signed = c.sign(text, mode=1)
|
signed = c.sign(text, mode=gpg.constants.sig.mode.DETACH)
|
||||||
|
|
||||||
with open("/path/to/statement.txt.sig", "wb") as afile:
|
with open("/path/to/statement.txt.sig", "wb") as afile:
|
||||||
afile.write(signed[0])
|
afile.write(signed[0])
|
||||||
@ -809,7 +825,7 @@
|
|||||||
text = text0.encode()
|
text = text0.encode()
|
||||||
|
|
||||||
c = gpg.Context()
|
c = gpg.Context()
|
||||||
signed = c.sign(text, mode=2)
|
signed = c.sign(text, mode=gpg.constants.sig.mode.CLEAR)
|
||||||
|
|
||||||
with open("/path/to/statement.txt.asc", "w") as afile:
|
with open("/path/to/statement.txt.asc", "w") as afile:
|
||||||
for line in signed[0].splitlines():
|
for line in signed[0].splitlines():
|
||||||
@ -826,7 +842,7 @@
|
|||||||
text = tfile.read()
|
text = tfile.read()
|
||||||
|
|
||||||
c = gpg.Context()
|
c = gpg.Context()
|
||||||
signed = c.sign(text, mode=2)
|
signed = c.sign(text, mode=gpg.constants.sig.mode.CLEAR)
|
||||||
|
|
||||||
with open("/path/to/statement.txt.asc", "wb") as afile:
|
with open("/path/to/statement.txt.asc", "wb") as afile:
|
||||||
afile.write(signed[0])
|
afile.write(signed[0])
|
||||||
@ -856,14 +872,15 @@
|
|||||||
c = gpg.Context()
|
c = gpg.Context()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
verified = c.verify(open(gpg_file))
|
data, result = c.verify(open(gpg_file))
|
||||||
|
verified = True
|
||||||
except gpg.errors.BadSignatures as e:
|
except gpg.errors.BadSignatures as e:
|
||||||
verified = None
|
verified = False
|
||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
if verified is not None:
|
if verified is True:
|
||||||
for i in range(len(verified[1].signatures)):
|
for i in range(len(result.signatures)):
|
||||||
sign = verified[1].signatures[i]
|
sign = result.signatures[i]
|
||||||
print("""Good signature from:
|
print("""Good signature from:
|
||||||
{0}
|
{0}
|
||||||
with key {1}
|
with key {1}
|
||||||
@ -887,14 +904,15 @@
|
|||||||
c = gpg.Context()
|
c = gpg.Context()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
verified = c.verify(open(asc_file))
|
data, result = c.verify(open(asc_file))
|
||||||
|
verified = True
|
||||||
except gpg.errors.BadSignatures as e:
|
except gpg.errors.BadSignatures as e:
|
||||||
verified = None
|
verified = False
|
||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
if verified is not None:
|
if verified is True:
|
||||||
for i in range(len(verified[1].signatures)):
|
for i in range(len(result.signatures)):
|
||||||
sign = verified[1].signatures[i]
|
sign = result.signatures[i]
|
||||||
print("""Good signature from:
|
print("""Good signature from:
|
||||||
{0}
|
{0}
|
||||||
with key {1}
|
with key {1}
|
||||||
@ -906,15 +924,14 @@
|
|||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
In both of the previous examples it is also possible to compare the
|
In both of the previous examples it is also possible to compare the
|
||||||
original data that was signed against the signed data in
|
original data that was signed against the signed data in =data= to
|
||||||
=verified[0]= to see if it matches with something like this:
|
see if it matches with something like this:
|
||||||
|
|
||||||
#+begin_src python
|
#+begin_src python
|
||||||
afile = open(filename, "rb")
|
with open(filename, "rb") as afile:
|
||||||
text = afile.read()
|
text = afile.read()
|
||||||
afile.close()
|
|
||||||
|
|
||||||
if text == verified[0]:
|
if text == data:
|
||||||
print("Good signature.")
|
print("Good signature.")
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
@ -923,8 +940,8 @@
|
|||||||
The following two examples, however, deal with detached signatures.
|
The following two examples, however, deal with detached signatures.
|
||||||
With his method of verification the data that was signed does not
|
With his method of verification the data that was signed does not
|
||||||
get returned since it is already being explicitly referenced in the
|
get returned since it is already being explicitly referenced in the
|
||||||
first argument of =c.verify=. So =verified[0]= is None and only
|
first argument of =c.verify=. So =data= is =None= and only the
|
||||||
the data in =verified[1]= is available.
|
information in =result= is available.
|
||||||
|
|
||||||
#+begin_src python
|
#+begin_src python
|
||||||
import gpg
|
import gpg
|
||||||
@ -936,14 +953,15 @@
|
|||||||
c = gpg.Context()
|
c = gpg.Context()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
verified = c.verify(open(filename), open(sig_file))
|
data, result = c.verify(open(filename), open(sig_file))
|
||||||
|
verified = True
|
||||||
except gpg.errors.BadSignatures as e:
|
except gpg.errors.BadSignatures as e:
|
||||||
verified = None
|
verified = False
|
||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
if verified is not None:
|
if verified is True:
|
||||||
for i in range(len(verified[1].signatures)):
|
for i in range(len(result.signatures)):
|
||||||
sign = verified[1].signatures[i]
|
sign = result.signatures[i]
|
||||||
print("""Good signature from:
|
print("""Good signature from:
|
||||||
{0}
|
{0}
|
||||||
with key {1}
|
with key {1}
|
||||||
@ -964,14 +982,15 @@
|
|||||||
c = gpg.Context()
|
c = gpg.Context()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
verified = c.verify(open(filename), open(asc_file))
|
data, result = c.verify(open(filename), open(asc_file))
|
||||||
|
verified = True
|
||||||
except gpg.errors.BadSignatures as e:
|
except gpg.errors.BadSignatures as e:
|
||||||
verified = None
|
verified = False
|
||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
if verified is not None:
|
if verified is not None:
|
||||||
for i in range(len(verified[1].signatures)):
|
for i in range(len(result.signatures)):
|
||||||
sign = verified[1].signatures[i]
|
sign = result.signatures[i]
|
||||||
print("""Good signature from:
|
print("""Good signature from:
|
||||||
{0}
|
{0}
|
||||||
with key {1}
|
with key {1}
|
||||||
@ -1008,11 +1027,8 @@
|
|||||||
allow-secret-key-import
|
allow-secret-key-import
|
||||||
trust-model tofu+pgp
|
trust-model tofu+pgp
|
||||||
tofu-default-policy unknown
|
tofu-default-policy unknown
|
||||||
# no-auto-check-trustdb
|
|
||||||
enable-large-rsa
|
enable-large-rsa
|
||||||
enable-dsa2
|
enable-dsa2
|
||||||
# no-emit-version
|
|
||||||
# no-comments
|
|
||||||
# cert-digest-algo SHA256
|
# cert-digest-algo SHA256
|
||||||
cert-digest-algo SHA512
|
cert-digest-algo SHA512
|
||||||
default-preference-list TWOFISH CAMELLIA256 AES256 CAMELLIA192 AES192 CAMELLIA128 AES BLOWFISH IDEA CAST5 3DES SHA512 SHA384 SHA256 SHA224 RIPEMD160 SHA1 ZLIB BZIP2 ZIP Uncompressed
|
default-preference-list TWOFISH CAMELLIA256 AES256 CAMELLIA192 AES192 CAMELLIA128 AES BLOWFISH IDEA CAST5 3DES SHA512 SHA384 SHA256 SHA224 RIPEMD160 SHA1 ZLIB BZIP2 ZIP Uncompressed
|
||||||
|
Loading…
Reference in New Issue
Block a user