Java 使用bouncy castle创建可供Thunderbird使用的公共PGP密钥

Java 使用bouncy castle创建可供Thunderbird使用的公共PGP密钥,java,email,encryption,bouncycastle,pgp,Java,Email,Encryption,Bouncycastle,Pgp,我使用创建了公共和私有PGP密钥。在进行GregS建议的更改后,公钥是.asc文件,私钥是.skr文件。我需要先将公钥分发给Thunderbird用户,然后再分发给Outlook和其他电子邮件客户端的用户。我读了,但说明只指定了.asc扩展名,没有指定.asc文件的内容/结构 如何进行设置,以便我下面的(修改的?)代码创建一个公钥,雷鸟的远程用户可以使用该公钥发送加密的电子邮件,然后可以用我的私钥解密,私钥也是由下面的(修改的?)代码创建的接受的答案将包括分步说明,不仅用于对下面的代码进行任何必

我使用创建了公共和私有
PGP
密钥。在进行GregS建议的更改后,公钥是
.asc
文件,私钥是
.skr
文件。我需要先将公钥分发给Thunderbird用户,然后再分发给Outlook和其他电子邮件客户端的用户。我读了,但说明只指定了
.asc
扩展名,没有指定
.asc
文件的内容/结构

如何进行设置,以便我下面的(修改的?)代码创建一个公钥,雷鸟的远程用户可以使用该公钥发送加密的电子邮件,然后可以用我的私钥解密,私钥也是由下面的(修改的?)代码创建的接受的答案将包括分步说明,不仅用于对下面的代码进行任何必要的更改,还用于设置每个远程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是免费的。。或者,在我的
CentOS 7
计算机上,我使用
yum安装Thunderbird
下载了Thunderbird。通过将以下内容添加到
pom.xml
,您可以下载bouncy castle:

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcpg-jdk15on</artifactId>
    <version>1.51</version>
</dependency>
我将
RSAGen.java
中的所有
import
语句添加到上面我的OP中的代码段中。我认为问题可能与密钥需要名称/签名有关

谷歌搜索此错误会导致以下链接,以及其他链接:



编辑#2

按照@JRichardSnape的建议,我尝试了
Enigmail->Key management->File->Import-Key-from-File
。这导致出现以下对话框,该对话框似乎表明,在导入密钥时,密钥未签名。因此,似乎没有与导入的
.asc
文件关联的名称或电子邮件地址。之后,该密钥也不会出现在EnigMail的密钥列表中

编辑#3

使用
gpg--gen key
,我能够让CentOS 7终端创建一个密钥对,包括一个公钥,我能够成功导入到Thunderbird中,这样Thunderbird现在就可以将终端gpg生成的公钥与预期的电子邮件收件人相关联。但是,当我采取所有步骤使用公钥从Thunderbird发送加密电子邮件时,电子邮件及其附件仍然未加密。我从远程Thunderbird向拥有私钥的服务器发送带有公钥的加密电子邮件的步骤如下

考虑到gpg--gen key似乎有效,目前剩下的主要问题似乎是这个悬赏问题中的雷鸟部分。我已经发布了很多关于解决上一段超级用户问题中Thunderbird部分的进展。你的帮助对回答这个问题也有很大帮助

编辑#4

我仍然无法获取要导入到Thunderbird中的
BouncyCastle
-创建的密钥。但是,当我使用在
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.

我的
CentOS 7
服务器仍在生成未加密的附件,当我从本编辑中描述的同一个“第一个”Thunderbird帐户向其发送电子邮件时。我正在试图确定这是由于
CentOS 7
服务器中的
dovecot
/
postfix
/
/
/
gpg
中的某些“自动解密”,还是由于Thunderbird sender中的某些设置。我正在研究这个

我将尝试逐一阐述以下几点:

Java bouncycastle密钥环生成 Java代码确实可以工作并生成可用的密钥环对。我用不同的电子邮件和不同的密码对它进行了测试,没有问题。我已经有一个第三方使用公钥给我发送了一封电子邮件,并成功地用这个Java代码生成的私钥对其进行了解密。该键可用于以下组合

  • Windows8上的Thunderbird(31.4.0)+Enigmail(1.7.2)+gpg(Gpg4win)
  • Ubuntu14.10上的Thunderbird+Enigmail(使用xfer桌面管理器)
但是

OP发现导入密钥时出现问题,失败意味着在
CentOS/Thunderbird/pgp
组合中没有用户ID。类似地,导入失败,错误是在
Windows/Outlook/Kleopatra
plugin上没有用户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版本

使用雷鸟 看来我的意见建议已经解决了大部分问题
uid  alice@example.com
sig!3        14AEE94A 2015-02-05  [self-signature]