【问题标题】:Need public/private RSA keys for encrypting in Java and decrypting in Python需要公共/私有 RSA 密钥以在 Java 中加密和在 Python 中解密
【发布时间】:2020-04-09 22:36:12
【问题描述】:

我们有一个用 Java 编写的系统,它将编写需要由 Python 系统解密的加密文件。我试图弄清楚我需要什么样的密钥可以被 Java 和 Python API 使用,以及如何生成它们。计划是使用 Java 中的公钥加密文件,使用 Python 中的私钥解密。

我尝试使用gpg --generate-key 生成 RSA 密钥,并在一个 armour 文件中得到一个如下所示的文件:

-----BEGIN PGP PRIVATE KEY BLOCK-----
... encoded key ...
-----END PGP PRIVATE KEY BLOCK-----

并根据如下所示创建一个公钥:

-----BEGIN PGP PUBLIC KEY BLOCK-----
... encoded key ...
-----END PGP PUBLIC KEY BLOCK-----

我可以用PGPUtil.getDecoderStream() 用Java 中的Bouncy Castle 解析公钥文件,得到一个PGPPublicKeyRingCollection 和一个PGPPublicKey,它们可以转换为java.security.PublicKey

在 Python 方面,我尝试使用 cryptography.hazmatPyCrypto api,但不知道如何导入私钥文件。当我尝试时

from Crypto.PublicKey import RSA

RSA.importKey(open('/path/to/private/key/file').read())

我收到RSA key format is not supported

我一直在阅读不同类型的密钥和算法,但我认为持有这样一个密钥的 ASCII 文件应该可以工作,但显然我缺少一些东西。

我还尝试了另一种方式,并使用PyCrypto 生成一个新密钥,例如:

from Crypto.PublicKey import RSA

key = RSA.generate(2048)
f = open('/tmp/private.pem','wb')
f.write(key.exportKey('PEM'))
f.close()

f = open('/tmp/public.pem','wb')
f.write(key.publickey().exportKey('PEM'))
f.close

然后像这样通过 Bouncy Castle 的 API 读取它:

PemReader reader = new PemReader(new FileReader("/tmp/public.pem"));
Object publicKey = RSAPublicKey.getInstance(reader.readPemObject().getContent());

但这给了我:

java.lang.IllegalArgumentException: illegal object in getInstance: org.bouncycastle.asn1.DLSequence

    at org.bouncycastle.asn1.ASN1Integer.getInstance(Unknown Source)
    at org.bouncycastle.asn1.pkcs.RSAPublicKey.<init>(Unknown Source)

Bouncy Castle 提供了两个RSAPublicKey 类,我都试过了,结果一样。

它似乎不应该这么难,所以我试图弄清楚我错过了什么。感谢您的帮助。

【问题讨论】:

  • 没有多少软件包支持 PGP/GPG 用于其密钥的格式。如果您想使用 PGP 格式的密钥,那么您需要使用专门支持它们的库。如果库支持 PGP 密钥不是很明显,那么它不支持。
  • 在我的第二个示例中,我尝试使用 PyCrypto 生成的 RSA 密钥,这似乎与 PGP/GPG 没有任何关系?

标签: bouncycastle pycrypto openpgp python-cryptography


【解决方案1】:

我最终解决了这个问题,想为遇到同样问题的任何人记录这一点。

首先,正如总统所说,PGP 密钥在编程加密 API 中并未得到普遍支持,因此可能不是一个好的选择。使用最广泛的似乎是 RSA 密钥,例如 OpenSSL 编写的密钥,this 文章给出了很好的解释。

一旦您有了密钥,您就需要确定在 Java 和 Python 中使用哪些 API。如上所述,可以简单地使用普通 Java API 加载密钥。在 Python 方面,cryptography 似乎相对较低级别,PyCrypto 更高级别但自 2014 年以来已经过时,PyCryptodomePyCrypto 的一个分支,它是最新的。对于我的解决方案,我选择了PyCryptodome

那么重要的是要认识到算法,即RSA只是加密的众多因素之一,还有哈希算法,填充等。这是com.sun.crypto.provider.RSACipher上的java文档的摘录:

