如何在Python中进行PGP(生成密钥,加密/解密)

发布于 2024-07-25 05:22:36 字数 423 浏览 8 评论 0原文

我正在用 Python 编写一个程序,通过安装程序分发给 Windows 用户。

该程序需要能够每天下载一个使用用户公钥加密的文件,然后对其进行解密。

因此,我需要找到一个 Python 库,它可以让我生成公共和私有 PGP 密钥,并解密使用公共密钥加密的文件。

这是 pyCrypto 会做的事情吗(文档含糊不清)? 还有其他纯Python库吗? 任何语言的独立命令行工具怎么样?

到目前为止我看到的只是 GNUPG,但是在 Windows 上安装它会向注册表中添加一些内容,并将 dll 扔到各处,然后我必须担心用户是否已经安装了这个,如何备份他们现有的密钥环等。我宁愿只需拥有一个 python 库或命令行工具并自己管理密钥即可。

更新: pyME 可能可以工作,但它似乎与我必须使用的 Python 2.4 不兼容。

I'm making a program in Python to be distributed to windows users via an installer.

The program needs to be able to download a file every day encrypted with the user's public key and then decrypt it.

So I need to find a Python library that will let me generate public and private PGP keys, and also decrypt files encrypted with the public key.

Is this something pyCrypto will do (documentation is nebulous)? Are there other pure Python libraries? How about a standalone command line tool in any language?

All I saw so far was GNUPG but installing that on Windows does stuff to the registry and throws dll's everywhere, and then I have to worry about whether the user already has this installed, how to backup their existing keyrings, etc. I'd rather just have a python library or command line tool and mange the keys myself.

Update: pyME might work but it doesn't seem to be compatible with Python 2.4 which I have to use.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

鹿港小镇 2024-08-01 05:22:37

我认为直接调用 gpg 会更适合你。
关于您既定的目标:
“生成公共和私人 PGP 密钥”

  1. 这将创建完整的密钥对:
    gpg --完整生成密钥

  2. 这会列出创建后密钥环中的密钥:
    gpg --列表键

  3. 这会从该密钥对导出公钥:
    gpg --输出 --armor -导出

...其中 pubkeyfile 是您选择的文件名,您将与交易对手共享,keyid 是

根据您的需求从命令 #2 获取的:
“解密使用公钥加密的文件”

  1. 这将解密您的加密文件:
    gpg --decrypt --output ;

4a. 如果您想在命令中包含用于私钥的密码条目:
gpg --pinentry-mode=loopback --passphrase "PASSPHRASE" --decrypt --output "PATH\TO\OUTPUT" "PATH\TO\FILE.gpg"

请注意,-d 和 -o 可以互换为 --decrypt和 --output

  1. 关于您对 Gpg4Win 的观察:
    “Windows 对注册表进行操作并将 dll 扔到各处,然后我必须担心用户是否已经安装了这个”

我不会担心这一点。 安装一次 gpg4win 就可以为每个人安装它。 但是,每个用户都将使用自己的密钥环。 这是标准安全(也是推荐的最佳)实践。 如果您尝试管理多个用户,可以采取以下策略之一。
5a - 让每个用户拥有自己的私钥环,他们可以导入并信任单个密钥对,可能是您从上面创建的密钥对。 您执行以下步骤:

5a.1 - 导出上面创建的密钥:
gpg --output --armor --export-secret-key ,然后与您的用户共享此文件
注意:您需要输入生成密钥对时使用的密码,然后与用户共享

5a.2 - 每个用户需要创建自己的密钥环一次:
gpg --列表键
这将生成密钥环并报告没有密钥,接下来该用户需要导入您的密钥对:

gpg --import

完成后,每个用户都可以使用上面提供的命令自行解密文件,因为私钥现在位于他们的文件中自己的密钥环

5b - 通过自动化在用户消费的上游执行解密,将解密的文件存放到文件夹中以供用户消费。 当用户不愿意自己学习加密细节时,这种方法效果最好。 我提倡这个解决方案,因为它消除了技术含量较低的用户的密钥管理麻烦。

5c - 调用 gpg 但指定共享密钥环。 我个人并不主张为了我的需要而这样做,而且它违反了标准的安全原则,但每个人的需求都是不同的。 您可以在此处找到有关如何执行此操作的更多信息:

gpg2:如何使用另一个秘密和公共密钥环?

当您尝试使用 python 库通过 python 协调所有这些时,您添加了一个抽象层,在我看来,这比它的价值更麻烦。 随着时间的推移,Python 的版本控制问题可能会出现。 如果您在 python 代码中调用上面的 gpg 命令,那么它是一个更可靠的解决方案。

祝你好运!

I think calling gpg directly will work better for you.
regarding your stated goals:
"generate public and private PGP keys"

  1. this creates the full keypair:
    gpg --full-generate-key

  2. this lists the keys in your keyring once created:
    gpg --list-keys

  3. this exports the public key from that keypair:
    gpg --output --armor -export

...where pubkeyfile is the filename of your choice which you will share with counterparties, keyid is obtained from command #2

