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