title: using bouncy castle to create public PGP key usable by Thunderbird
body: <p>I created public and private <code>PGP</code> keys using <a href="http://grepcode.com/file/repo1.maven.org/maven2/org.bouncycastle/bcpg-jdk16/1.45/org/bouncycastle/openpgp/PGPKeyRingGenerator.java" rel="nofollow noreferrer">org.bouncycastle.openpgp.PGPKeyRingGenerator</a>.  After making a change suggested by GregS, the public key is a <code>.asc</code> file, and the private key is a <code>.skr</code> file.  I need to distribute the public key at first to Thunderbird users, and then later to users of Outlook and other email clients.  I read <a href="https://support.mozilla.org/en-US/kb/digitally-signing-and-encrypting-messages#w_receiving-a-public-key-via-email" rel="nofollow noreferrer">these instructions for receiving a public key in thunderbird</a>, but the instructions merely specify a <code>.asc</code> extension without specifying the contents/structure of the <code>.asc</code> file.  </p>
  
  <p>How do set things up so that my (modified?) code below creates a public key that can be used by remote users of Thunderbird to send encrypted emails which can then be decrypted by my private key, also created by the (modified?) code below?  <strong>The accepted answer will include step by step instructions, not only for making any necessary changes to the code below, but also for setting up each remote Thunderbird user to utilize the below-generated-public-key to send emails which can be decrypted by the private key in my app, created by the (modified?) code below.</strong>  </p>
  
  <p>Here is my first draft of the key-generating code:  </p>
  
  <pre><code>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;
      }
  }
  </code></pre>
  
  <p>When I run the code above to generate the <code>.asc</code> file, and then try to import the <code>.asc</code> file into Thunderbird, I get the following error screen:  </p>
  
  <p><img src="https://s28.postimg.org/ik0ep35nh/gpg_fail.png"></p>
  
  <p>Note that I did not install GnuPG on my CentOS 7 machine.  </p>
  
  <p>Also, <strong>you can recreate this problem on your own machine easily because Thunderbird is free.</strong>  <a href="https://www.mozilla.org/en-US/thunderbird/download" rel="nofollow noreferrer">You can download thunderbird for free using this link</a>.  Alternatively, on my <code>CentOS 7</code> machine, I downloaded Thunderbird with <code>yum install thunderbird</code>.  You can download bouncy castle by adding the following to your <code>pom.xml</code>:  </p>
  
  <pre><code>&lt;dependency&gt;
      &lt;groupId&gt;org.bouncycastle&lt;/groupId&gt;
      &lt;artifactId&gt;bcpg-jdk15on&lt;/artifactId&gt;
      &lt;version&gt;1.51&lt;/version&gt;
  &lt;/dependency&gt;
  </code></pre>
  
  <hr>
  
  <p><strong>EDIT#1:</strong>  </p>
  
  <p>To address JRichardSnape's questions, I found that maven must also be automatically downloading the <code>org.bouncycastle.crypto</code> library because it is a dependency of <code>bcpg-jdk15on</code>.  JRichardSnape is correct that <a href="http://grepcode.com/file/repo1.maven.org/maven2/org.bouncycastle/bcprov-jdk16/1.45/org/bouncycastle/crypto/params/RSAKeyGenerationParameters.java" rel="nofollow noreferrer"><code>RSAKeyGenerationParameters</code></a> and <a href="http://grepcode.com/file/repo1.maven.org/maven2/org.apache.servicemix.bundles/org.apache.servicemix.bundles.bcprov-jdk15on/1.48_1/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java" rel="nofollow noreferrer"><code>RSAKeyPairGenerator</code></a> are not in the <code>bcpg-jdk15on.jar</code> manual download. (Note: the versions in the links may not be current.) <strong>However, both classes are in the automated maven download that results from the single dependency snippet from <code>pom.xml</code> shown above.</strong>  I say this because there are no other <code>bouncycastle</code> dependencies in my <code>pom.xml</code>.  I am using Java 7.</p>
  
  <p>Eclipse describes the two classes imported as:  </p>
  
  <pre><code>import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
  import org.bouncycastle.crypto.generators.RSAKeyPairGenerator;  
  </code></pre>
  
  <p>I added all of the <code>import</code> statements from <code>RSAGen.java</code> to the code segment in my OP above.  I think the problem might to do with the need for a name/signature for the key.  </p>
  
  <p>Googling this error results in the following links, among others:  </p>
  
  <p><a href="https://github.com/openpgpjs/openpgpjs/pull/96" rel="nofollow noreferrer"> Convert userId to UTF8 before generating signature #96 </a><br>
  <a href="https://github.com/mailvelope/mailvelope/issues/92" rel="nofollow noreferrer"> non-ascii characters in Name field #92 </a><br>
  <a href="http://support.gpgtools.org/discussions/problems/3178-unable-to-import-pgp-certificate-into-keychain" rel="nofollow noreferrer">Unable to import PGP certificate into Keychain</a> </p>
  
  <p><strong>EDIT#2</strong>  </p>
  
  <p>As per @JRichardSnape's advice, I tried <code>Enigmail-&gt;Key management -&gt;File -&gt;Import keys from file</code>.  This resulted in the following dialog box, which seems to indicate that, while the key was imported, the key was not signed.  Thus, it seems that there is no name or no email address associated with the <code>.asc</code> file that was imported.  The key also does not show up in EnigMail's list of keys afterwards.  </p>
  
  <p><img src="https://s9.postimg.org/ewtqtulfj/Enig_Mail_Alert.png">  </p>
  
  <p><strong>EDIT#3</strong>  </p>
  
  <p>Using <code>gpg --gen-key</code>, I was able to get the CentOS 7 terminal to create a keypair, including a public key, which I was able to successfully import into Thunderbird, so that Thunderbird can now associate the terminal-gpg-generated public key with the intended email recipient.  But when I take all the steps to send an encrypted email from Thunderbird using the public key, the email and its attachments nonetheless arrive unencrypted.  The steps I took to send encrypted email with the public key from remote Thunderbird to the server that has the private key are <a href="https://superuser.com/questions/874303/how-can-i-force-thunderbird-to-encrypt-an-email-and-its-attachments">described in this SuperUser posting</a>.  </p>
  
  <p>Given that <code>gpg --gen-key</code> seems to work, the major remaining problem at the moment, seems to be the Thunderbird part of this bounty question.  I have posted a lot of progress towards solving the Thunderbird part in the SuperUser question in the preceding paragraph.  Your help answering it would go a long way towards answering this question as well.  </p>
  
  <p><strong>EDIT#4</strong>  </p>
  
  <p>I am still not able to get the <code>BouncyCastle</code>-created key to import into Thunderbird.  However, when I use keys created on the <code>CentOS 7</code> terminal using <code>gpg --gen-key</code>, I am able to complete the following steps:  </p>
  
  <pre><code>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.
  </code></pre>
  
  <p>My <code>CentOS 7</code> server is still producing un-encrypted attachments when I send an email to it from the same "first" Thunderbird account as described in this edit.  I am trying to determine whether this is due to some "auto-decryption" in <code>dovecot</code>/<code>postfix</code>/<code>mailx</code>/<code>gpg</code> in the <code>CentOS 7</code> server, or whether it is due to some settings in the Thunderbird sender.  I am researching this.  </p>