regarding your needs:
"decrypt files encrypted with the public key"

  1. this decrypts your encrypted files:
    gpg --decrypt --output </path/to/decrypted filename> </path/to/encrypted filename>

4a. if you want to include the passphrase entry used for the private key in the command:
gpg --pinentry-mode=loopback --passphrase "PASSPHRASE" --decrypt --output "PATH\TO\OUTPUT" "PATH\TO\FILE.gpg"

note that -d and -o can be interchanged for --decrypt and --output

  1. Regarding your observations regarding Gpg4Win:
    "Windows does stuff to the registry and throws dll's everywhere, and then I have to worry about whether the user already has this installed"

I would not worry about this. Installing gpg4win once installs it for everyone. Each user, however, will use their own keyring. This is standard security (and recommended best) practice. If you are trying to manage multiple users, you can take one of the following strategies..
5a - let each user have their own private keyring, they can import and then trust a single keypair, possibly the one you created from above. You perform these steps as follows:

5a.1 - export your secret key, created above:
gpg --output --armor --export-secret-key , then share this file with your users
note: you will need to input the passphrase used when you generated the keypair and then share this with the users

5a.2 - each user will need to create their own keyring once:
gpg --list-keys
this will generate the keyring and report back no keys, next that user will need to import your keypair:

gpg --import

once done each user can use the commands provided above to decrypt the files on their own as the private key is now in their own keyring

5b - perform decryption upstream of user consumption, via automation, depositing decrypted files into a folder for user consumption. This works best when dealing with users unwilling to navigate learn encryption specifics themselves. I advocate this solution as it removes the hassles of key management from less-technical users.

5c - call gpg but specify a shared keyring. I do not personally advocate doing this for my needs, and it violates standard security tenets, but everyone's needs are different. You can find more information on how to do this here:

gpg2: How to use another secret and public keyring?

When you try to orchestrate all this via python using python libraries, you add a layer of abstraction which is more trouble than its worth in my opinion. Versioning issues with python can arise over time. If you invoke the gpg commands above within your python code instead it is a more reliable solution.

Best of Luck!

幸福丶如此 2024-08-01 05:22:36

您不需要 PyCryptoPyMe,尽管这些包可能很好 - 您将在 Windows 下构建时遇到各种问题。 相反,为什么不避开兔子洞并做我所做的呢? 使用gnupg 1.4.9。 您不需要在最终用户计算机上进行完整安装 - 只需发行版中的 gpg.exeiconv.dll 就足够了,并且您只需要拥有它们位于路径中的某个位置,或者使用完整路径名从 Python 代码访问。 无需更改注册表,并且如果需要,所有内容(可执行文件和数据文件)都可以限制在单个文件夹中。

有一个模块 GPG.py 最初由 Andrew Kuchling 编写,由 Richard Jones 改进,并由 Steve Traugott 进一步改进。 它可用 这里,但它不适合 Windows,因为它使用 os.fork()。 虽然最初是 PyCrypto 的一部分,但它完全独立于 PyCrypto 的其他部分,并且只需要 gpg.exe/iconv.dll 即可工作

我有一个源自 Traugott 的 GPG.py 的版本 (gnupg.py),它使用 subprocess 模块。 它在 Windows 下运行良好,至少对于我的目的来说是这样 - 我用它来执行以下操作:

  • 密钥管理 - 生成、列出、导出等
  • 从外部源导入密钥(例如从合作伙伴公司收到的公钥)
  • 加密和解密数据
  • 签名并验证签名

我所拥有的模块现在并不理想,因为它包含了一些不应该存在的其他内容 - 这意味着我目前无法按原样发布它。 在某个时候,也许在接下来的几周内,我希望能够整理它,添加一些更多的单元测试(例如,我没有任何用于签名/验证的单元测试)并发布它(无论是在原始的 PyCrypto 许可证或类似的商业友好许可证)。 如果您等不及了,请使用 Traugott 的模块并自行修改它 - 使其与 subprocess 模块一起工作并不需要太多工作。

这种方法比其他方法(例如基于 SWIG 的解决方案或需要使用 MinGW/MSYS 构建的解决方案)要轻松得多,我思考并尝试过。 我对用其他语言(例如 C#)编写的系统使用了相同的(gpg.exe/iconv.dll)方法,同样轻松结果。

PS 它适用于 Python 2.4 以及 Python 2.5 及更高版本。 尚未与其他版本进行测试,但我预计不会出现任何问题。

You don't need PyCrypto or PyMe, fine though those packages may be - you will have all kinds of problems building under Windows. Instead, why not avoid the rabbit-holes and do what I did? Use gnupg 1.4.9. You don't need to do a full installation on end-user machines - just gpg.exe and iconv.dll from the distribution are sufficient, and you just need to have them somewhere in the path or accessed from your Python code using a full pathname. No changes to the registry are needed, and everything (executables and data files) can be confined to a single folder if you want.

There's a module GPG.py which was originally written by Andrew Kuchling, improved by Richard Jones and improved further by Steve Traugott. It's available here, but as-is it's not suitable for Windows because it uses os.fork(). Although originally part of PyCrypto, it is completely independent of the other parts of PyCrypto and needs only gpg.exe/iconv.dll in order to work.

I have a version (gnupg.py) derived from Traugott's GPG.py, which uses the subprocess module. It works fine under Windows, at least for my purposes - I use it to do the following:

  • Key management - generation, listing, export etc.
  • Import keys from an external source (e.g. public keys received from a partner company)
  • Encrypt and decrypt data
  • Sign and verify signatures

The module I've got is not ideal to show right now, because it includes some other stuff which shouldn't be there - which means I can't release it as-is at the moment. At some point, perhaps in the next couple of weeks, I hope to be able to tidy it up, add some more unit tests (I don't have any unit tests for sign/verify, for example) and release it (either under the original PyCrypto licence or a similar commercial-friendly license). If you can't wait, go with Traugott's module and modify it yourself - it wasn't too much work to make it work with the subprocess module.

