PGP Cryptography With The Legion of the Bouncy Castle – Part 1

Posted on Updated on

Some of my favorite things to do in software engineering is use libraries with cool names. Nothing beats The Legion of the Bouncy Castle. I like that name so much that I decided to just start using it! Well actually I really needed to use PGP cryptography in one of my projects and though it would be nice to blog about it, but yeah cool name though.

So one of the tricky issues with using Bouncy Castle (we seriously do it a disservice by shortening its amazing name to just 2 words) is the lack of proper, complete and friendly documentation. You may find many articles on the net or tutorials, but I found them way too complex and some just didn’t know what they were doing.

The best option was to look at the Bouncy Castle source code and go directly to their examples package. There they give some pretty good examples and enough to build your own tools for their API. In this part of my series I will go over generating a full fledged DSA/El Gamal PGP Key Ring that is importable into PGP.

Generating a PGP Key Ring was not what I thought it was at first. It generally has to go in this order:

  1. Generate DSA Key
  2. Generate your safe prime modulus
  3. Generate El Gamal Key using generated safe prime modulus
  4. Create a PGP Key Ring generator with following parameters
    • Identity string (like name or email address)
    • Secret pass phrase
    • SHA1 Hashing for signing content
    • AES-256 Algorithm and SHA1 for secret key generation
  5. Generate the Secret Key and write it to a file
  6. Generate the Public Key and write it to a file

I have written a class called PGPTool which performs these tasks in a step by step manner and makes the whole prime modulus thing much easier to handle than most tutorials I see on the net. The other is the main program that uses PGPTool to generate the key pair. These sources are GPLv3 so use them wisely πŸ™‚

Be sure you have downloaded and installed the Unlimited Strength Jurisdiction Policy Files before using the below code as you will need it. The JCE policy file can be downloaded here for Java 1.6 users and here for Java 1.7 users.

import java.io.File;
import java.math.BigInteger;
import java.security.KeyPair;
import org.bouncycastle.jce.spec.ElGamalParameterSpec;
import org.bouncycastle.openpgp.PGPKeyRingGenerator;

