![](/img/trans.png)
[英]Create CipherOutputStream with PGP Bouncy Castle dependency
[英]using bouncy castle to create public PGP key usable by Thunderbird
我使用org.bouncycastle.openpgp.PGPKeyRingGenerator创建了公共和私有PGP
密钥。 在进行GregS建议的更改后,公钥是.asc
文件,私钥是.skr
文件。 我需要首先将公钥分发给Thunderbird用户,然后再分发给Outlook和其他电子邮件客户端的用户。 我阅读了这些用于在thunderbird中接收公钥的说明,但是这些指令仅指定了.asc
扩展名,而没有指定.asc
文件的内容/结构。
如何设置以便我的(修改后的?)代码创建一个公钥,Thunderbird的远程用户可以使用该公钥发送加密的电子邮件,然后可以通过我的私钥解密,也可以由(修改后的?)代码创建下面? 接受的答案将包括分步说明,不仅用于对下面的代码进行任何必要的更改,而且还用于设置每个远程Thunderbird用户以利用下面生成的公钥来发送可以被解密的电子邮件。我的应用中的私钥,由下面的(修改后的?)代码创建。
这是我的密钥生成代码的第一个草稿:
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Date;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.HashAlgorithmTags;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.bcpg.sig.Features;
import org.bouncycastle.bcpg.sig.KeyFlags;
import org.bouncycastle.crypto.generators.RSAKeyPairGenerator;
import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
import org.bouncycastle.openpgp.PGPEncryptedData;
import org.bouncycastle.openpgp.PGPKeyPair;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPKeyRingGenerator;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
import org.bouncycastle.openpgp.operator.PGPDigestCalculator;
import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyEncryptorBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
import org.bouncycastle.openpgp.operator.bc.BcPGPKeyPair;
public class RSAGen {
public static void main(String args[]) throws Exception {
char pass[] = {'h', 'e', 'l', 'l', 'o'};
PGPKeyRingGenerator krgen = generateKeyRingGenerator("alice@example.com", pass);
// Generate public key ring, dump to file.
PGPPublicKeyRing pkr = krgen.generatePublicKeyRing();
ArmoredOutputStream pubout = new ArmoredOutputStream(new BufferedOutputStream(new FileOutputStream("/home/user/dummy.asc")));
pkr.encode(pubout);
pubout.close();
// Generate private key, dump to file.
PGPSecretKeyRing skr = krgen.generateSecretKeyRing();
BufferedOutputStream secout = new BufferedOutputStream(new FileOutputStream("/home/user/dummy.skr"));
skr.encode(secout);
secout.close();
}
public final static PGPKeyRingGenerator generateKeyRingGenerator(String id, char[] pass) throws Exception{
return generateKeyRingGenerator(id, pass, 0xc0);
}
// Note: s2kcount is a number between 0 and 0xff that controls the number of times to iterate the password hash before use. More
// iterations are useful against offline attacks, as it takes more time to check each password. The actual number of iterations is
// rather complex, and also depends on the hash function in use. Refer to Section 3.7.1.3 in rfc4880.txt. Bigger numbers give
// you more iterations. As a rough rule of thumb, when using SHA256 as the hashing function, 0x10 gives you about 64
// iterations, 0x20 about 128, 0x30 about 256 and so on till 0xf0, or about 1 million iterations. The maximum you can go to is
// 0xff, or about 2 million iterations. I'll use 0xc0 as a default -- about 130,000 iterations.
public final static PGPKeyRingGenerator generateKeyRingGenerator(String id, char[] pass, int s2kcount) throws Exception {
// This object generates individual key-pairs.
RSAKeyPairGenerator kpg = new RSAKeyPairGenerator();
// Boilerplate RSA parameters, no need to change anything
// except for the RSA key-size (2048). You can use whatever key-size makes sense for you -- 4096, etc.
kpg.init(new RSAKeyGenerationParameters(BigInteger.valueOf(0x10001), new SecureRandom(), 2048, 12));
// First create the master (signing) key with the generator.
PGPKeyPair rsakp_sign = new BcPGPKeyPair(PGPPublicKey.RSA_SIGN, kpg.generateKeyPair(), new Date());
// Then an encryption subkey.
PGPKeyPair rsakp_enc = new BcPGPKeyPair(PGPPublicKey.RSA_ENCRYPT, kpg.generateKeyPair(), new Date());
// Add a self-signature on the id
PGPSignatureSubpacketGenerator signhashgen = new PGPSignatureSubpacketGenerator();
// Add signed metadata on the signature.
// 1) Declare its purpose
signhashgen.setKeyFlags(false, KeyFlags.SIGN_DATA|KeyFlags.CERTIFY_OTHER);
// 2) Set preferences for secondary crypto algorithms to use when sending messages to this key.
signhashgen.setPreferredSymmetricAlgorithms
(false, new int[] {
SymmetricKeyAlgorithmTags.AES_256,
SymmetricKeyAlgorithmTags.AES_192,
SymmetricKeyAlgorithmTags.AES_128
});
signhashgen.setPreferredHashAlgorithms
(false, new int[] {
HashAlgorithmTags.SHA256,
HashAlgorithmTags.SHA1,
HashAlgorithmTags.SHA384,
HashAlgorithmTags.SHA512,
HashAlgorithmTags.SHA224,
});
// 3) Request senders add additional checksums to the message (useful when verifying unsigned messages.)
signhashgen.setFeature(false, Features.FEATURE_MODIFICATION_DETECTION);
// Create a signature on the encryption subkey.
PGPSignatureSubpacketGenerator enchashgen = new PGPSignatureSubpacketGenerator();
// Add metadata to declare its purpose
enchashgen.setKeyFlags(false, KeyFlags.ENCRYPT_COMMS|KeyFlags.ENCRYPT_STORAGE);
// Objects used to encrypt the secret key.
PGPDigestCalculator sha1Calc = new BcPGPDigestCalculatorProvider().get(HashAlgorithmTags.SHA1);
PGPDigestCalculator sha256Calc = new BcPGPDigestCalculatorProvider().get(HashAlgorithmTags.SHA256);
// bcpg 1.48 exposes this API that includes s2kcount. Earlier versions use a default of 0x60.
PBESecretKeyEncryptor pske = (new BcPBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256, sha256Calc, s2kcount)).build(pass);
// Finally, create the keyring itself. The constructor takes parameters that allow it to generate the self signature.
PGPKeyRingGenerator keyRingGen =
new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, rsakp_sign,
id, sha1Calc, signhashgen.generate(), null,
new BcPGPContentSignerBuilder(rsakp_sign.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1), pske);
// Add our encryption subkey, together with its signature.
keyRingGen.addSubKey(rsakp_enc, enchashgen.generate(), null);
return keyRingGen;
}
}
当我运行上面的代码生成.asc
文件,然后尝试将.asc
文件导入Thunderbird时,我得到以下错误屏幕:
请注意,我没有在我的CentOS 7机器上安装GnuPG。
此外, 您可以轻松地在自己的计算机上重现此问题,因为Thunderbird是免费的。 您可以使用此链接免费下载thunderbird 。 或者,在我的CentOS 7
机器上,我用yum install thunderbird
下载了Thunderbird。 您可以通过在pom.xml
添加以下内容来下载充气城堡:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpg-jdk15on</artifactId>
<version>1.51</version>
</dependency>
编辑#1:
为了解决JRichardSnape的问题,我发现maven还必须自动下载org.bouncycastle.crypto
库,因为它是bcpg-jdk15on
的依赖。 JRichardSnape是正确的, RSAKeyGenerationParameters
和RSAKeyPairGenerator
不在bcpg-jdk15on.jar
手册下载中。 (注意:链接中的版本可能不是最新版本。) 但是,这两个类都在自动maven下载中,这是由上面显示的pom.xml
中的单个依赖性片段产生的。 我这样说是因为我的pom.xml
中没有其他的bouncycastle
依赖项。 我使用的是Java 7。
Eclipse将导入的两个类描述为:
import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
import org.bouncycastle.crypto.generators.RSAKeyPairGenerator;
我将RSAGen.java
中的所有import
语句RSAGen.java
到上面的OP中的代码段。 我认为问题可能与密钥的名称/签名的需要有关。
谷歌搜索此错误会导致以下链接,其中包括:
在生成签名#96之前将userId转换为UTF8
名称字段#92中的非ascii字符
无法将PGP证书导入Keychain
编辑#2
根据@ JRichardSnape的建议,我尝试了Enigmail->Key management ->File ->Import keys from file
。 这导致以下对话框,这似乎表明,在导入密钥时,密钥未签名。 因此,似乎没有与导入的.asc
文件关联的名称或电子邮件地址。 之后,密钥也不会出现在EnigMail的密钥列表中。
编辑#3
使用gpg --gen-key
,我能够让CentOS 7终端创建一个密钥对,包括一个公钥,我能够成功导入Thunderbird,这样Thunderbird现在可以关联终端-gpg生成的公共与预期的电子邮件收件人密钥。 但是,当我采取所有步骤使用公钥从Thunderbird发送加密电子邮件时,电子邮件及其附件仍然未加密。 此超级用户帖子中描述了我使用公钥从远程Thunderbird向具有私钥的服务器发送加密电子邮件所采取的步骤。
鉴于gpg --gen-key
似乎有效,目前仍然存在的主要问题似乎是这个赏金问题中的Thunderbird部分。 我在上一段的SuperUser问题中已经在解决Thunderbird部分方面取得了很多进展。 你回答这个问题的帮助对于回答这个问题还有很长的路要走。
编辑#4
我仍然无法获得BouncyCastle
创建的密钥导入Thunderbird。 但是,当我使用gpg --gen-key
在CentOS 7
终端上创建gpg --gen-key
,我可以完成以下步骤:
1.) I configured my Thunderbird to manage another (second)
email account I have not been using.
2.) I then created a gpg key for that second account and
configured encryption for that second account in Thunderbird.
3.) I sent an encrypted email containing an attachment from the
first Thunderbird account to the second Thunderbird account.
4.) I was able to see that the attachment remained encrypted in
the second account's inbox until I used the recipient key's
passphrase to decrypt it.
当我从同一个“第一个”Thunderbird帐户向该邮件发送电子邮件时,我的CentOS 7
服务器仍在生成未加密的附件。 我试图确定这是否是由于CentOS 7
服务器中的dovecot
/ postfix
/ mailx
/ gpg
中的某些“自动解密”,或者是否是由于Thunderbird发送器中的某些设置。 我在研究这个。
我将尝试逐一解决这些问题:
Java代码确实有效并产生可用的密钥环对。 我用不同的电子邮件和不同的密码测试它没有问题。 我让第三方使用公钥向我发送了一封电子邮件,并使用这个Java代码生成的私钥成功解密了它。 关键是使用以下组合
然而
OP发现导入密钥时出现问题,导致CentOS / Thunderbird / pgp
组合中没有用户ID。 同样,它无法导入,但在Windows / Outlook / Kleopatra
插件上没有提供用户ID的错误(尽管该问题特别引用了Thunderbird)。
我无法重现错误 - 强烈怀疑这是由于GNU PG中的配置差异或版本差异造成的。 我的设置显示以下gpg --version
gpg (GnuPG) 2.0.26 (Gpg4win 2.2.3)
libgcrypt 1.6.2
直接用gpg测试Java生成的密钥
您可以使用java代码生成密钥,转到命令行并执行
gpg --import dummy.asc
然后通过执行测试
gpg --edit-key alice@example.com
通过在gpg>
提示符下键入check
检查它是否具有用户ID。 样本输出:
uid alice@example.com
sig!3 14AEE94A 2015-02-05 [self-signature]
如果这样做 - 您已经消除了密钥导入的gpg问题 - 请检查Thunderbird / Enigmail版本。
似乎大部分问题已经通过我的评论解决,建议通过Enigmail->Key management ->File ->Import keys from file
超级用户OP上的相关问题来Enigmail->Key management ->File ->Import keys from file
。
另请注意 - Enigmail的密钥管理对话框中有“生成”选项。 如果不需要Java生成,则可以使用它来生成密钥对 - 它基本上与我所知道的直接通过gpg生成相同。
使用Thunderbird的剩余问题似乎是对加密缺乏信心,因为该消息在OP的服务器上以纯文本形式出现(似乎该密钥将用于客户端发送加密电子邮件的服务器/客户端组合中)服务器)。
为了确信消息确实被加密 - 我建议改变一个Enigmail设置:
Enigmail
- > Preference
- > Sending tab
"Manual encryption settings"
"confirm before sending"
框中选择"Always"
然后,您将在发送之前看到加密邮件以及一个配置框。
因为它依赖于你的服务器配置,并会更好地问作为一个单独的问题,很可能在任我不能说就如何停止服务器自动解密收到的邮件, 超级用户或serverfault StackExchange网站。
您也可以考虑遵循Enigmail教程的建议并发送加密邮件
阿黛尔,“友好的OpenPGP电子邮件机器人”。 Adele接受OpenPGP消息并以解释的方式回复任何类型的OpenPGP消息。
地址是adele <at> gnupp <dot> de
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.