This approach was a lot less painful than the others (e.g. SWIG-based solutions, or solutions which require building with MinGW/MSYS), which I considered and experimented with. I've used the same (gpg.exe/iconv.dll) approach with systems written in other languages, e.g. C#, with equally painless results.

P.S. It works with Python 2.4 as well as Python 2.5 and later. Not tested with other versions, though I don't foresee any problems.

浪漫之都 2024-08-01 05:22:36

经过大量挖掘后,我找到了一个适合我的软件包。 虽然说支持生成密钥,但我没有测试。 不过,我确实成功解密了一条使用 GPG 公钥加密的消息。 该包的优点是它不需要计算机上的 GPG 可执行文件,并且是 OpenPGP 的基于 Python 的实现(而不是可执行文件的包装器)。
我使用 GPG4win 和 kleopatra for windows 创建了私钥和公钥
请参阅下面的我的代码。

import pgpy
emsg = pgpy.PGPMessage.from_file(<path to the file from the client that was encrypted using your public key>)
key,_  = pgpy.PGPKey.from_file(<path to your private key>)
with key.unlock(<your private key passpharase>):
    print (key.decrypt(emsg).message)

虽然这个问题很老了。 我希望这对未来的用户有所帮助。

After a LOT of digging, I found a package that worked for me. Although it is said to support the generation of keys, I didn't test it. However I did manage to decrypt a message that was encrypted using a GPG public key. The advantage of this package is that it does not require a GPG executable file on the machine, and is a Python based implementation of the OpenPGP (rather than a wrapper around the executable).
I created the private and public keys using GPG4win and kleopatra for windows
See my code below.

import pgpy
emsg = pgpy.PGPMessage.from_file(<path to the file from the client that was encrypted using your public key>)
key,_  = pgpy.PGPKey.from_file(<path to your private key>)
with key.unlock(<your private key passpharase>):
    print (key.decrypt(emsg).message)

Although the question is very old. I hope this helps future users.

悍妇囚夫 2024-08-01 05:22:36

PyCrypto 支持 PGP - 尽管您应该测试它以确保它符合您的规范。

虽然文档很难找到,但如果你查看 Util/test.py (模块测试脚本),你可以找到其 PGP 支持的基本示例:

if verbose: print '  PGP mode:',
obj1=ciph.new(password, ciph.MODE_PGP, IV)
obj2=ciph.new(password, ciph.MODE_PGP, IV)
start=time.time()
ciphertext=obj1.encrypt(str)
plaintext=obj2.decrypt(ciphertext)
end=time.time()
if (plaintext!=str):
    die('Error in resulting plaintext from PGP mode')
print_timing(256, end-start, verbose)
del obj1, obj2

此外,PublicKey/pubkey.py 提供了以下相关方法:

def encrypt(self, plaintext, K)
def decrypt(self, ciphertext):
def sign(self, M, K):
def verify (self, M, signature):
def can_sign (self):
    """can_sign() : bool
    Return a Boolean value recording whether this algorithm can
    generate signatures.  (This does not imply that this
    particular key object has the private information required to
    to generate a signature.)
    """
    return 1

PyCrypto supports PGP - albeit you should test it to make sure that it works to your specifications.

Although documentation is hard to come by, if you look through Util/test.py (the module test script), you can find a rudimentary example of their PGP support:

if verbose: print '  PGP mode:',
obj1=ciph.new(password, ciph.MODE_PGP, IV)
obj2=ciph.new(password, ciph.MODE_PGP, IV)
start=time.time()
ciphertext=obj1.encrypt(str)
plaintext=obj2.decrypt(ciphertext)
end=time.time()
if (plaintext!=str):
    die('Error in resulting plaintext from PGP mode')
print_timing(256, end-start, verbose)
del obj1, obj2

Futhermore, PublicKey/pubkey.py provides for the following relevant methods:

def encrypt(self, plaintext, K)
def decrypt(self, ciphertext):
def sign(self, M, K):
def verify (self, M, signature):
def can_sign (self):
    """can_sign() : bool
    Return a Boolean value recording whether this algorithm can
    generate signatures.  (This does not imply that this
    particular key object has the private information required to
    to generate a signature.)
    """
    return 1