/**
 * 
 * Copyright George El-Haddad</br>
 * <b>Time stamp:</b> Dec 6, 2012 - 11:41:43 AM<br/>
 * @author George El-Haddad
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
public class PGPCryptoBC {

	public PGPCryptoBC() {
		try {
			String keysDir = System.getProperty("user.dir")+File.separator+"mykeys";
			
			BigInteger primeModulous = PGPTools.getSafePrimeModulus(PGPTools.PRIME_MODULUS_4096_BIT);
			BigInteger baseGenerator = PGPTools.getBaseGenerator();
			ElGamalParameterSpec paramSpecs = new ElGamalParameterSpec(primeModulous, baseGenerator);
	        
			KeyPair dsaKeyPair = PGPTools.generateDsaKeyPair(1024);
			KeyPair elGamalKeyPair = PGPTools.generateElGamalKeyPair(paramSpecs);
			
			PGPKeyRingGenerator pgpKeyRingGen = PGPTools.createPGPKeyRingGenerator(
					dsaKeyPair,
					elGamalKeyPair,
					"test@gmail.com",
					"TestPass12345!".toCharArray()
					);
			
			PGPTools.exportSecretKey(pgpKeyRingGen, new File(keysDir+File.separator+"secret.asc"), true);
			PGPTools.exportPublicKey(pgpKeyRingGen, new File(keysDir+File.separator+"public.asc"), true);
		}
		catch(Exception ex) {
			ex.printStackTrace();
		}
	}
	
	public static void main(String ... args) {
		new PGPCryptoBC();
	}
}
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import java.util.Date;

import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.HashAlgorithmTags;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ElGamalParameterSpec;
import org.bouncycastle.openpgp.PGPEncryptedData;
import org.bouncycastle.openpgp.PGPKeyPair;
import org.bouncycastle.openpgp.PGPKeyRingGenerator;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.operator.PGPDigestCalculator;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;

/**
 * 
 * Copyright George El-Haddad</br>
 * <b>Time stamp:</b> Dec 6, 2012 - 11:41:43 AM<br/>
 * @author George El-Haddad
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
public final class PGPTools {

	public static final int PRIME_MODULUS_4096_BIT = 1;
	public static final int PRIME_MODULUS_3072_BIT = 2;
	public static final int PRIME_MODULUS_2048_BIT = 3;
	public static final int PRIME_MODULUS_1536_BIT = 4;
	
	static {
		Security.addProvider(new BouncyCastleProvider());
	}

	private PGPTools() {

	}

	public static final void exportSecretKey(PGPKeyRingGenerator pgpKeyRingGen, File keyFile, boolean asciiArmor) throws IOException {
		PGPSecretKeyRing pgpSecKeyRing = pgpKeyRingGen.generateSecretKeyRing();

		if(asciiArmor) {
			ArmoredOutputStream aos = new ArmoredOutputStream(new FileOutputStream(keyFile));
			pgpSecKeyRing.encode(aos);
			aos.close();
		}
		else {
			FileOutputStream fos = new FileOutputStream(keyFile);
			pgpSecKeyRing.encode(fos);
			fos.close();
		}
	}

	public static final void exportPublicKey(PGPKeyRingGenerator pgpKeyRingGen, File keyFile, boolean asciiArmor) throws IOException {
		PGPPublicKeyRing pgpPubKeyRing = pgpKeyRingGen.generatePublicKeyRing();

		if(asciiArmor) {
			ArmoredOutputStream aos = new ArmoredOutputStream(new FileOutputStream(keyFile));
			pgpPubKeyRing.encode(aos);
			aos.close();
		}
		else {
			FileOutputStream fos = new FileOutputStream(keyFile);
			pgpPubKeyRing.encode(fos);
			fos.close();
		}
	}

	/**
	 * 
	 * @param dsaKeyPair - the generated DSA key pair
	 * @param elGamalKeyPair - the generated El Gamal key pair
	 * @param identity - the given identity of the key pair ring
	 * @param passphrase - the secret pass phrase to protect the key pair
	 * @return a PGP Key Ring Generate with the El Gamal key pair added as sub key
	 * @throws Exception
	 */
	@SuppressWarnings("deprecation")
	public static final PGPKeyRingGenerator createPGPKeyRingGenerator(KeyPair dsaKeyPair, KeyPair elGamalKeyPair, String identity, char[] passphrase) throws Exception
	{
		PGPKeyPair dsaPgpKeyPair = new PGPKeyPair(PGPPublicKey.DSA, dsaKeyPair, new Date());
		PGPKeyPair elGamalPgpKeyPair = new PGPKeyPair(PGPPublicKey.ELGAMAL_ENCRYPT, elGamalKeyPair, new Date());
		PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get(HashAlgorithmTags.SHA1);
		PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(
	        		PGPSignature.POSITIVE_CERTIFICATION,
	        		dsaPgpKeyPair,
	        		identity,
	        		sha1Calc,
	        		null,
	        		null,
	        		new JcaPGPContentSignerBuilder(dsaPgpKeyPair.getPublicKey().getAlgorithm(),HashAlgorithmTags.SHA1),
	        		new JcePBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256, sha1Calc).setProvider("BC").build(passphrase)
	        		);
		
		keyRingGen.addSubKey(elGamalPgpKeyPair);
		return keyRingGen;
	}

	/**
	 * 
	 * @param keySize 512 - 1024 (multiple of 64)
	 * @return the DSA generated key pair
	 * @throws NoSuchProviderException 
	 * @throws NoSuchAlgorithmException 
	 */
	public static final KeyPair generateDsaKeyPair(int keySize) throws NoSuchAlgorithmException, NoSuchProviderException
	{
		KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA", "BC");
		keyPairGenerator.initialize(keySize);
		KeyPair keyPair = keyPairGenerator.generateKeyPair();
		return keyPair;
	}

	/**
	 * 
	 * @param keySize - 1024, 2048, 4096
	 * @return the El Gamal generated key pair
	 * @throws Exception 
	 */
	public static final KeyPair generateElGamalKeyPair(int keySize) throws Exception
	{
		KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ELGAMAL", "BC");
		keyPairGenerator.initialize(keySize);
		KeyPair keyPair = keyPairGenerator.generateKeyPair();
		return keyPair;
	}

	/**
	 * 
	 * @param paramSpecs - the pre-defined parameter specs
	 * @return the El Gamal generated key pair
	 * @throws Exception
	 */
	public static final KeyPair generateElGamalKeyPair(ElGamalParameterSpec paramSpecs) throws Exception
	{
		KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ELGAMAL", "BC");
		keyPairGenerator.initialize(paramSpecs);
		KeyPair keyPair = keyPairGenerator.generateKeyPair();
		return keyPair;
	}
	
	/**
	 * <p>Given the size in bits
	 * <ul>
	 * 	<li>PRIME_MODULUS_4096_BIT</li>
	 * 	<li>PRIME_MODULUS_3072_BIT</li>
	 * 	<li>PRIME_MODULUS_2048_BIT</li>
	 * 	<li>PRIME_MODULUS_1536_BIT</li>
	 * </ul>
	 * 
	 * It will return a safe prime modulus {@link BigInteger}</p>
	 * 
	 * @param bitSize - the size in bits
	 * @return a safe prime modulus of the specified size of <code>bitSize</code>
	 */
	public static final BigInteger getSafePrimeModulus(int bitSize)
	{
		switch(bitSize)
		{
			case PRIME_MODULUS_4096_BIT:
			{
				return getSafePrimeModulus4096();
			}
			
			case PRIME_MODULUS_3072_BIT:
			{
				return getSafePrimeModulus3072();
			}
			
			case PRIME_MODULUS_2048_BIT:
			{
				return getSafePrimeModulus2048();
			}
			
			case PRIME_MODULUS_1536_BIT:
			{
				return getSafePrimeModulus1536();
			}
			
			default:
			{
				return getSafePrimeModulus1536();
			}
		}
	}
	
	/**
	 * This is a 4096 bit MODP Group 
	 * Prime number is: 2^4096 - 2^4032 - 1 + 2^64 * { [2^3996 pi] + 240904 }
	 * 
	 * @return a 4096 bit MODP group safe prime modulus
	 */
	public static final BigInteger getSafePrimeModulus4096()
	{
		StringBuilder sb = new StringBuilder();
		sb.append("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1");
		sb.append("29024E088A67CC74020BBEA63B139B22514A08798E3404DD");
		sb.append("EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245");
		sb.append("E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED");
		sb.append("EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D");
		sb.append("C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F");
		sb.append("83655D23DCA3AD961C62F356208552BB9ED529077096966D");
		sb.append("670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B");
		sb.append("E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9");
		sb.append("DE2BCBF6955817183995497CEA956AE515D2261898FA0510");
		sb.append("15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64");
		sb.append("ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7");
		sb.append("ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B");
		sb.append("F12FFA06D98A0864D87602733EC86A64521F2B18177B200C");
		sb.append("BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31");
		sb.append("43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7");
		sb.append("88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA");
		sb.append("2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6");
		sb.append("287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED");
		sb.append("1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9");
		sb.append("93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199");
		sb.append("FFFFFFFFFFFFFFFF");
		return new BigInteger(sb.toString(), 16);
	}
	
	/**
	 * This is a 3072 bit MODP Group
	 * Prime is: 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 }
	 * 
	 * @return a 3072 bit MODP group safe prime modulus
	 */
	public static final BigInteger getSafePrimeModulus3072()
	{
		StringBuilder sb = new StringBuilder();
		sb.append("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1");
		sb.append("29024E088A67CC74020BBEA63B139B22514A08798E3404DD");
		sb.append("EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245");
		sb.append("E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED");
		sb.append("EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D");
		sb.append("C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F");
		sb.append("83655D23DCA3AD961C62F356208552BB9ED529077096966D");
		sb.append("670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B");
		sb.append("E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9");
		sb.append("DE2BCBF6955817183995497CEA956AE515D2261898FA0510");
		sb.append("15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64");
		sb.append("ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7");
		sb.append("ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B");
		sb.append("F12FFA06D98A0864D87602733EC86A64521F2B18177B200C");
		sb.append("BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31");
		sb.append("43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF");
		return new BigInteger(sb.toString(), 16);
	}
	
	/**
	 * This is a 2048 bit MODP Group
	 * Prime is: 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 }
	 * 
	 * @return a 2048 bit MODP group safe prime modulus
	 */
	public static final BigInteger getSafePrimeModulus2048()
	{
		StringBuilder sb = new StringBuilder();
		sb.append("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1");
		sb.append("29024E088A67CC74020BBEA63B139B22514A08798E3404DD");
		sb.append("EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245");
		sb.append("E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED");
		sb.append("EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D");
		sb.append("C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F");
		sb.append("83655D23DCA3AD961C62F356208552BB9ED529077096966D");
		sb.append("670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B");
		sb.append("E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9");
		sb.append("DE2BCBF6955817183995497CEA956AE515D2261898FA0510");
		sb.append("15728E5A8AACAA68FFFFFFFFFFFFFFFF");
		return new BigInteger(sb.toString(), 16);
	}
	
	/**
	 * This is a 1536 bit MODP Group
	 * Prime is: 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 }
	 * 
	 * @return a 1536 bit MODP group safe prime modulus
	 */
	public static final BigInteger getSafePrimeModulus1536()
	{
		StringBuilder sb = new StringBuilder();
		sb.append("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1");
		sb.append("29024E088A67CC74020BBEA63B139B22514A08798E3404DD");
		sb.append("EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245");
		sb.append("E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED");
		sb.append("EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D");
		sb.append("C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F");
		sb.append("83655D23DCA3AD961C62F356208552BB9ED529077096966D");
		sb.append("670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF");
		return new BigInteger(sb.toString(), 16);
	}
	
	/**
	 * 
	 * @return the base generator number
	 */
	public static final BigInteger getBaseGenerator()
	{
		return new BigInteger("2", 16);
	}
}