/**
 * RSA cipher implementation. Supports RSA en/decryption and signing/verifying
 * using both PKCS#1 v1.5 and OAEP (v2.2) paddings and without padding (raw RSA).
 * Note that raw RSA is supported mostly for completeness and should only be
 * used in rare cases.
 *
 * Objects should be instantiated by calling Cipher.getInstance() using the
 * following algorithm names:
 *  . "RSA/ECB/PKCS1Padding" (or "RSA") for PKCS#1 v1.5 padding.
 *  . "RSA/ECB/OAEPwith<hash>andMGF1Padding" (or "RSA/ECB/OAEPPadding") for
 *    PKCS#1 v2.2 padding.
 *  . "RSA/ECB/NoPadding" for rsa RSA.
 * ...

在我的例子中,我使用的 Java 工具包是用 Cipher.getInstance("RSA") (YMMV) 创建密码,基于此和上面的 cmets,我知道我需要哪个 Python 模块,在我的例子中是 PKCS1_v1_5 模块在PyCryptodome

这导致了这个 Python 解决方案,我已经解释为省略了一些特定于我的案例的细节,但应该足以让您开发自己的解决方案。

import base64
from Crypto.PublicKey import RSA
from Crypto.Cipher import AES, PKCS1_v1_5

# The public key is not needed for this POC but this demonstrates how to load it
pub_key = RSA.importKey(open('openssl-public.pem').read())
priv_key = RSA.importKey(open('openssl-private.pem').read())

# The public key extracted from the private key should match the imported public key,
# could implement that as a double check
# priv_key.publickey().export_key()

# Need to use the PKCS1_v1_5 module to match "PKCS#1 v1.5" in the Java RSA class
cipher_rsa = PKCS1_v1_5.new(priv_key)
meta = # get the content key x-amz-key, IV x-amz-iv and the unencrypted content length x-amz-unencrypted-content-length

# Base64 decode the iv and key
iv = base64.b64decode(meta['x-amz-iv'])
key = base64.b64decode(meta['x-amz-key'])

# Decrypt the key
decrypted_key = cipher_rsa.decrypt(key, 'An error has occurred')

# Create an AES cipher using the content key and IV.  This must match
# how the data was encoded
cipher_aes = AES.new(decrypted_key, AES.MODE_CBC, iv)

encryptedFile = # get the encrypted file
# Need to read the encrypted file as binary 'rb'
# The decrypted file may be padded
length = meta['x-amz-unencrypted-content-length']
decryptedContent = cipher_aes.decrypt(open(encryptedFile,mode='rb').read())[:length]

【讨论】:

    【解决方案2】:

    不幸的是,在公钥加密软件中,相同的基本内容有许多不同的格式。大多数软件包都试图支持最流行的软件包。一种更普遍的公钥格式是SubjectPublicKeyInfo or SPKI 格式。正如 RFC 5280 中定义的那样,格式是SubjectPublicKeyInfoAsn.1 结构的DER 编码,这种二进制编码对于某些应用程序来说是不方便的。通过使用 base64 编码并用页眉和页脚行包装,我们得到了所谓的"PEM" encoding of public keys。 Pycryptodome 产生的正是这种格式。通过使用 Bouncycastle (BC) 提供程序和 pkix 库,它可以在 Java 中相对轻松地处理。仅使用标准 Java SE 类处理它只是稍微困难一点。

    这里有一些代码 sn-ps 展示了如何解析 PEM SPKI 数据以在 Java 中生成公钥对象。

    使用 Java SE、BC prov 和 BC pkix:

    File pemPubFile = new File("/tmp/public.pem");
    PEMParser pemParser = new PEMParser(new FileReader(pemPubFile));
    SubjectPublicKeyInfo spki = (SubjectPublicKeyInfo) pemParser.readObject();
    PublicKey publicKey = new JcaPEMKeyConverter().getPublicKey(spki);
    System.out.println(publicKey);
    

    仅使用 Java SE:

    List < String > pemLines = Files.readAllLines(pemPubFile.toPath());
    String b64Data = pemLines.stream().reduce("", new BinaryOperator < String > () {@Override
        public String apply(String accum, String element) {
            return accum.concat(element.trim());
        }
    });
    
    // Delete the -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY-----
    b64Data = b64Data.replace("-----BEGIN PUBLIC KEY-----", "");
    b64Data = b64Data.replace("-----END PUBLIC KEY-----", "");
    byte[] der = Base64.getDecoder().decode(b64Data);
    PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(der));
    System.out.println(publicKey);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-18
      • 1970-01-01
      • 1970-01-01
      • 2020-02-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多