Home > Uncategorized > Bouncycastle encryption for PGP 2.6.x

Bouncycastle encryption for PGP 2.6.x

Thanks to David Hook who pointed me to right direction and gave ideas to keep on pushing :)

The requirement: I had to integrate with external system by sending pgp encrypted xml messages to server X. Sounds easy. But the server X used pgp 2.6.3is for decryption.

I used Bouncycastle (BC) java implementation for encryption and did not find any very good examples. I think BC tries to keep things really flexible by having CompressedDataGenerator, LiteralDataGenerator, EncryptedDataGenerator etc. At first all this looks overwhelming. I was hoping to create and configure single instance and call encrypt method.

Because I had to be compatible with pgp 2.6.x I anticipated more issues. So how did I encrypt my xml. If your interested of the code then read on.

I saw various errors on console when I tried to decrypt my messages but unfortunately I am not able to bind them to my code. Meaning I’m probably not capable of saying that “Decompression error.  Probable corrupted input.” means that you have to change X, Y and Z in your code. Final solution came from one BC example – KeyBasedFileProcessor

First the general ecryption process:

public File encryptXml(String xml, File result) throws IOException {

// Stream that writes result
 System.out.println("Encrypting: " + xml.length() + " bytes");

//This is the final output
 OutputStream out = new FileOutputStream(result);

//Intermediate output
ByteArrayOutputStream bOut = new ByteArrayOutputStream();

//Create compressed generator
PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(PGPCompressedDataGenerator.ZIP);
 // and direct that generator to write results to ByteArrayOutputStream 
OutputStream compressedOut = comData.open(bOut);

Now in the BC example file shortens stuff and uses PGPUtil class here but I had to write stuff out.

true means that we require that data generator is compatible with old version (2.6.x)
PGPLiteralDataGenerator literalDataGen = new PGPLiteralDataGenerator(true);
// Actual stream that writes bytes to compressedOut.
OutputStream literalOut =
   literalDataGen.open(compressedOut, PGPLiteralData.BINARY, 
                       result.getName(), //Name of the "file" encrypted. As I understood its could be any name
                       xml.length(),     //Total length of the data encrypted
                       new Date());      //Last modified time
// Copy input file bytes to literal stream with commons io utils
IOUtils.write(xml, literalOut);
literalOut.close();
compressedOut.close();

Some examples also demonstrate this done with some byte array as buffer (that has to be with size of power of 2).Those examples use partial packet streams to write data. But because old versions (like pgp 2.6.3) do not support partial packages we can’t use that. Instead we have to use length (fixed size package).

Now data compression is completed and we can start encrypting.

 // object that encrypts the data
    PGPEncryptedDataGenerator encDataGen =
        new PGPEncryptedDataGenerator(PGPEncryptedDataGenerator.IDEA,//Algorithm (CAST5 should also work)
                                      new SecureRandom(), 
                                      true,                   // oldFormat support is required
                                      BOUNCYCASTLE_PROVIDER); //This is "BC"
//Add public key that is used for encryption
encDataGen.addMethod(publicKey);

//Get compressed bytes that are encrypted
byte[] bytes = bOut.toByteArray();
//Open encryption stream to final result outputstream and mark how many bytes will be written there
encryptedOut = encDataGen.open(out, bytes.length);
//Write bytes to stream that is going to encrypt them
encryptedOut.write(bytes);
//Close it up
encryptedOut.close();
out.close();

And we are done.Some key points:

  • new PGPLiteralDataGenerator(true) – oldFormat
  • new PGPEncryptedDataGenerator with oldFormat = true. Make sure you don’t set withIntegrityPacket=true. Look javaDoc for constructors
  • Do not just stack up the streams
  • Don’t use buffers, use fixed length packet format

For me it worked hope you won’t have to jump so many hoops for security. If you find any mistakes or see anything that could be improved please let me know.

Categories: Uncategorized
  1. Awad Awad
    June 16, 2009 at 3:43 pm | #1

    Hi there,

    Thanks a lot for that, very little documentation on how to circumvent the PGP2.6+ issue. Although, I’m still getting the “Unsupported packet format” error.

    Below is teh code I use, which is pretty much identical to what you have –

    ByteArrayOutputStream bOut = new ByteArrayOutputStream();

    boolean oldFormat = true;

    OutputStream out = new FileOutputStream(encodedFile);

    if(armor)
    {
    out = new ArmoredOutputStream(out);
    }

    PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(PGPCompressedData.ZIP);

    OutputStream compressedStream = comData.open(bOut);

    PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator(true);

    OutputStream literalOut = lData.open(compressedStream, PGPLiteralData.BINARY, encodedFile.getName(), h1ldaOutput.length(), new Date());

    IOUtils.write(h1ldaOutput, literalOut);

    literalOut.close();
    compressedStream.close();

    PGPEncryptedDataGenerator encDataGen = new PGPEncryptedDataGenerator(PGPEncryptedData.IDEA, new SecureRandom(), oldFormat, “BC”);

    encDataGen.addMethod(encKey);

    byte[] bytes = bOut.toByteArray();

    OutputStream encryptedOut = encDataGen.open(out, bytes.length);

    encryptedOut.write(bytes);

    encryptedOut.close();
    out.close();

    Any help/suggestions would be greatly appreciated

    • Priit
      June 18, 2009 at 3:39 pm | #2

      Is this h1ldaOutput a stream you wish to encrypt? Stream length() method might not return the total length of stream (I’m encrypting byte array). In addtion try removing armored option. when encrypted with armor you have to provide -a key when decrypting on command line if I remember it correctly.

      Sry for late reply. Hope you get this thing working :)

      • Awad Awad
        June 18, 2009 at 3:46 pm | #3

        Thanks for the reply, actually, you were a lot quicker than you think :)

        h1ldaOutput is a String object. the armor variable at that point is false so it isn’t armored.

        Thanks

      • Priit
        June 19, 2009 at 7:18 am | #4

        I cannot see a reason why this shouldn’t work. Had few ideas but for me encrypting String with your code worked. Also you might still want to convert your string to byte[] before encryption. If String contains UTF8 characters then h1ldaOutput.length() != h1ldaOutput.getBytes().length
        For example ‘char ä’ length()=6 getBytes().length=7 and after decrypting last character is missing.

  1. No trackbacks yet.

Leave a Reply

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 )

Connecting to %s

Follow

Get every new post delivered to your Inbox.