弃爱 2024-08-01 05:22:36

PyMe 确实声称与 Python 2.4 完全兼容,我引用:

PyMe 的最新版本(截至目前
写作)是 v0.8.0。 它的二进制
Debian 发行版已编译
使用 SWIG v1.3.33 和 GCC v4.2.3
GPGME v1.1.6 和 Python v2.3.5,
v2.4.4 和 v2.5.2(提供于
当时的分布“不稳定”)。
它的 Windows 二进制发行版
使用 SWIG v1.3.29 编译并且
适用于 GPGME v1.1.6 和 Python 的 MinGW v4.1
v2.5.2(虽然相同的二进制文件得到
在 v2.4.2 中安装并正常工作
好吧)。

我不知道你为什么说“它似乎与我必须使用的 Python 2.4 不兼容”——请具体说明一下?

是的,它确实作为 GPGME 上的半 Pythonic (SWIGd) 包装器存在——一旦您拥有基本上可以完成这项工作的 C 库,这就是开发 Python 扩展的一种流行方法。

PyPgp 有一个更简单的方法 - 这就是为什么它是一个单一的,简单的 Python 脚本:基本上它只不过是“shell out”到命令行 PGP 命令。 例如,解密就是:

def decrypt(data):
    "Decrypt a string - if you have the right key."
    pw,pr = os.popen2('pgpv -f')
    pw.write(data)
    pw.close()
    ptext = pr.read()
    return ptext

即将加密后的密文写入pgpv -f的标准输入,读取pgpv的标准输出作为解密后的明文。

PyPgp 也是一个非常古老的项目,尽管它的简单性意味着使其与现代 Python 一起工作(例如,使用 subprocess 而不是现在已弃用的 os.popen2)并不困难。 但你仍然需要安装 PGP,否则 PyPgp 将不会执行任何操作;-)。

PyMe does claim full compatibility with Python 2.4, and I quote:

The latest version of PyMe (as of this
writing) is v0.8.0. Its binary
distribution for Debian was compiled
with SWIG v1.3.33 and GCC v4.2.3 for
GPGME v1.1.6 and Python v2.3.5,
v2.4.4, and v2.5.2 (provided in
'unstable' distribution at the time).
Its binary distribution for Windows
was compiled with SWIG v1.3.29 and
MinGW v4.1 for GPGME v1.1.6 and Python
v2.5.2 (although the same binary get
installed and works fine in v2.4.2 as
well).

I'm not sure why you say "it doesn't seem to be compatible with Python 2.4 which I have to use" -- specifics please?

And yes it does exist as a semi-Pythonic (SWIGd) wrapper on GPGME -- that's a popular way to develop Python extensions once you have a C library that basically does the job.

PyPgp has a much simpler approach -- that's why it's a single, simple Python script: basically it does nothing more than "shell out" to command-line PGP commands. For example, decryption is just:

def decrypt(data):
    "Decrypt a string - if you have the right key."
    pw,pr = os.popen2('pgpv -f')
    pw.write(data)
    pw.close()
    ptext = pr.read()
    return ptext

i.e., write the encrypted cyphertext to the standard input of pgpv -f, read pgpv's standard output as the decrypted plaintext.

PyPgp is also a very old project, though its simplicity means that making it work with modern Python (e.g., subprocess instead of now-deprecated os.popen2) would not be hard. But you still do need PGP installed, or PyPgp won't do anything;-).

述情 2024-08-01 05:22:36

M2Crypto 有 PGP 模块,但我实际上从未尝试过使用它。 如果您尝试过并且有效,请告诉我(我是当前的 M2Crypto 维护者)。 一些链接:

更新: PGP 模块不提供生成密钥的方法,但大概可以使用较低级别的RSADSA 等模块。 我不了解 PGP 的内部情况,所以你必须挖掘细节。 另外,如果您知道如何使用 openssl 命令行命令生成这些命令,那么将其转换为 M2Crypto 调用应该相当容易。

M2Crypto has PGP module, but I have actually never tried to use it. If you try it, and it works, please let me know (I am the current M2Crypto maintainer). Some links:

Update: The PGP module does not provide ways to generate keys, but presumably these could be created with the lower level RSA, DSA etc. modules. I don't know PGP insides, so you'd have to dig up the details. Also, if you know how to generate these using openssl command line commands, it should be reasonably easy to convert that to M2Crypto calls.

戒ㄋ 2024-08-01 05:22:36

正如其他人所指出的,PyMe 是此问题的规范解决方案,因为它基于 GpgME,而 GpgME 是 GnuPG 生态系统的一部分。

对于 Windows,我强烈建议使用 Gpg4win 作为 GnuPG 发行版,原因有两个:

