001: package org.bouncycastle.openpgp.examples;
002:
003: import java.io.File;
004: import java.io.FileInputStream;
005: import java.io.FileOutputStream;
006: import java.io.IOException;
007: import java.io.InputStream;
008: import java.io.OutputStream;
009: import java.security.NoSuchAlgorithmException;
010: import java.security.NoSuchProviderException;
011: import java.security.Security;
012: import java.security.SignatureException;
013: import java.util.Iterator;
014:
015: import org.bouncycastle.bcpg.ArmoredOutputStream;
016: import org.bouncycastle.bcpg.BCPGOutputStream;
017:
018: import org.bouncycastle.jce.provider.BouncyCastleProvider;
019: import org.bouncycastle.openpgp.PGPException;
020: import org.bouncycastle.openpgp.PGPLiteralData;
021: import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
022: import org.bouncycastle.openpgp.PGPCompressedData;
023: import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
024: import org.bouncycastle.openpgp.PGPObjectFactory;
025: import org.bouncycastle.openpgp.PGPOnePassSignature;
026: import org.bouncycastle.openpgp.PGPOnePassSignatureList;
027: import org.bouncycastle.openpgp.PGPPrivateKey;
028: import org.bouncycastle.openpgp.PGPPublicKey;
029: import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
030: import org.bouncycastle.openpgp.PGPSecretKey;
031: import org.bouncycastle.openpgp.PGPSecretKeyRing;
032: import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
033: import org.bouncycastle.openpgp.PGPSignature;
034: import org.bouncycastle.openpgp.PGPSignatureGenerator;
035: import org.bouncycastle.openpgp.PGPSignatureList;
036: import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
037: import org.bouncycastle.openpgp.PGPUtil;
038:
039: /**
040: * A simple utility class that signs and verifies files.
041: * <p>
042: * To sign a file: SignedFileProcessor -s [-a] fileName secretKey passPhrase.<br>
043: * If -a is specified the output file will be "ascii-armored".
044: * <p>
045: * To decrypt: SignedFileProcessor -v fileName publicKeyFile.
046: * <p>
047: * <b>Note</b>: this example will silently overwrite files, nor does it pay any attention to
048: * the specification of "_CONSOLE" in the filename. It also expects that a single pass phrase
049: * will have been used.
050: * <p>
051: * <b>Note</b>: the example also makes use of PGP compression. If you are having difficulty getting it
052: * to interoperate with other PGP programs try removing the use of compression first.
053: */
054: public class SignedFileProcessor {
055: /**
056: * A simple routine that opens a key ring file and loads the first available key suitable for
057: * signature generation.
058: *
059: * @param in
060: * @return
061: * @throws IOException
062: * @throws PGPException
063: */
064: private static PGPSecretKey readSecretKey(InputStream in)
065: throws IOException, PGPException {
066: in = PGPUtil.getDecoderStream(in);
067:
068: PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
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: PGPSecretKey key = null;
076:
077: //
078: // iterate through the key rings.
079: //
080: Iterator rIt = pgpSec.getKeyRings();
081:
082: while (key == null && rIt.hasNext()) {
083: PGPSecretKeyRing kRing = (PGPSecretKeyRing) rIt.next();
084: Iterator kIt = kRing.getSecretKeys();
085:
086: while (key == null && kIt.hasNext()) {
087: PGPSecretKey k = (PGPSecretKey) kIt.next();
088:
089: if (k.isSigningKey()) {
090: key = k;
091: }
092: }
093: }
094:
095: if (key == null) {
096: throw new IllegalArgumentException(
097: "Can't find signing key in key ring.");
098: }
099:
100: return key;
101: }
102:
103: /**
104: * verify the passed in file as being correctly signed.
105: */
106: private static void verifyFile(InputStream in, InputStream keyIn)
107: throws Exception {
108: in = PGPUtil.getDecoderStream(in);
109:
110: PGPObjectFactory pgpFact = new PGPObjectFactory(in);
111:
112: PGPCompressedData c1 = (PGPCompressedData) pgpFact.nextObject();
113:
114: pgpFact = new PGPObjectFactory(c1.getDataStream());
115:
116: PGPOnePassSignatureList p1 = (PGPOnePassSignatureList) pgpFact
117: .nextObject();
118:
119: PGPOnePassSignature ops = p1.get(0);
120:
121: PGPLiteralData p2 = (PGPLiteralData) pgpFact.nextObject();
122:
123: InputStream dIn = p2.getInputStream();
124: int ch;
125: PGPPublicKeyRingCollection pgpRing = new PGPPublicKeyRingCollection(
126: PGPUtil.getDecoderStream(keyIn));
127:
128: PGPPublicKey key = pgpRing.getPublicKey(ops.getKeyID());
129: FileOutputStream out = new FileOutputStream(p2.getFileName());
130:
131: ops.initVerify(key, "BC");
132:
133: while ((ch = dIn.read()) >= 0) {
134: ops.update((byte) ch);
135: out.write(ch);
136: }
137:
138: out.close();
139:
140: PGPSignatureList p3 = (PGPSignatureList) pgpFact.nextObject();
141:
142: if (ops.verify(p3.get(0))) {
143: System.out.println("signature verified.");
144: } else {
145: System.out.println("signature verification failed.");
146: }
147: }
148:
149: /**
150: * Generate an encapsulated signed file.
151: *
152: * @param fileName
153: * @param keyIn
154: * @param out
155: * @param pass
156: * @param armor
157: * @throws IOException
158: * @throws NoSuchAlgorithmException
159: * @throws NoSuchProviderException
160: * @throws PGPException
161: * @throws SignatureException
162: */
163: private static void signFile(String fileName, InputStream keyIn,
164: OutputStream out, char[] pass, boolean armor)
165: throws IOException, NoSuchAlgorithmException,
166: NoSuchProviderException, PGPException, SignatureException {
167: if (armor) {
168: out = new ArmoredOutputStream(out);
169: }
170:
171: PGPSecretKey pgpSec = readSecretKey(keyIn);
172: PGPPrivateKey pgpPrivKey = pgpSec.extractPrivateKey(pass, "BC");
173: PGPSignatureGenerator sGen = new PGPSignatureGenerator(pgpSec
174: .getPublicKey().getAlgorithm(), PGPUtil.SHA1, "BC");
175:
176: sGen.initSign(PGPSignature.BINARY_DOCUMENT, pgpPrivKey);
177:
178: Iterator it = pgpSec.getPublicKey().getUserIDs();
179: if (it.hasNext()) {
180: PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
181:
182: spGen.setSignerUserID(false, (String) it.next());
183: sGen.setHashedSubpackets(spGen.generate());
184: }
185:
186: PGPCompressedDataGenerator cGen = new PGPCompressedDataGenerator(
187: PGPCompressedData.ZLIB);
188:
189: BCPGOutputStream bOut = new BCPGOutputStream(cGen.open(out));
190:
191: sGen.generateOnePassVersion(false).encode(bOut);
192:
193: File file = new File(fileName);
194: PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator();
195: OutputStream lOut = lGen
196: .open(bOut, PGPLiteralData.BINARY, file);
197: FileInputStream fIn = new FileInputStream(file);
198: int ch = 0;
199:
200: while ((ch = fIn.read()) >= 0) {
201: lOut.write(ch);
202: sGen.update((byte) ch);
203: }
204:
205: sGen.generate().encode(bOut);
206:
207: lGen.close();
208:
209: cGen.close();
210:
211: out.close();
212: }
213:
214: public static void main(String[] args) throws Exception {
215: Security.addProvider(new BouncyCastleProvider());
216:
217: if (args[0].equals("-s")) {
218: if (args[1].equals("-a")) {
219: FileInputStream keyIn = new FileInputStream(args[3]);
220: FileOutputStream out = new FileOutputStream(args[2]
221: + ".asc");
222:
223: signFile(args[2], keyIn, out, args[4].toCharArray(),
224: true);
225: } else {
226: FileInputStream keyIn = new FileInputStream(args[2]);
227: FileOutputStream out = new FileOutputStream(args[1]
228: + ".bpg");
229:
230: signFile(args[1], keyIn, out, args[3].toCharArray(),
231: false);
232: }
233: } else if (args[0].equals("-v")) {
234: FileInputStream in = new FileInputStream(args[1]);
235: FileInputStream keyIn = new FileInputStream(args[2]);
236:
237: verifyFile(in, keyIn);
238: } else {
239: System.err
240: .println("usage: SignedFileProcessor -v|-s [-a] file keyfile [passPhrase]");
241: }
242: }
243: }
|