Stay tuned for Part 2 which will include encryption and signing!

Advertisements

7 thoughts on “PGP Cryptography With The Legion of the Bouncy Castle – Part 1

    PabloC said:
    May 31, 2013 at 3:16 pm

    In order to make this code work(If you have a standard SDK, ) you need to dowloand the Unlimited Strength Java(UnlimitedJCEPolicyJDK7.zip). Otherwise you will get an error related to the length of the key.

      geodma responded:
      May 31, 2013 at 3:26 pm

      Yes that’s true, it won’t work without the unlimited policy files. Thanks for bringing that up, I just updated the post to include this info. Thanks.

    Someone said:
    June 14, 2013 at 11:24 am

    In “exportPublicKey” it should be “pgpPubKeyRing.encode(aos);” instead of “pgpPubKeyRing.encode(new ArmoredOutputStream(aos));”. Otherwise the key get’s wrapped twice, which leads to a wrong header in the output file that prevents reading the public key from it later on.

      geodma responded:
      June 14, 2013 at 11:43 am

      Yeah that is a mistake, I forgot to remove it and just use aos instead. Thanks for pointing that out and keeping the code in good condition. I just fixed it.

    […] have the PGPTools file which I wrote to make cryptography easier with BC (Full source can be found here). Generating a detached signature file needs the […]

    […] want to generate Keys for PGP. I used this tutorial and get the error message: the constructor is undefined line 105 and […]

    […] want to generate Keys for PGP. I used this tutorial and get the error message: the constructor is undefined line 105 and […]

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s