它基于 GnuPG 2,其中,除其他外,还包括 gpg2.exe,它可以(最后,我可能会添加:)按需启动 gpg-agent.exe(gpg v1.x 可以't)。

其次,它是 GnuPG 开发人员唯一的官方 Windows 版本。 例如,它完全是从 Linux 到 Windows 的交叉编译,因此在准备它时没有使用任何非自由软件(对于安全套件来说非常重要:)。

As other have noted, PyMe is the canonical solution for this, since it's based on GpgME, which is part of the GnuPG ecosystem.

For Windows, I strongly recommend to use Gpg4win as the GnuPG distribution, for two reasons:

It's based on GnuPG 2, which, among other things, includes gpg2.exe, which can (finally, I might add :) start gpg-agent.exe on-demand (gpg v1.x can't).

And secondly, it's the only official Windows build by the GnuPG developers. E.g. it's entirely cross-compiled from Linux to Windows, so not a iota of non-free software was used in preparing it (quite important for a security suite :).

小…楫夜泊 2024-08-01 05:22:36

这是一个完整的脚本,它将:

  1. 尝试解密给定文件夹中使用您的公钥加密的所有文件。
  2. 将新文件写入指定文件夹。
  3. 将加密文件移动到指定文件夹。

该脚本还包含创建和存储您自己的私钥和公钥所需的一切,请查看下面的“首次设置”部分。

这个想法是,您可以安排此脚本根据需要运行,它会自动解密找到的数据并为您存储。

我希望这对某人有帮助,这是一个很难弄清楚的项目。

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~ Introduction, change log and table of contents
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Purpose: This script is used to encrypt and decrypt files using the PGP (Pretty Good Privacy) standard..
#
# Change date   Changed by      Description
# 2022-10-03    Ryan Bradley    Initial draft
# 2022-10-12    Ryan Bradley    Cleaned up some comments and table of contents. 
#
# Table of Contents
# [1.0] Hard-coded variables
# [1.1] Load packages and custom functions
# [1.3] First time set up
# [1.4] Define custom functions
# [2.0] Load keys and decrypt files
#
# Sources used to create this script, and for further reading:
# https://github.com/SecurityInnovation/PGPy/
# https://stackoverflow.com/questions/1020320/how-to-do-pgp-in-python-generate-keys-encrypt-decrypt
# https://pypi.org/project/PGPy/
# https://betterprogramming.pub/creating-a-pgp-encryption-tool-with-python-19bae51b7fd
# https://pgpy.readthedocs.io/en/latest/examples.html

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~ [1.1] Load packages 
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
import glob
import pgpy
import shutil
import io

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~ [1.2] Hard-coded variables
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

# Define the paths to public and private keys
path_public_key = r'YOUR PATH HERE'
path_private_key = r'YOUR PATH HERE'

# Define paths to files you want to try decrypting
path_original_files = r'YOUR PATH HERE'
path_decrypted_files = r'YOUR PATH HERE'
path_encrypted_files= r'YOUR PATH HERE'

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~ [1.3] First time set up
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# IMPORTANT WARNINGS!!!!
#  - Do NOT share your private key with anyone else. 
#  - You MUST have the associated private key that is is generated along with a public key 
#       if you want to be able to decrypt anything that is encryped with that public key. Do
#       not overwrite the existing keys unless you will never need any of the previously 
#       encryped data. 
#   - Do not generate new public and private keys unless you have a good reason to. 
#
# The following steps will walk you through how to create and write public and private keys to
# a network location. Be very careful where you store this information. Anyone with access
# to your private key can decrypt anything that was encryped with your public key.
#
# These steps only need to be performed one time when the script is first being 
# created. They are commented out intentionally, as they shouldn't need to be performed 
# every time the script is ran. 
# 
# Here's the a link to the documentation on this topic:
# https://pgpy.readthedocs.io/en/latest/examples.html

# # Load the extra things we need to define a new key
# from pgpy.constants import PubKeyAlgorithm, KeyFlags, HashAlgorithm, SymmetricKeyAlgorithm, CompressionAlgorithm
 
# # Gerate a new a primary key. For this example, we'll use RSA, but it could be DSA or ECDSA as well
# key = pgpy.PGPKey.new(PubKeyAlgorithm.RSAEncryptOrSign, 4096)

# # Define a new user 
# uid = pgpy.PGPUID.new('USER_NAME', comment='Generic user', email='YOUR_EMAIL')

# # Add the new user id to the key, and define all the key preferences.
# key.add_uid(uid, usage={KeyFlags.Sign, KeyFlags.EncryptCommunications, KeyFlags.EncryptStorage},
#             hashes=[HashAlgorithm.SHA256, HashAlgorithm.SHA384, HashAlgorithm.SHA512, HashAlgorithm.SHA224],
#             ciphers=[SymmetricKeyAlgorithm.AES256, SymmetricKeyAlgorithm.AES192, SymmetricKeyAlgorithm.AES128],
#             compression=[CompressionAlgorithm.ZLIB, CompressionAlgorithm.BZ2, CompressionAlgorithm.ZIP, CompressionAlgorithm.Uncompressed]
#             , is_compressed = True)

# # Write the ASCII armored public key to a network location.
# text_file = open(path_public_key, 'w')
# text_file.write(str(key.pubkey))
# text_file.close()

# # Write the ASCII armored private key to a network location.
# text_file = open(path_private_key, 'w')
# text_file.write(str(key))
# text_file.close()

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~ [1.4] Define custom functions
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

