001: package org.bouncycastle.openpgp.examples;
002:
003: import org.bouncycastle.bcpg.ArmoredOutputStream;
004: import org.bouncycastle.jce.provider.BouncyCastleProvider;
005: import org.bouncycastle.openpgp.PGPCompressedData;
006: import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
007: import org.bouncycastle.openpgp.PGPEncryptedData;
008: import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
009: import org.bouncycastle.openpgp.PGPEncryptedDataList;
010: import org.bouncycastle.openpgp.PGPException;
011: import org.bouncycastle.openpgp.PGPLiteralData;
012: import org.bouncycastle.openpgp.PGPObjectFactory;
013: import org.bouncycastle.openpgp.PGPOnePassSignatureList;
014: import org.bouncycastle.openpgp.PGPPrivateKey;
015: import org.bouncycastle.openpgp.PGPPublicKey;
016: import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
017: import org.bouncycastle.openpgp.PGPPublicKeyRing;
018: import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
019: import org.bouncycastle.openpgp.PGPSecretKey;
020: import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
021: import org.bouncycastle.openpgp.PGPUtil;
022:
023: import java.io.BufferedInputStream;
024: import java.io.BufferedOutputStream;
025: import java.io.File;
026: import java.io.FileInputStream;
027: import java.io.FileOutputStream;
028: import java.io.IOException;
029: import java.io.InputStream;
030: import java.io.OutputStream;
031: import java.security.NoSuchProviderException;
032: import java.security.SecureRandom;
033: import java.security.Security;
034: import java.util.Iterator;
035:
036: /**
037: * A simple utility class that encrypts/decrypts public key based
038: * encryption large files.
039: * <p>
040: * To encrypt a file: KeyBasedLargeFileProcessor -e [-a|-ai] fileName publicKeyFile.<br>
041: * If -a is specified the output file will be "ascii-armored".
042: * If -i is specified the output file will be have integrity checking added.
043: * <p>
044: * To decrypt: KeyBasedLargeFileProcessor -d fileName secretKeyFile passPhrase.
045: * <p>
046: * Note 1: this example will silently overwrite files, nor does it pay any attention to
047: * the specification of "_CONSOLE" in the filename. It also expects that a single pass phrase
048: * will have been used.
049: * <p>
050: * Note 2: this example generates partial packets to encode the file, the output it generates
051: * will not be readable by older PGP products or products that don't support partial packet
052: * encoding.
053: */
054: public class KeyBasedLargeFileProcessor {
055: /**
056: * A simple routine that opens a key ring file and loads the first available key suitable for
057: * encryption.
058: *
059: * @param in
060: * @return
061: * @throws IOException
062: * @throws PGPException
063: */
064: private static PGPPublicKey readPublicKey(InputStream in)
065: throws IOException, PGPException {
066: in = PGPUtil.getDecoderStream(in);
067:
068: PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(
069: in);
070:
071: //
072: // we just loop through the collection till we find a key suitable for encryption, in the real
073: // world you would probably want to be a bit smarter about this.
074: //
075: PGPPublicKey key = null;
076:
077: //
078: // iterate through the key rings.
079: //
080: Iterator rIt = pgpPub.getKeyRings();
081:
082: while (key == null && rIt.hasNext()) {
083: PGPPublicKeyRing kRing = (PGPPublicKeyRing) rIt.next();
084: Iterator kIt = kRing.getPublicKeys();
085:
086: while (key == null && kIt.hasNext()) {
087: PGPPublicKey k = (PGPPublicKey) kIt.next();
088:
089: if (k.isEncryptionKey()) {
090: key = k;
091: }
092: }
093: }
094:
095: if (key == null) {
096: throw new IllegalArgumentException(
097: "Can't find encryption key in key ring.");
098: }
099:
100: return key;
101: }
102:
103: /**
104: * Load a secret key ring collection from keyIn and find the secret key corresponding to
105: * keyID if it exists.
106: *
107: * @param keyIn input stream representing a key ring collection.
108: * @param keyID keyID we want.
109: * @param pass passphrase to decrypt secret key with.
110: * @return
111: * @throws IOException
112: * @throws PGPException
113: * @throws NoSuchProviderException
114: */
115: private static PGPPrivateKey findSecretKey(InputStream keyIn,
116: long keyID, char[] pass) throws IOException, PGPException,
117: NoSuchProviderException {
118: PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
119: PGPUtil.getDecoderStream(keyIn));
120: PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID);
121:
122: if (pgpSecKey == null) {
123: return null;
124: }
125:
126: return pgpSecKey.extractPrivateKey(pass, "BC");
127: }
128:
129: /**
130: * decrypt the passed in message stream
131: */
132: private static void decryptFile(InputStream in, InputStream keyIn,
133: char[] passwd) throws Exception {
134: in = PGPUtil.getDecoderStream(in);
135:
136: try {
137: PGPObjectFactory pgpF = new PGPObjectFactory(in);
138: PGPEncryptedDataList enc;
139:
140: Object o = pgpF.nextObject();
141: //
142: // the first object might be a PGP marker packet.
143: //
144: if (o instanceof PGPEncryptedDataList) {
145: enc = (PGPEncryptedDataList) o;
146: } else {
147: enc = (PGPEncryptedDataList) pgpF.nextObject();
148: }
149:
150: //
151: // find the secret key
152: //
153: Iterator it = enc.getEncryptedDataObjects();
154: PGPPrivateKey sKey = null;
155: PGPPublicKeyEncryptedData pbe = null;
156:
157: while (sKey == null && it.hasNext()) {
158: pbe = (PGPPublicKeyEncryptedData) it.next();
159:
160: sKey = findSecretKey(keyIn, pbe.getKeyID(), passwd);
161: }
162:
163: if (sKey == null) {
164: throw new IllegalArgumentException(
165: "secret key for message not found.");
166: }
167:
168: InputStream clear = pbe.getDataStream(sKey, "BC");
169:
170: PGPObjectFactory plainFact = new PGPObjectFactory(clear);
171:
172: PGPCompressedData cData = (PGPCompressedData) plainFact
173: .nextObject();
174:
175: InputStream compressedStream = new BufferedInputStream(
176: cData.getDataStream());
177: PGPObjectFactory pgpFact = new PGPObjectFactory(
178: compressedStream);
179:
180: Object message = pgpFact.nextObject();
181:
182: if (message instanceof PGPLiteralData) {
183: PGPLiteralData ld = (PGPLiteralData) message;
184:
185: FileOutputStream fOut = new FileOutputStream(ld
186: .getFileName());
187: BufferedOutputStream bOut = new BufferedOutputStream(
188: fOut);
189:
190: InputStream unc = ld.getInputStream();
191: int ch;
192:
193: while ((ch = unc.read()) >= 0) {
194: bOut.write(ch);
195: }
196:
197: bOut.close();
198: } else if (message instanceof PGPOnePassSignatureList) {
199: throw new PGPException(
200: "encrypted message contains a signed message - not literal data.");
201: } else {
202: throw new PGPException(
203: "message is not a simple encrypted file - type unknown.");
204: }
205:
206: if (pbe.isIntegrityProtected()) {
207: if (!pbe.verify()) {
208: System.err
209: .println("message failed integrity check");
210: } else {
211: System.err
212: .println("message integrity check passed");
213: }
214: } else {
215: System.err.println("no message integrity check");
216: }
217: } catch (PGPException e) {
218: System.err.println(e);
219: if (e.getUnderlyingException() != null) {
220: e.getUnderlyingException().printStackTrace();
221: }
222: }
223: }
224:
225: private static void encryptFile(OutputStream out, String fileName,
226: PGPPublicKey encKey, boolean armor,
227: boolean withIntegrityCheck) throws IOException,
228: NoSuchProviderException {
229: if (armor) {
230: out = new ArmoredOutputStream(out);
231: }
232:
233: try {
234: PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(
235: PGPEncryptedData.CAST5, withIntegrityCheck,
236: new SecureRandom(), "BC");
237:
238: cPk.addMethod(encKey);
239:
240: OutputStream cOut = cPk.open(out, new byte[1 << 16]);
241:
242: PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(
243: PGPCompressedData.ZIP);
244:
245: PGPUtil.writeFileToLiteralData(comData.open(cOut),
246: PGPLiteralData.BINARY, new File(fileName),
247: new byte[1 << 16]);
248:
249: comData.close();
250:
251: cOut.close();
252:
253: out.close();
254: } catch (PGPException e) {
255: System.err.println(e);
256: if (e.getUnderlyingException() != null) {
257: e.getUnderlyingException().printStackTrace();
258: }
259: }
260: }
261:
262: public static void main(String[] args) throws Exception {
263: Security.addProvider(new BouncyCastleProvider());
264:
265: if (args.length == 0) {
266: System.err
267: .println("usage: KeyBasedLargeFileProcessor -e|-d [-a|ai] file [secretKeyFile passPhrase|pubKeyFile]");
268: return;
269: }
270:
271: if (args[0].equals("-e")) {
272: if (args[1].equals("-a") || args[1].equals("-ai")
273: || args[1].equals("-ia")) {
274: FileInputStream keyIn = new FileInputStream(args[3]);
275: FileOutputStream out = new FileOutputStream(args[2]
276: + ".asc");
277: encryptFile(out, args[2], readPublicKey(keyIn), true,
278: (args[1].indexOf('i') > 0));
279: } else if (args[1].equals("-i")) {
280: FileInputStream keyIn = new FileInputStream(args[3]);
281: FileOutputStream out = new FileOutputStream(args[2]
282: + ".bpg");
283: encryptFile(out, args[2], readPublicKey(keyIn), false,
284: true);
285: } else {
286: FileInputStream keyIn = new FileInputStream(args[2]);
287: FileOutputStream out = new FileOutputStream(args[1]
288: + ".bpg");
289: encryptFile(out, args[1], readPublicKey(keyIn), false,
290: false);
291: }
292: } else if (args[0].equals("-d")) {
293: FileInputStream in = new FileInputStream(args[1]);
294: FileInputStream keyIn = new FileInputStream(args[2]);
295: decryptFile(in, keyIn, args[3].toCharArray());
296: } else {
297: System.err
298: .println("usage: KeyBasedLargeFileProcessor -d|-e [-a|ai] file [secretKeyFile passPhrase|pubKeyFile]");
299: }
300: }
301: }
|