001: package org.bouncycastle.openpgp.test;
002:
003: import org.bouncycastle.bcpg.ArmoredInputStream;
004: import org.bouncycastle.bcpg.ArmoredOutputStream;
005: import org.bouncycastle.bcpg.BCPGOutputStream;
006: import org.bouncycastle.openpgp.PGPException;
007: import org.bouncycastle.openpgp.PGPObjectFactory;
008: import org.bouncycastle.openpgp.PGPPrivateKey;
009: import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
010: import org.bouncycastle.openpgp.PGPSecretKey;
011: import org.bouncycastle.openpgp.PGPSecretKeyRing;
012: import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
013: import org.bouncycastle.openpgp.PGPSignature;
014: import org.bouncycastle.openpgp.PGPSignatureGenerator;
015: import org.bouncycastle.openpgp.PGPSignatureList;
016: import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
017: import org.bouncycastle.openpgp.PGPUtil;
018: import org.bouncycastle.util.encoders.Base64;
019: import org.bouncycastle.util.test.SimpleTest;
020:
021: import java.io.ByteArrayInputStream;
022: import java.io.ByteArrayOutputStream;
023: import java.io.IOException;
024: import java.io.InputStream;
025: import java.io.OutputStream;
026: import java.security.SignatureException;
027: import java.util.Iterator;
028:
029: public class PGPClearSignedSignatureTest extends SimpleTest {
030: byte[] publicKey = Base64
031: .decode("mQELBEQh2+wBCAD26kte0hO6flr7Y2aetpPYutHY4qsmDPy+GwmmqVeCDkX+"
032: + "r1g7DuFbMhVeu0NkKDnVl7GsJ9VarYsFYyqu0NzLa9XS2qlTIkmJV+2/xKa1"
033: + "tzjn18fT/cnAWL88ZLCOWUr241aPVhLuIc6vpHnySpEMkCh4rvMaimnTrKwO"
034: + "42kgeDGd5cXfs4J4ovRcTbc4hmU2BRVsRjiYMZWWx0kkyL2zDVyaJSs4yVX7"
035: + "Jm4/LSR1uC/wDT0IJJuZT/gQPCMJNMEsVCziRgYkAxQK3OWojPSuv4rXpyd4"
036: + "Gvo6IbvyTgIskfpSkCnQtORNLIudQSuK7pW+LkL62N+ohuKdMvdxauOnAAYp"
037: + "tBNnZ2dnZ2dnZyA8Z2dnQGdnZ2c+iQE2BBMBAgAgBQJEIdvsAhsDBgsJCAcD"
038: + "AgQVAggDBBYCAwECHgECF4AACgkQ4M/Ier3f9xagdAf/fbKWBjLQM8xR7JkR"
039: + "P4ri8YKOQPhK+VrddGUD59/wzVnvaGyl9MZE7TXFUeniQq5iXKnm22EQbYch"
040: + "v2Jcxyt2H9yptpzyh4tP6tEHl1C887p2J4qe7F2ATua9CzVGwXQSUbKtj2fg"
041: + "UZP5SsNp25guhPiZdtkf2sHMeiotmykFErzqGMrvOAUThrO63GiYsRk4hF6r"
042: + "cQ01d+EUVpY/sBcCxgNyOiB7a84sDtrxnX5BTEZDTEj8LvuEyEV3TMUuAjx1"
043: + "7Eyd+9JtKzwV4v3hlTaWOvGro9nPS7YaPuG+RtufzXCUJPbPfTjTvtGOqvEz"
044: + "oztls8tuWA0OGHba9XfX9rfgorACAAM=");
045:
046: byte[] secretKey = Base64
047: .decode("lQOWBEQh2+wBCAD26kte0hO6flr7Y2aetpPYutHY4qsmDPy+GwmmqVeCDkX+"
048: + "r1g7DuFbMhVeu0NkKDnVl7GsJ9VarYsFYyqu0NzLa9XS2qlTIkmJV+2/xKa1"
049: + "tzjn18fT/cnAWL88ZLCOWUr241aPVhLuIc6vpHnySpEMkCh4rvMaimnTrKwO"
050: + "42kgeDGd5cXfs4J4ovRcTbc4hmU2BRVsRjiYMZWWx0kkyL2zDVyaJSs4yVX7"
051: + "Jm4/LSR1uC/wDT0IJJuZT/gQPCMJNMEsVCziRgYkAxQK3OWojPSuv4rXpyd4"
052: + "Gvo6IbvyTgIskfpSkCnQtORNLIudQSuK7pW+LkL62N+ohuKdMvdxauOnAAYp"
053: + "AAf+JCJJeAXEcrTVHotsrRR5idzmg6RK/1MSQUijwPmP7ZGy1BmpAmYUfbxn"
054: + "B56GvXyFV3Pbj9PgyJZGS7cY+l0BF4ZqN9USiQtC9OEpCVT5LVMCFXC/lahC"
055: + "/O3EkjQy0CYK+GwyIXa+Flxcr460L/Hvw2ZEXJZ6/aPdiR+DU1l5h99Zw8V1"
056: + "Y625MpfwN6ufJfqE0HLoqIjlqCfi1iwcKAK2oVx2SwnT1W0NwUUXjagGhD2s"
057: + "VzJVpLqhlwmS0A+RE9Niqrf80/zwE7QNDF2DtHxmMHJ3RY/pfu5u1rrFg9YE"
058: + "lmS60mzOe31CaD8Li0k5YCJBPnmvM9mN3/DWWprSZZKtmQQA96C2/VJF5EWm"
059: + "+/Yxi5J06dG6Bkz311Ui4p2zHm9/4GvTPCIKNpGx9Zn47YFD3tIg3fIBVPOE"
060: + "ktG38pEPx++dSSFF9Ep5UgmYFNOKNUVq3yGpatBtCQBXb1LQLAMBJCJ5TQmk"
061: + "68hMOEaqjMHSOa18cS63INgA6okb/ueAKIHxYQcEAP9DaXu5n9dZQw7pshbN"
062: + "Nu/T5IP0/D/wqM+W5r+j4P1N7PgiAnfKA4JjKrUgl8PGnI2qM/Qu+g3qK++c"
063: + "F1ESHasnJPjvNvY+cfti06xnJVtCB/EBOA2UZkAr//Tqa76xEwYAWRBnO2Y+"
064: + "KIVOT+nMiBFkjPTrNAD6fSr1O4aOueBhBAC6aA35IfjC2h5MYk8+Z+S4io2o"
065: + "mRxUZ/dUuS+kITvWph2e4DT28Xpycpl2n1Pa5dCDO1lRqe/5JnaDYDKqxfmF"
066: + "5tTG8GR4d4nVawwLlifXH5Ll7t5NcukGNMCsGuQAHMy0QHuAaOvMdLs5kGHn"
067: + "8VxfKEVKhVrXsvJSwyXXSBtMtUcRtBNnZ2dnZ2dnZyA8Z2dnQGdnZ2c+iQE2"
068: + "BBMBAgAgBQJEIdvsAhsDBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQ4M/I"
069: + "er3f9xagdAf/fbKWBjLQM8xR7JkRP4ri8YKOQPhK+VrddGUD59/wzVnvaGyl"
070: + "9MZE7TXFUeniQq5iXKnm22EQbYchv2Jcxyt2H9yptpzyh4tP6tEHl1C887p2"
071: + "J4qe7F2ATua9CzVGwXQSUbKtj2fgUZP5SsNp25guhPiZdtkf2sHMeiotmykF"
072: + "ErzqGMrvOAUThrO63GiYsRk4hF6rcQ01d+EUVpY/sBcCxgNyOiB7a84sDtrx"
073: + "nX5BTEZDTEj8LvuEyEV3TMUuAjx17Eyd+9JtKzwV4v3hlTaWOvGro9nPS7Ya"
074: + "PuG+RtufzXCUJPbPfTjTvtGOqvEzoztls8tuWA0OGHba9XfX9rfgorACAAA=");
075:
076: String crOnlyMessage = "\r" + " hello world!\r" + "\r" + "- dash\r";
077:
078: String nlOnlyMessage = "\n" + " hello world!\n" + "\n" + "- dash\n";
079:
080: String crNlMessage = "\r\n" + " hello world!\r\n" + "\r\n"
081: + "- dash\r\n";
082:
083: String crOnlySignedMessage = "-----BEGIN PGP SIGNED MESSAGE-----\r"
084: + "Hash: SHA256\r"
085: + "\r"
086: + "\r"
087: + " hello world!\r"
088: + "\r"
089: + "- - dash\r"
090: + "-----BEGIN PGP SIGNATURE-----\r"
091: + "Version: GnuPG v1.4.2.1 (GNU/Linux)\r"
092: + "\r"
093: + "iQEVAwUBRCNS8+DPyHq93/cWAQi6SwgAj3ItmSLr/sd/ixAQLW7/12jzEjfNmFDt\r"
094: + "WOZpJFmXj0fnMzTrOILVnbxHv2Ru+U8Y1K6nhzFSR7d28n31/XGgFtdohDEaFJpx\r"
095: + "Fl+KvASKIonnpEDjFJsPIvT1/G/eCPalwO9IuxaIthmKj0z44SO1VQtmNKxdLAfK\r"
096: + "+xTnXGawXS1WUE4CQGPM45mIGSqXcYrLtJkAg3jtRa8YRUn2d7b2BtmWH+jVaVuC\r"
097: + "hNrXYv7iHFOu25yRWhUQJisvdC13D/gKIPRvARXPgPhAC2kovIy6VS8tDoyG6Hm5\r"
098: + "dMgLEGhmqsgaetVq1ZIuBZj5S4j2apBJCDpF6GBfpBOfwIZs0Tpmlw==\r"
099: + "=84Nd\r" + "-----END PGP SIGNATURE-----\r";
100:
101: String nlOnlySignedMessage = "-----BEGIN PGP SIGNED MESSAGE-----\n"
102: + "Hash: SHA256\n"
103: + "\n"
104: + "\n"
105: + " hello world!\n"
106: + "\n"
107: + "- - dash\n"
108: + "-----BEGIN PGP SIGNATURE-----\n"
109: + "Version: GnuPG v1.4.2.1 (GNU/Linux)\n"
110: + "\n"
111: + "iQEVAwUBRCNS8+DPyHq93/cWAQi6SwgAj3ItmSLr/sd/ixAQLW7/12jzEjfNmFDt\n"
112: + "WOZpJFmXj0fnMzTrOILVnbxHv2Ru+U8Y1K6nhzFSR7d28n31/XGgFtdohDEaFJpx\n"
113: + "Fl+KvASKIonnpEDjFJsPIvT1/G/eCPalwO9IuxaIthmKj0z44SO1VQtmNKxdLAfK\n"
114: + "+xTnXGawXS1WUE4CQGPM45mIGSqXcYrLtJkAg3jtRa8YRUn2d7b2BtmWH+jVaVuC\n"
115: + "hNrXYv7iHFOu25yRWhUQJisvdC13D/gKIPRvARXPgPhAC2kovIy6VS8tDoyG6Hm5\n"
116: + "dMgLEGhmqsgaetVq1ZIuBZj5S4j2apBJCDpF6GBfpBOfwIZs0Tpmlw==\n"
117: + "=84Nd\n" + "-----END PGP SIGNATURE-----\n";
118:
119: String crNlSignedMessage = "-----BEGIN PGP SIGNED MESSAGE-----\r\n"
120: + "Hash: SHA256\r\n"
121: + "\r\n"
122: + "\r\n"
123: + " hello world!\r\n"
124: + "\r\n"
125: + "- - dash\r\n"
126: + "-----BEGIN PGP SIGNATURE-----\r\n"
127: + "Version: GnuPG v1.4.2.1 (GNU/Linux)\r\n"
128: + "\r\n"
129: + "iQEVAwUBRCNS8+DPyHq93/cWAQi6SwgAj3ItmSLr/sd/ixAQLW7/12jzEjfNmFDt\r\n"
130: + "WOZpJFmXj0fnMzTrOILVnbxHv2Ru+U8Y1K6nhzFSR7d28n31/XGgFtdohDEaFJpx\r\n"
131: + "Fl+KvASKIonnpEDjFJsPIvT1/G/eCPalwO9IuxaIthmKj0z44SO1VQtmNKxdLAfK\r\n"
132: + "+xTnXGawXS1WUE4CQGPM45mIGSqXcYrLtJkAg3jtRa8YRUn2d7b2BtmWH+jVaVuC\r\n"
133: + "hNrXYv7iHFOu25yRWhUQJisvdC13D/gKIPRvARXPgPhAC2kovIy6VS8tDoyG6Hm5\r\n"
134: + "dMgLEGhmqsgaetVq1ZIuBZj5S4j2apBJCDpF6GBfpBOfwIZs0Tpmlw==\r\n"
135: + "=84Nd\r" + "-----END PGP SIGNATURE-----\r\n";
136:
137: String crNlSignedMessageTrailingWhiteSpace = "-----BEGIN PGP SIGNED MESSAGE-----\r\n"
138: + "Hash: SHA256\r\n"
139: + "\r\n"
140: + "\r\n"
141: + " hello world! \t\r\n"
142: + "\r\n"
143: + "- - dash\r\n"
144: + "-----BEGIN PGP SIGNATURE-----\r\n"
145: + "Version: GnuPG v1.4.2.1 (GNU/Linux)\r\n"
146: + "\r\n"
147: + "iQEVAwUBRCNS8+DPyHq93/cWAQi6SwgAj3ItmSLr/sd/ixAQLW7/12jzEjfNmFDt\r\n"
148: + "WOZpJFmXj0fnMzTrOILVnbxHv2Ru+U8Y1K6nhzFSR7d28n31/XGgFtdohDEaFJpx\r\n"
149: + "Fl+KvASKIonnpEDjFJsPIvT1/G/eCPalwO9IuxaIthmKj0z44SO1VQtmNKxdLAfK\r\n"
150: + "+xTnXGawXS1WUE4CQGPM45mIGSqXcYrLtJkAg3jtRa8YRUn2d7b2BtmWH+jVaVuC\r\n"
151: + "hNrXYv7iHFOu25yRWhUQJisvdC13D/gKIPRvARXPgPhAC2kovIy6VS8tDoyG6Hm5\r\n"
152: + "dMgLEGhmqsgaetVq1ZIuBZj5S4j2apBJCDpF6GBfpBOfwIZs0Tpmlw==\r\n"
153: + "=84Nd\r" + "-----END PGP SIGNATURE-----\r\n";
154:
155: public String getName() {
156: return "PGPClearSignedSignature";
157: }
158:
159: private void messageTest(String message, String type)
160: throws Exception {
161: ArmoredInputStream aIn = new ArmoredInputStream(
162: new ByteArrayInputStream(message.getBytes()));
163:
164: String[] headers = aIn.getArmorHeaders();
165:
166: if (headers == null || headers.length != 1) {
167: fail("wrong number of headers found");
168: }
169:
170: if (!"Hash: SHA256".equals(headers[0])) {
171: fail("header value wrong: " + headers[0]);
172: }
173:
174: //
175: // read the input, making sure we ingore the last newline.
176: //
177: ByteArrayOutputStream bOut = new ByteArrayOutputStream();
178: int ch;
179:
180: while ((ch = aIn.read()) >= 0 && aIn.isClearText()) {
181: bOut.write((byte) ch);
182: }
183:
184: PGPPublicKeyRingCollection pgpRings = new PGPPublicKeyRingCollection(
185: publicKey);
186:
187: PGPObjectFactory pgpFact = new PGPObjectFactory(aIn);
188: PGPSignatureList p3 = (PGPSignatureList) pgpFact.nextObject();
189: PGPSignature sig = p3.get(0);
190:
191: sig.initVerify(pgpRings.getPublicKey(sig.getKeyID()), "BC");
192:
193: ByteArrayOutputStream lineOut = new ByteArrayOutputStream();
194: InputStream sigIn = new ByteArrayInputStream(bOut.toByteArray());
195: int lookAhead = readInputLine(lineOut, sigIn);
196:
197: processLine(sig, lineOut.toByteArray());
198:
199: if (lookAhead != -1) {
200: do {
201: lookAhead = readInputLine(lineOut, lookAhead, sigIn);
202:
203: sig.update((byte) '\r');
204: sig.update((byte) '\n');
205:
206: processLine(sig, lineOut.toByteArray());
207: } while (lookAhead != -1);
208: }
209:
210: if (!sig.verify()) {
211: fail("signature failed to verify in " + type);
212: }
213: }
214:
215: private PGPSecretKey readSecretKey(InputStream in)
216: throws IOException, PGPException {
217: PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
218: in);
219:
220: PGPSecretKey key = null;
221:
222: //
223: // iterate through the key rings.
224: //
225: Iterator rIt = pgpSec.getKeyRings();
226:
227: while (key == null && rIt.hasNext()) {
228: PGPSecretKeyRing kRing = (PGPSecretKeyRing) rIt.next();
229: Iterator kIt = kRing.getSecretKeys();
230:
231: while (key == null && kIt.hasNext()) {
232: PGPSecretKey k = (PGPSecretKey) kIt.next();
233:
234: if (k.isSigningKey()) {
235: key = k;
236: }
237: }
238: }
239:
240: if (key == null) {
241: throw new IllegalArgumentException(
242: "Can't find signing key in key ring.");
243: }
244:
245: return key;
246: }
247:
248: private void generateTest(String message, String type)
249: throws Exception {
250: PGPSecretKey pgpSecKey = readSecretKey(new ByteArrayInputStream(
251: secretKey));
252: PGPPrivateKey pgpPrivKey = pgpSecKey.extractPrivateKey(""
253: .toCharArray(), "BC");
254: PGPSignatureGenerator sGen = new PGPSignatureGenerator(
255: pgpSecKey.getPublicKey().getAlgorithm(),
256: PGPUtil.SHA256, "BC");
257: PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
258:
259: sGen.initSign(PGPSignature.CANONICAL_TEXT_DOCUMENT, pgpPrivKey);
260:
261: Iterator it = pgpSecKey.getPublicKey().getUserIDs();
262: if (it.hasNext()) {
263: spGen.setSignerUserID(false, (String) it.next());
264: sGen.setHashedSubpackets(spGen.generate());
265: }
266:
267: ByteArrayOutputStream bOut = new ByteArrayOutputStream();
268: ArmoredOutputStream aOut = new ArmoredOutputStream(bOut);
269: ByteArrayInputStream bIn = new ByteArrayInputStream(message
270: .getBytes());
271:
272: aOut.beginClearText(PGPUtil.SHA256);
273:
274: //
275: // note the last \n in the file is ignored
276: //
277: ByteArrayOutputStream lineOut = new ByteArrayOutputStream();
278: int lookAhead = readInputLine(lineOut, bIn);
279:
280: processLine(aOut, sGen, lineOut.toByteArray());
281:
282: if (lookAhead != -1) {
283: do {
284: lookAhead = readInputLine(lineOut, lookAhead, bIn);
285:
286: sGen.update((byte) '\r');
287: sGen.update((byte) '\n');
288:
289: processLine(aOut, sGen, lineOut.toByteArray());
290: } while (lookAhead != -1);
291: }
292:
293: aOut.endClearText();
294:
295: BCPGOutputStream bcpgOut = new BCPGOutputStream(aOut);
296:
297: sGen.generate().encode(bcpgOut);
298:
299: aOut.close();
300:
301: messageTest(new String(bOut.toByteArray()), type);
302: }
303:
304: private static int readInputLine(ByteArrayOutputStream bOut,
305: InputStream fIn) throws IOException {
306: bOut.reset();
307:
308: int lookAhead = -1;
309: int ch;
310:
311: while ((ch = fIn.read()) >= 0) {
312: bOut.write(ch);
313: if (ch == '\r' || ch == '\n') {
314: lookAhead = readPassedEOL(bOut, ch, fIn);
315: break;
316: }
317: }
318:
319: return lookAhead;
320: }
321:
322: private static int readInputLine(ByteArrayOutputStream bOut,
323: int lookAhead, InputStream fIn) throws IOException {
324: bOut.reset();
325:
326: int ch = lookAhead;
327:
328: do {
329: bOut.write(ch);
330: if (ch == '\r' || ch == '\n') {
331: lookAhead = readPassedEOL(bOut, ch, fIn);
332: break;
333: }
334: } while ((ch = fIn.read()) >= 0);
335:
336: return lookAhead;
337: }
338:
339: private static int readPassedEOL(ByteArrayOutputStream bOut,
340: int lastCh, InputStream fIn) throws IOException {
341: int lookAhead = fIn.read();
342:
343: if (lastCh == '\r' && lookAhead == '\n') {
344: bOut.write(lookAhead);
345: lookAhead = fIn.read();
346: }
347:
348: return lookAhead;
349: }
350:
351: private static void processLine(PGPSignature sig, byte[] line)
352: throws SignatureException, IOException {
353: int length = getLengthWithoutWhiteSpace(line);
354: if (length > 0) {
355: sig.update(line, 0, length);
356: }
357: }
358:
359: private static void processLine(OutputStream aOut,
360: PGPSignatureGenerator sGen, byte[] line)
361: throws SignatureException, IOException {
362: int length = getLengthWithoutWhiteSpace(line);
363: if (length > 0) {
364: sGen.update(line, 0, length);
365: }
366:
367: aOut.write(line, 0, line.length);
368: }
369:
370: private static int getLengthWithoutWhiteSpace(byte[] line) {
371: int end = line.length - 1;
372:
373: while (end >= 0 && isWhiteSpace(line[end])) {
374: end--;
375: }
376:
377: return end + 1;
378: }
379:
380: private static boolean isWhiteSpace(byte b) {
381: return b == '\r' || b == '\n' || b == '\t' || b == ' ';
382: }
383:
384: public void performTest() throws Exception {
385: messageTest(crOnlySignedMessage, "\\r");
386: messageTest(nlOnlySignedMessage, "\\n");
387: messageTest(crNlSignedMessage, "\\r\\n");
388: messageTest(crNlSignedMessageTrailingWhiteSpace, "\\r\\n");
389:
390: generateTest(nlOnlyMessage, "\\r");
391: generateTest(crOnlyMessage, "\\n");
392: generateTest(crNlMessage, "\\r\\n");
393: }
394:
395: public static void main(String[] args) {
396: runTest(new PGPClearSignedSignatureTest());
397: }
398: }
|