def file_encrypt(path_original_file, path_encrypted_file, key_public):
    """
    A function that encrypts the content of a file at the given path and 
    creates an ecryped version file at the new location using the specified
    public key.
    """
    
    # Create a PGP file, compressed with ZIP DEFLATE by default unless otherwise specified
    pgp_file = pgpy.PGPMessage.new(path_original_file, file=True)
    
    # Encrypt the data with the public key
    encrypted_data = key_public.encrypt(pgp_file) 
    
    # Write the encryped data to the encrypted destination
    text_file = open(path_encrypted_file, 'w')
    text_file.write(str(encrypted_data))
    text_file.close()
   
def file_decrypt(path_encrypted_file, path_decrypted_file, key_private):
    """
    A function that decrypts the content of a file at path path and 
    creates a decrypted file at the new location using the given 
    private key.
    """

    # Load a previously encryped message from a file
    pgp_file = pgpy.PGPMessage.from_file(path_encrypted_file)
    
    # Decrypt the data with the given private key
    decrypted_data = key_private.decrypt(pgp_file).message
   
    # Read in the bytes of the decrypted data
    toread = io.BytesIO()
    toread.write(bytes(decrypted_data))  
    toread.seek(0)  # reset the pointer 
   
    # Write the data to the location
    with open(path_decrypted_file, 'wb') as f:
        shutil.copyfileobj(toread, f)
        f.close()
      
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~ [2.0] Load keys and decrypt files
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

# Load your pre-generated public key from the network
key_public, _ = pgpy.PGPKey.from_file(path_public_key)

# Load your pre-generated public key from the network
key_private, _ = pgpy.PGPKey.from_file(path_private_key)

# Find and process any encrypted files in the landing folder
for file in glob.glob(path_original_files + '\*.pgp'):
    
    # Get the path to the file we need to decrypt
    path_encrypted_file = str(file)
    
    # Extract the file name
    parts = path_encrypted_file.split('\\')
    str_file_name = parts[len(parts)-1]
    str_clean_file_name = str_file_name[:-4]
    
    # Extract the file exension
    str_extension = str_clean_file_name.split('.')
    str_extension = str_extension[len(str_extension) - 1]
    
    # Create the path to the new decryped file, dropping the ".pgp" extension
    path_decrypted_file = path_decrypted_files + '\\' + str_clean_file_name
    
    # Create the path to the place we'll store the encryped file
    path_archived_encrypted_file = path_encrypted_files + '\\' + str_file_name
    
    # Decrypt the file
    try:
        file_decrypt(path_encrypted_file, path_decrypted_file, key_private)
        
        # Move the encryped file to its new location
        shutil.move(path_encrypted_file, path_archived_encrypted_file)
    except:
        print('DECRYPTION ERROR!')
        print(f'Unable to decrypt {path_encrypted_file}')
    
# Just for reference, here's how you would call the function to encrypt a file:
# file_encrypt(path_original_file, path_encrypted_file, key_public)

Here's a full script that will:

  1. Attempt to decrypt all the files in a given folder that were encrypted with your public key.
  2. Write the new files to a specified folder.
  3. Move the encrypted files to a specified folder.

The script also has everything you need to create and store your own private and public keys, check out the "First time set up" section below.

The idea is that you can schedule this script to run as often as you like, and it'll automatically decrypt data found and store it for you.

I hope this helps someone, this was a tricky project to figure out.

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~ Introduction, change log and table of contents
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Purpose: This script is used to encrypt and decrypt files using the PGP (Pretty Good Privacy) standard..
#
# Change date   Changed by      Description
# 2022-10-03    Ryan Bradley    Initial draft
# 2022-10-12    Ryan Bradley    Cleaned up some comments and table of contents. 
#
# Table of Contents
# [1.0] Hard-coded variables
# [1.1] Load packages and custom functions
# [1.3] First time set up
# [1.4] Define custom functions
# [2.0] Load keys and decrypt files
#
# Sources used to create this script, and for further reading:
# https://github.com/SecurityInnovation/PGPy/
# https://stackoverflow.com/questions/1020320/how-to-do-pgp-in-python-generate-keys-encrypt-decrypt
# https://pypi.org/project/PGPy/
# https://betterprogramming.pub/creating-a-pgp-encryption-tool-with-python-19bae51b7fd
# https://pgpy.readthedocs.io/en/latest/examples.html

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~ [1.1] Load packages 
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
import glob
import pgpy
import shutil
import io

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~ [1.2] Hard-coded variables
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

# Define the paths to public and private keys
path_public_key = r'YOUR PATH HERE'
path_private_key = r'YOUR PATH HERE'

