Bouncy Castle – Encrypt a stream
July 28, 2010 Leave a comment
I should be forgiven for thinking that encrypting a stream of data instead of a disk file is more difficult than it is supposed to be. Actually when somebody like me who has no experience with writing secure code uses encryption/decryption libraries like Bouncy Castle, it is always hard. So even after the PCI auditor has paid us a visit many times I can still point out many loopholes in our security code like the AES key that is hard-coded. Split key technology is really hard to grok. So I have not attempted it yet. So the AES key is still inside the java class.
This example uses Bouncy Castle to decrypt a file using a GPG key and encrypt it again using another key on the fly. Some parts of the code that are supposed to be obvious are not shown but as I mentioned secure code is hard to write. The missing piece can be reconstructed from the Bouncy Castle examples.
I am planning to fix the code formatting in the blog soon.
Decrypt a file and return a stream
public InputStream decryptToStream( String file ) throws KeyNotFoundException{ PGPEncryptedDataList enc = null; PGPObjectFactory objectFactory = null; InputStream in = null; try { objectFactory = new PGPObjectFactory( new FileInputStream( file ) ); Object o = objectFactory.nextObject(); // What is a PGP marker packet ? if ( o instanceof PGPEncryptedDataList ) { enc = ( PGPEncryptedDataList ) o; } else { enc = ( PGPEncryptedDataList ) objectFactory.nextObject(); } Iterator it = enc.getEncryptedDataObjects(); PGPPublicKeyEncryptedData pbe = null; while ( it.hasNext()){ pbe = (PGPPublicKeyEncryptedData)it.next(); } InputStream clear = pbe.getDataStream( getPGPSecretKey( pbe.getKeyID() ), "BC" ); PGPObjectFactory plainFact = new PGPObjectFactory( clear ); Object message = plainFact.nextObject(); if (message instanceof PGPCompressedData) { PGPCompressedData cData = (PGPCompressedData) message; PGPObjectFactory pgpFact = new PGPObjectFactory(cData.getDataStream()); message = pgpFact.nextObject(); } if (message instanceof PGPLiteralData) { PGPLiteralData literal = (PGPLiteralData) message; in = literal.getInputStream(); } } catch (FileNotFoundException e) { } catch (IOException e) { } catch (NoSuchProviderException e) { } catch (PGPException e) { } return in; }
Encrypt the stream and write to a file
It should be possible to return a stream from this method instead of writing it to a file.
public void encrypt( PGPPublicKey key, String compressedfile, String encryptedfile, InputStream in ) throws IOException, PGPException, NoSuchProviderException { byte[] buf = new byte[ 1024 ], literalBuffer = new byte[ 1024 ], encryption = new byte[ 1024 ]; ByteArrayOutputStream o = null; OutputStream out = null; FileOutputStream fos = new FileOutputStream( new File( encryptedfile )); PGPCompressedDataGenerator compressedData = new PGPCompressedDataGenerator( PGPCompressedDataGenerator.ZIP); PGPLiteralDataGenerator lg = new PGPLiteralDataGenerator(); try { PGPEncryptedDataGenerator data = new PGPEncryptedDataGenerator( PGPEncryptedData.CAST5, true, new SecureRandom(), "BC"); data.addMethod( key ); int bytes = 0; OutputStream writeToEncrypt = data.open( fos, encryption ); OutputStream lgo = lg.open( compressedData.open( writeToEncrypt ), PGPLiteralData.BINARY, compressedfile, new Date(), literalBuffer ); while ((bytes = in.read( buf )) >= 0) { //As it is written it is encrypted. lgo.write( buf, 0 , bytes ); } lg.close(); compressedData.close(); writeToEncrypt.close(); lgo.close(); } catch (IOException e) { } catch (NoSuchProviderException e) { } catch (PGPException e) { } }