How to encrypt a string/stream with bouncycastle pgp without starting with a file
Looking at the source of PGPUtil you can see what API to call when working with streams or arrays directly:
public static void writeFileToLiteralData(OutputStream out, char fileType, File file, byte[] buffer) throws IOException { PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator(); OutputStream pOut = lData.open(out, fileType, file.getName(), new Date(file.lastModified()), buffer); FileInputStream in = new FileInputStream(file); byte[] buf = new byte[buffer.length]; int len; while ((len = in.read(buf)) > 0) { pOut.write(buf, 0, len); } lData.close(); in.close();}
Found at grepcode repository - payneteasy superfly sources
private static void writeBytesToLiteralData(OutputStream out, char fileType, String name, byte[] bytes) throws IOException { PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator(); OutputStream pOut = lData.open(out, fileType, name,bytes.length, new Date()); pOut.write(bytes);}
Your code was extremely useful to me. I am working in ColdFusion (with Java) and needed to take passwords, encode them in a short import file for LDIFDE (to be used in Active Directory on another server) and I never wanted the passwords to touch the disk in plaintext. I modified it only slightly to add two functions that abstract out the common use case of encrypting a string to disk and decrypting from disk. Thank you @dstarh.
import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.security.NoSuchProviderException;import java.security.SecureRandom;import java.security.Security;import java.util.Date;import java.util.Iterator;import org.bouncycastle.bcpg.ArmoredOutputStream;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.PGPException;import org.bouncycastle.openpgp.PGPLiteralData;import org.bouncycastle.openpgp.PGPLiteralDataGenerator;import org.bouncycastle.openpgp.PGPObjectFactory;import org.bouncycastle.openpgp.PGPPrivateKey;import org.bouncycastle.openpgp.PGPPublicKey;import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;import org.bouncycastle.openpgp.PGPPublicKeyRing;import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;import org.bouncycastle.openpgp.PGPSecretKey;import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;import org.bouncycastle.openpgp.PGPUtil;/** * Simple routine to encrypt and decrypt using a Public and Private key with passphrase. This service * routine provides the basic PGP services between byte arrays. * */public class PgpEncryption { public PgpEncryption() { // Empty constructor } private static PGPPrivateKey findSecretKey( PGPSecretKeyRingCollection pgpSec, long keyID, char[] pass) throws PGPException, NoSuchProviderException { PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID); if (pgpSecKey == null) { return null; } return pgpSecKey.extractPrivateKey(pass, "BC"); } /** * decrypt the passed in message stream * * @param encrypted * The message to be decrypted. * @param passPhrase * Pass phrase (key) * * @return Clear text as a byte array. I18N considerations are not handled * by this routine * @exception IOException * @exception PGPException * @exception NoSuchProviderException */ public static byte[] decrypt(byte[] encrypted, InputStream keyIn, char[] password) throws IOException, PGPException, NoSuchProviderException { InputStream in = new ByteArrayInputStream(encrypted); in = PGPUtil.getDecoderStream(in); PGPObjectFactory pgpF = new PGPObjectFactory(in); PGPEncryptedDataList enc = null; Object o = pgpF.nextObject(); // // the first object might be a PGP marker packet. // if (o instanceof PGPEncryptedDataList) { enc = (PGPEncryptedDataList) o; } else { enc = (PGPEncryptedDataList) pgpF.nextObject(); } // // find the secret key // Iterator it = enc.getEncryptedDataObjects(); PGPPrivateKey sKey = null; PGPPublicKeyEncryptedData pbe = null; PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection( PGPUtil.getDecoderStream(keyIn)); while (sKey == null && it.hasNext()) { pbe = (PGPPublicKeyEncryptedData) it.next(); sKey = findSecretKey(pgpSec, pbe.getKeyID(), password); } if (sKey == null) { throw new IllegalArgumentException( "secret key for message not found."); } InputStream clear = pbe.getDataStream(sKey, "BC"); PGPObjectFactory pgpFact = new PGPObjectFactory(clear); PGPCompressedData cData = (PGPCompressedData) pgpFact.nextObject(); pgpFact = new PGPObjectFactory(cData.getDataStream()); PGPLiteralData ld = (PGPLiteralData) pgpFact.nextObject(); InputStream unc = ld.getInputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream(); int ch; while ((ch = unc.read()) >= 0) { out.write(ch); } byte[] returnBytes = out.toByteArray(); out.close(); return returnBytes; } /** * Simple PGP encryptor between byte[]. * * @param clearData * The test to be encrypted * @param passPhrase * The pass phrase (key). This method assumes that the key is a * simple pass phrase, and does not yet support RSA or more * sophisiticated keying. * @param fileName * File name. This is used in the Literal Data Packet (tag 11) * which is really inly important if the data is to be related to * a file to be recovered later. Because this routine does not * know the source of the information, the caller can set * something here for file name use that will be carried. If this * routine is being used to encrypt SOAP MIME bodies, for * example, use the file name from the MIME type, if applicable. * Or anything else appropriate. * * @param armor * * @return encrypted data. * @exception IOException * @exception PGPException * @exception NoSuchProviderException */ public static byte[] encrypt(byte[] clearData, PGPPublicKey encKey, String fileName,boolean withIntegrityCheck, boolean armor) throws IOException, PGPException, NoSuchProviderException { if (fileName == null) { fileName = PGPLiteralData.CONSOLE; } ByteArrayOutputStream encOut = new ByteArrayOutputStream(); OutputStream out = encOut; if (armor) { out = new ArmoredOutputStream(out); } ByteArrayOutputStream bOut = new ByteArrayOutputStream(); PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator( PGPCompressedDataGenerator.ZIP); OutputStream cos = comData.open(bOut); // open it with the final // destination PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator(); // we want to generate compressed data. This might be a user option // later, // in which case we would pass in bOut. OutputStream pOut = lData.open(cos, // the compressed output stream PGPLiteralData.BINARY, fileName, // "filename" to store clearData.length, // length of clear data new Date() // current time ); pOut.write(clearData); lData.close(); comData.close(); PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator( PGPEncryptedData.CAST5, withIntegrityCheck, new SecureRandom(), "BC"); cPk.addMethod(encKey); byte[] bytes = bOut.toByteArray(); OutputStream cOut = cPk.open(out, bytes.length); cOut.write(bytes); // obtain the actual bytes from the compressed stream cOut.close(); out.close(); return encOut.toByteArray(); } private static PGPPublicKey readPublicKey(InputStream in) throws IOException, PGPException { in = PGPUtil.getDecoderStream(in); PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(in); // // we just loop through the collection till we find a key suitable for // encryption, in the real // world you would probably want to be a bit smarter about this. // // // iterate through the key rings. // Iterator rIt = pgpPub.getKeyRings(); while (rIt.hasNext()) { PGPPublicKeyRing kRing = (PGPPublicKeyRing) rIt.next(); Iterator kIt = kRing.getPublicKeys(); while (kIt.hasNext()) { PGPPublicKey k = (PGPPublicKey) kIt.next(); if (k.isEncryptionKey()) { return k; } } } throw new IllegalArgumentException( "Can't find encryption key in key ring."); } public static byte[] getBytesFromFile(File file) throws IOException { InputStream is = new FileInputStream(file); // Get the size of the file long length = file.length(); if (length > Integer.MAX_VALUE) { // File is too large } // Create the byte array to hold the data byte[] bytes = new byte[(int)length]; // Read in the bytes int offset = 0; int numRead = 0; while (offset < bytes.length && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) { offset += numRead; } // Ensure all the bytes have been read in if (offset < bytes.length) { throw new IOException("Could not completely read file "+file.getName()); } // Close the input stream and return bytes is.close(); return bytes; } public static String encryptToFile(String inputStr, String keyFile, String outFile) throws Exception { Security.addProvider(new BouncyCastleProvider()); byte[] original = inputStr.getBytes(); FileInputStream pubKey = new FileInputStream(keyFile); byte[] encrypted = encrypt(original, readPublicKey(pubKey), null, true, true); FileOutputStream dfis = new FileOutputStream(outFile); dfis.write(encrypted); dfis.close(); return new String(encrypted); } public static String decryptFromFile(String passphrase, String keyFile, String inputFile) throws Exception { Security.addProvider(new BouncyCastleProvider()); byte[] encFromFile = getBytesFromFile(new File(inputFile)); FileInputStream secKey = new FileInputStream(keyFile); byte[] decrypted = decrypt(encFromFile, secKey, passphrase.toCharArray()); return new String(decrypted); } public static void main(String[] args) throws Exception { String encrypted = encryptToFile("Hello world","pub.asc","enc.asc"); System.out.println("\nencrypted data = '" + new String(encrypted) + "'"); String decrypted = decryptFromFile("open sesame", "secret.asc", "enc.asc"); System.out.println("\ndecrypted data = '" + decrypted + "'"); }}