# Define paths to files you want to try decrypting
path_original_files = r'YOUR PATH HERE'
path_decrypted_files = r'YOUR PATH HERE'
path_encrypted_files= r'YOUR PATH HERE'

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~ [1.3] First time set up
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# IMPORTANT WARNINGS!!!!
#  - Do NOT share your private key with anyone else. 
#  - You MUST have the associated private key that is is generated along with a public key 
#       if you want to be able to decrypt anything that is encryped with that public key. Do
#       not overwrite the existing keys unless you will never need any of the previously 
#       encryped data. 
#   - Do not generate new public and private keys unless you have a good reason to. 
#
# The following steps will walk you through how to create and write public and private keys to
# a network location. Be very careful where you store this information. Anyone with access
# to your private key can decrypt anything that was encryped with your public key.
#
# These steps only need to be performed one time when the script is first being 
# created. They are commented out intentionally, as they shouldn't need to be performed 
# every time the script is ran. 
# 
# Here's the a link to the documentation on this topic:
# https://pgpy.readthedocs.io/en/latest/examples.html

# # Load the extra things we need to define a new key
# from pgpy.constants import PubKeyAlgorithm, KeyFlags, HashAlgorithm, SymmetricKeyAlgorithm, CompressionAlgorithm
 
# # Gerate a new a primary key. For this example, we'll use RSA, but it could be DSA or ECDSA as well
# key = pgpy.PGPKey.new(PubKeyAlgorithm.RSAEncryptOrSign, 4096)

# # Define a new user 
# uid = pgpy.PGPUID.new('USER_NAME', comment='Generic user', email='YOUR_EMAIL')

# # Add the new user id to the key, and define all the key preferences.
# key.add_uid(uid, usage={KeyFlags.Sign, KeyFlags.EncryptCommunications, KeyFlags.EncryptStorage},
#             hashes=[HashAlgorithm.SHA256, HashAlgorithm.SHA384, HashAlgorithm.SHA512, HashAlgorithm.SHA224],
#             ciphers=[SymmetricKeyAlgorithm.AES256, SymmetricKeyAlgorithm.AES192, SymmetricKeyAlgorithm.AES128],
#             compression=[CompressionAlgorithm.ZLIB, CompressionAlgorithm.BZ2, CompressionAlgorithm.ZIP, CompressionAlgorithm.Uncompressed]
#             , is_compressed = True)

# # Write the ASCII armored public key to a network location.
# text_file = open(path_public_key, 'w')
# text_file.write(str(key.pubkey))
# text_file.close()

# # Write the ASCII armored private key to a network location.
# text_file = open(path_private_key, 'w')
# text_file.write(str(key))
# text_file.close()

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~ [1.4] Define custom functions
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

def file_encrypt(path_original_file, path_encrypted_file, key_public):
    """
    A function that encrypts the content of a file at the given path and 
    creates an ecryped version file at the new location using the specified
    public key.
    """
    
    # Create a PGP file, compressed with ZIP DEFLATE by default unless otherwise specified
    pgp_file = pgpy.PGPMessage.new(path_original_file, file=True)
    
    # Encrypt the data with the public key
    encrypted_data = key_public.encrypt(pgp_file) 
    
    # Write the encryped data to the encrypted destination
    text_file = open(path_encrypted_file, 'w')
    text_file.write(str(encrypted_data))
    text_file.close()
   
def file_decrypt(path_encrypted_file, path_decrypted_file, key_private):
    """
    A function that decrypts the content of a file at path path and 
    creates a decrypted file at the new location using the given 
    private key.
    """

    # Load a previously encryped message from a file
    pgp_file = pgpy.PGPMessage.from_file(path_encrypted_file)
    
    # Decrypt the data with the given private key
    decrypted_data = key_private.decrypt(pgp_file).message
   
    # Read in the bytes of the decrypted data
    toread = io.BytesIO()
    toread.write(bytes(decrypted_data))  
    toread.seek(0)  # reset the pointer 
   
    # Write the data to the location
    with open(path_decrypted_file, 'wb') as f:
        shutil.copyfileobj(toread, f)
        f.close()
      
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~ [2.0] Load keys and decrypt files
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

# Load your pre-generated public key from the network
key_public, _ = pgpy.PGPKey.from_file(path_public_key)

# Load your pre-generated public key from the network
key_private, _ = pgpy.PGPKey.from_file(path_private_key)

# Find and process any encrypted files in the landing folder
for file in glob.glob(path_original_files + '\*.pgp'):
    
    # Get the path to the file we need to decrypt
    path_encrypted_file = str(file)
    
    # Extract the file name
    parts = path_encrypted_file.split('\\')
    str_file_name = parts[len(parts)-1]
    str_clean_file_name = str_file_name[:-4]
    
    # Extract the file exension
    str_extension = str_clean_file_name.split('.')
    str_extension = str_extension[len(str_extension) - 1]
    
    # Create the path to the new decryped file, dropping the ".pgp" extension
    path_decrypted_file = path_decrypted_files + '\\' + str_clean_file_name
    
    # Create the path to the place we'll store the encryped file
    path_archived_encrypted_file = path_encrypted_files + '\\' + str_file_name
    
    # Decrypt the file
    try:
        file_decrypt(path_encrypted_file, path_decrypted_file, key_private)
        
        # Move the encryped file to its new location
        shutil.move(path_encrypted_file, path_archived_encrypted_file)
    except:
        print('DECRYPTION ERROR!')
        print(f'Unable to decrypt {path_encrypted_file}')
    
