package org.bouncycastle.openpgp.test; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.openpgp.PGPCompressedData; import org.bouncycastle.openpgp.PGPCompressedDataGenerator; import org.bouncycastle.openpgp.PGPEncryptedData; import org.bouncycastle.openpgp.PGPEncryptedDataGenerator; import org.bouncycastle.openpgp.PGPEncryptedDataList; import org.bouncycastle.openpgp.PGPLiteralData; import org.bouncycastle.openpgp.PGPLiteralDataGenerator; import org.bouncycastle.openpgp.PGPObjectFactory; import org.bouncycastle.openpgp.PGPPBEEncryptedData; import org.bouncycastle.util.encoders.Base64; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; import org.bouncycastle.util.test.UncloseableOutputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.security.SecureRandom; import java.security.Security; import java.util.Date; public class PGPPBETest extends SimpleTest { private static final Date TEST_DATE = new Date(1062200111000L); byte[] enc1 = Base64.decode( "jA0EAwMC5M5wWBP2HBZgySvUwWFAmMRLn7dWiZN6AkQMvpE3b6qwN3SSun7zInw2" + "hxxdgFzVGfbjuB8w"); byte[] enc1crc = Base64.decode("H66L"); char[] pass = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' }; /** * Message with both PBE and symmetric */ byte[] testPBEAsym = Base64.decode( "hQIOA/ZlQEFWB5vuEAf/covEUaBve7NlWWdiO5NZubdtTHGElEXzG9hyBycp9At8" + "nZGi27xOZtEGFQo7pfz4JySRc3O0s6w7PpjJSonFJyNSxuze2LuqRwFWBYYcbS8/" + "7YcjB6PqutrT939OWsozfNqivI9/QyZCjBvFU89pp7dtUngiZ6MVv81ds2I+vcvk" + "GlIFcxcE1XoCIB3EvbqWNaoOotgEPT60unnB2BeDV1KD3lDRouMIYHfZ3SzBwOOI" + "6aK39sWnY5sAK7JjFvnDAMBdueOiI0Fy+gxbFD/zFDt4cWAVSAGTC4w371iqppmT" + "25TM7zAtCgpiq5IsELPlUZZnXKmnYQ7OCeysF0eeVwf+OFB9fyvCEv/zVQocJCg8" + "fWxfCBlIVFNeNQpeGygn/ZmRaILvB7IXDWP0oOw7/F2Ym66IdYYIp2HeEZv+jFwa" + "l41w5W4BH/gtbwGjFQ6CvF/m+lfUv6ZZdzsMIeEOwhP5g7rXBxrbcnGBaU+PXbho" + "gjDqaYzAWGlrmAd6aPSj51AGeYXkb2T1T/yoJ++M3GvhH4C4hvitamDkksh/qRnM" + "M/s8Nku6z1+RXO3M6p5QC1nlAVqieU8esT43945eSoC77K8WyujDNbysDyUCUTzt" + "p/aoQwe/HgkeOTJNelKR9y2W3xinZLFzep0SqpNI/e468yB/2/LGsykIyQa7JX6r" + "BYwuBAIDAkOKfv5rK8v0YDfnN+eFqwhTcrfBj5rDH7hER6nW3lNWcMataUiHEaMg" + "o6Q0OO1vptIGxW8jClTD4N1sCNwNu9vKny8dKYDDHbCjE06DNTv7XYVW3+JqTL5E" + "BnidvGgOmA=="); /** * decrypt the passed in message stream */ private byte[] decryptMessage( byte[] message, Date date) throws Exception { PGPObjectFactory pgpF = new PGPObjectFactory(message); PGPEncryptedDataList enc = (PGPEncryptedDataList)pgpF.nextObject(); PGPPBEEncryptedData pbe = (PGPPBEEncryptedData)enc.get(0); InputStream clear = pbe.getDataStream(pass, "BC"); PGPObjectFactory pgpFact = new PGPObjectFactory(clear); PGPCompressedData cData = (PGPCompressedData)pgpFact.nextObject(); pgpFact = new PGPObjectFactory(cData.getDataStream()); PGPLiteralData ld = (PGPLiteralData)pgpFact.nextObject(); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); if (!ld.getFileName().equals("test.txt") && !ld.getFileName().equals("_CONSOLE")) { fail("wrong filename in packet"); } if (!ld.getModificationTime().equals(date)) { fail("wrong modification time in packet: " + ld.getModificationTime().getTime() + " " + date.getTime()); } InputStream unc = ld.getInputStream(); int ch; while ((ch = unc.read()) >= 0) { bOut.write(ch); } if (pbe.isIntegrityProtected() && !pbe.verify()) { fail("integrity check failed"); } return bOut.toByteArray(); } private byte[] decryptMessageBuffered( byte[] message, Date date) throws Exception { PGPObjectFactory pgpF = new PGPObjectFactory(message); PGPEncryptedDataList enc = (PGPEncryptedDataList)pgpF.nextObject(); PGPPBEEncryptedData pbe = (PGPPBEEncryptedData)enc.get(0); InputStream clear = pbe.getDataStream(pass, "BC"); PGPObjectFactory pgpFact = new PGPObjectFactory(clear); PGPCompressedData cData = (PGPCompressedData)pgpFact.nextObject(); pgpFact = new PGPObjectFactory(cData.getDataStream()); PGPLiteralData ld = (PGPLiteralData)pgpFact.nextObject(); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); if (!ld.getFileName().equals("test.txt") && !ld.getFileName().equals("_CONSOLE")) { fail("wrong filename in packet"); } if (!ld.getModificationTime().equals(date)) { fail("wrong modification time in packet: " + ld.getModificationTime().getTime() + " " + date.getTime()); } InputStream unc = ld.getInputStream(); byte[] buf = new byte[1024]; int len; while ((len = unc.read(buf)) >= 0) { bOut.write(buf, 0, len); } if (pbe.isIntegrityProtected() && !pbe.verify()) { fail("integrity check failed"); } return bOut.toByteArray(); } public void performTest() throws Exception { byte[] out = decryptMessage(enc1, TEST_DATE); if (out[0] != 'h' || out[1] != 'e' || out[2] != 'l') { fail("wrong plain text in packet"); } // // create a PBE encrypted message and read it back. // byte[] text = { (byte)'h', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)' ', (byte)'w', (byte)'o', (byte)'r', (byte)'l', (byte)'d', (byte)'!', (byte)'\n' }; // // encryption step - convert to literal data, compress, encode. // ByteArrayOutputStream bOut = new ByteArrayOutputStream(); PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator( PGPCompressedData.ZIP); Date cDate = new Date((System.currentTimeMillis() / 1000) * 1000); PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator(); OutputStream comOut = comData.open(new UncloseableOutputStream(bOut)); OutputStream ldOut = lData.open( new UncloseableOutputStream(comOut), PGPLiteralData.BINARY, PGPLiteralData.CONSOLE, text.length, cDate); ldOut.write(text); ldOut.close(); comOut.close(); // // encrypt - with stream close // ByteArrayOutputStream cbOut = new ByteArrayOutputStream(); PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(PGPEncryptedData.CAST5, new SecureRandom(), "BC"); cPk.addMethod(pass); OutputStream cOut = cPk.open(new UncloseableOutputStream(cbOut), bOut.toByteArray().length); cOut.write(bOut.toByteArray()); cOut.close(); out = decryptMessage(cbOut.toByteArray(), cDate); if (!areEqual(out, text)) { fail("wrong plain text in generated packet"); } // // encrypt - with generator close // cbOut = new ByteArrayOutputStream(); cPk = new PGPEncryptedDataGenerator(PGPEncryptedData.CAST5, new SecureRandom(), "BC"); cPk.addMethod(pass); cOut = cPk.open(new UncloseableOutputStream(cbOut), bOut.toByteArray().length); cOut.write(bOut.toByteArray()); cPk.close(); out = decryptMessage(cbOut.toByteArray(), cDate); if (!areEqual(out, text)) { fail("wrong plain text in generated packet"); } // // encrypt - partial packet style. // SecureRandom rand = new SecureRandom(); byte[] test = new byte[1233]; rand.nextBytes(test); bOut = new ByteArrayOutputStream(); comData = new PGPCompressedDataGenerator( PGPCompressedData.ZIP); comOut = comData.open(bOut); lData = new PGPLiteralDataGenerator(); ldOut = lData.open(new UncloseableOutputStream(comOut), PGPLiteralData.BINARY, PGPLiteralData.CONSOLE, TEST_DATE, new byte[16]); ldOut.write(test); ldOut.close(); comOut.close(); cbOut = new ByteArrayOutputStream(); cPk = new PGPEncryptedDataGenerator(PGPEncryptedData.CAST5, rand, "BC"); cPk.addMethod(pass); cOut = cPk.open(new UncloseableOutputStream(cbOut), new byte[16]); cOut.write(bOut.toByteArray()); cOut.close(); out = decryptMessage(cbOut.toByteArray(), TEST_DATE); if (!areEqual(out, test)) { fail("wrong plain text in generated packet"); } // // with integrity packet // cbOut = new ByteArrayOutputStream(); cPk = new PGPEncryptedDataGenerator(PGPEncryptedData.CAST5, true, rand, "BC"); cPk.addMethod(pass); cOut = cPk.open(new UncloseableOutputStream(cbOut), new byte[16]); cOut.write(bOut.toByteArray()); cOut.close(); out = decryptMessage(cbOut.toByteArray(), TEST_DATE); if (!areEqual(out, test)) { fail("wrong plain text in generated packet"); } // // decrypt with buffering // out = decryptMessageBuffered(cbOut.toByteArray(), TEST_DATE); if (!areEqual(out, test)) { fail("wrong plain text in buffer generated packet"); } // // sample message // PGPObjectFactory pgpFact = new PGPObjectFactory(testPBEAsym); PGPEncryptedDataList enc = (PGPEncryptedDataList)pgpFact.nextObject(); PGPPBEEncryptedData pbe = (PGPPBEEncryptedData)enc.get(1); InputStream clear = pbe.getDataStream("password".toCharArray(), "BC"); pgpFact = new PGPObjectFactory(clear); PGPLiteralData ld = (PGPLiteralData)pgpFact.nextObject(); bOut = new ByteArrayOutputStream(); InputStream unc = ld.getInputStream(); int ch; while ((ch = unc.read()) >= 0) { bOut.write(ch); } if (!areEqual(bOut.toByteArray(), Hex.decode("5361742031302e30322e30370d0a"))) { fail("data mismatch on combined PBE"); } // // with integrity packet - one byte message // byte[] msg = new byte[1]; bOut = new ByteArrayOutputStream(); comData = new PGPCompressedDataGenerator( PGPCompressedData.ZIP); lData = new PGPLiteralDataGenerator(); comOut = comData.open(new UncloseableOutputStream(bOut)); ldOut = lData.open( new UncloseableOutputStream(comOut), PGPLiteralData.BINARY, PGPLiteralData.CONSOLE, msg.length, cDate); ldOut.write(msg); ldOut.close(); comOut.close(); cbOut = new ByteArrayOutputStream(); cPk = new PGPEncryptedDataGenerator(PGPEncryptedData.CAST5, true, rand, "BC"); cPk.addMethod(pass); cOut = cPk.open(new UncloseableOutputStream(cbOut), new byte[16]); cOut.write(bOut.toByteArray()); cOut.close(); out = decryptMessage(cbOut.toByteArray(), cDate); if (!areEqual(out, msg)) { fail("wrong plain text in generated packet"); } // // decrypt with buffering // out = decryptMessageBuffered(cbOut.toByteArray(), cDate); if (!areEqual(out, msg)) { fail("wrong plain text in buffer generated packet"); } } public String getName() { return "PGPPBETest"; } public static void main( String[] args) { Security.addProvider(new BouncyCastleProvider()); runTest(new PGPPBETest()); } }