# Just for reference, here's how you would call the function to encrypt a file:
# file_encrypt(path_original_file, path_encrypted_file, key_public)
梦里兽 2024-08-01 05:22:36

仅使用导出的公钥文件进行签名,而不使用密钥环。

使用 PGPy 0.5.2(纯 Python GPG RFC 实现):

key_fpath = './recipient-PUB.gpg'
     
rsa_pub, _ = pgpy.PGPKey.from_file(rkey_fpath)
rkey = rsa_pub.subkeys.values()[0]                                                                                                     
     
text_message = pgpy.PGPMessage.new('my msg')
encrypted_message = rkey.encrypt(text_message)
print encrypted_message.__bytes__()

使用 gpg 1.10.0(gpgme Python 绑定 - 前 PyME):

rkey_fpath = './recipient-PUB.gpg'
cg = gpg.Context()
rkey = list(cg.keylist(source = rkey_fpath))                                                                                                                
 
ciphertext, result, sign_result = cg.encrypt('my msg', recipients=rkey, sign=False, always_trust=True)
print ciphertext

for 循环中的简单基准测试表明,对于我的系统上的这个简单操作,PGPy 的速度快了约 3 倍比 gpgme Python 绑定(请不要接受此声明,因为 X 比 Y 更快:我将邀请您在您的环境中进行测试)。

To sign with only the exported public key file without a keyring.

With PGPy 0.5.2 (pure Python GPG RFC implementation):

key_fpath = './recipient-PUB.gpg'
     
rsa_pub, _ = pgpy.PGPKey.from_file(rkey_fpath)
rkey = rsa_pub.subkeys.values()[0]                                                                                                     
     
text_message = pgpy.PGPMessage.new('my msg')
encrypted_message = rkey.encrypt(text_message)
print encrypted_message.__bytes__()

With gpg 1.10.0 (gpgme Python bindings - former PyME):

rkey_fpath = './recipient-PUB.gpg'
cg = gpg.Context()
rkey = list(cg.keylist(source = rkey_fpath))                                                                                                                
 
ciphertext, result, sign_result = cg.encrypt('my msg', recipients=rkey, sign=False, always_trust=True)
print ciphertext

A simple benchmark in a for loop shows me that for this simple operation on my system PGPy is ~3x time faster than gpgme Python bindings (please do not take this statement as X is faster than Y: I will invite you to test in your environment).

傲鸠 2024-08-01 05:22:36

我将仅附加@Roee Anuar 和他使用 pgpy 的想法,

# pip install pgpy
from pgpy import PGPKey, PGPMessage
from pgpy.packet.packets import LiteralData
from pgpy.constants import CompressionAlgorithm
from datetime import datetime, timezone
import json

pass_phrase = "some_password_anyway"

# simple string
message = PGPMessage.new("42 is quite a pleasant number")
enc_message = message.encrypt(pass_phrase)
print(json.dumps(str(enc_message)))

# file
PGPMessage.filename = "filename.txt"
file_message = PGPMessage.new("/tmp/123", file=True)
enc_message = file_message.encrypt(pass_phrase)
print(json.dumps(str(enc_message)))

这就是加密字符串和文件的方法。
我还实现了一种将数据加密为文件的方法:

# file from byte array and with custom name
msg = PGPMessage()
lit = LiteralData()
lit._contents = bytearray(msg.text_to_bytes("Some stuff in file"))
lit.filename = "any file name here even long ones are ok but do not exceed 192 bytes.txt"
lit.mtime = datetime.now(timezone.utc)
lit.format = 'b'
lit.update_hlen()

msg |= lit
msg._compression = CompressionAlgorithm.ZIP

enc_message = msg.encrypt(pass_phrase)
print(json.dumps(str(enc_message)))

I will just append to @Roee Anuar and his idea with pgpy

# pip install pgpy
from pgpy import PGPKey, PGPMessage
from pgpy.packet.packets import LiteralData
from pgpy.constants import CompressionAlgorithm
from datetime import datetime, timezone
import json

pass_phrase = "some_password_anyway"

# simple string
message = PGPMessage.new("42 is quite a pleasant number")
enc_message = message.encrypt(pass_phrase)
print(json.dumps(str(enc_message)))

# file
PGPMessage.filename = "filename.txt"
file_message = PGPMessage.new("/tmp/123", file=True)
enc_message = file_message.encrypt(pass_phrase)
print(json.dumps(str(enc_message)))

This is how you can encrypt strings and files.
Also I've implemented a way of encrypting data as a file:

# file from byte array and with custom name
msg = PGPMessage()
lit = LiteralData()
lit._contents = bytearray(msg.text_to_bytes("Some stuff in file"))
lit.filename = "any file name here even long ones are ok but do not exceed 192 bytes.txt"
lit.mtime = datetime.now(timezone.utc)
lit.format = 'b'
lit.update_hlen()

msg |= lit
msg._compression = CompressionAlgorithm.ZIP

enc_message = msg.encrypt(pass_phrase)
print(json.dumps(str(enc_message)))

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文