001: /*
002: * $Id: PdfEncryption.java 2840 2007-06-14 20:01:11Z psoares33 $
003: *
004: * Copyright 2001-2006 Paulo Soares
005: *
006: * The contents of this file are subject to the Mozilla Public License Version 1.1
007: * (the "License"); you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at http://www.mozilla.org/MPL/
009: *
010: * Software distributed under the License is distributed on an "AS IS" basis,
011: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
012: * for the specific language governing rights and limitations under the License.
013: *
014: * The Original Code is 'iText, a free JAVA-PDF library'.
015: *
016: * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
017: * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
018: * All Rights Reserved.
019: * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
020: * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
021: *
022: * Contributor(s): all the names of the contributors are added in the source code
023: * where applicable.
024: *
025: * Alternatively, the contents of this file may be used under the terms of the
026: * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
027: * provisions of LGPL are applicable instead of those above. If you wish to
028: * allow use of your version of this file only under the terms of the LGPL
029: * License and not to allow others to use your version of this file under
030: * the MPL, indicate your decision by deleting the provisions above and
031: * replace them with the notice and other provisions required by the LGPL.
032: * If you do not delete the provisions above, a recipient may use your version
033: * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
034: *
035: * This library is free software; you can redistribute it and/or modify it
036: * under the terms of the MPL as stated above or under the terms of the GNU
037: * Library General Public License as published by the Free Software Foundation;
038: * either version 2 of the License, or any later version.
039: *
040: * This library is distributed in the hope that it will be useful, but WITHOUT
041: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
042: * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
043: * details.
044: *
045: * If you didn't download this code from the following link, you should check if
046: * you aren't using an obsolete version:
047: * http://www.lowagie.com/iText/
048: */
049:
050: package com.lowagie.text.pdf;
051:
052: import com.lowagie.text.pdf.crypto.ARCFOUREncryption;
053:
054: import java.io.IOException;
055: import java.io.OutputStream;
056: import java.io.ByteArrayOutputStream;
057: import java.security.MessageDigest;
058: import java.security.cert.Certificate;
059:
060: import com.lowagie.text.ExceptionConverter;
061:
062: /**
063: *
064: * @author Paulo Soares (psoares@consiste.pt)
065: * @author Kazuya Ujihara
066: */
067: public class PdfEncryption {
068:
069: public static final int STANDARD_ENCRYPTION_40 = 2;
070:
071: public static final int STANDARD_ENCRYPTION_128 = 3;
072:
073: public static final int AES_128 = 4;
074:
075: private static final byte[] pad = { (byte) 0x28, (byte) 0xBF,
076: (byte) 0x4E, (byte) 0x5E, (byte) 0x4E, (byte) 0x75,
077: (byte) 0x8A, (byte) 0x41, (byte) 0x64, (byte) 0x00,
078: (byte) 0x4E, (byte) 0x56, (byte) 0xFF, (byte) 0xFA,
079: (byte) 0x01, (byte) 0x08, (byte) 0x2E, (byte) 0x2E,
080: (byte) 0x00, (byte) 0xB6, (byte) 0xD0, (byte) 0x68,
081: (byte) 0x3E, (byte) 0x80, (byte) 0x2F, (byte) 0x0C,
082: (byte) 0xA9, (byte) 0xFE, (byte) 0x64, (byte) 0x53,
083: (byte) 0x69, (byte) 0x7A };
084:
085: private static final byte[] salt = { (byte) 0x73, (byte) 0x41,
086: (byte) 0x6c, (byte) 0x54 };
087:
088: private static final byte[] metadataPad = { (byte) 255, (byte) 255,
089: (byte) 255, (byte) 255 };
090:
091: /** The encryption key for a particular object/generation */
092: byte key[];
093:
094: /** The encryption key length for a particular object/generation */
095: int keySize;
096:
097: /** The global encryption key */
098: byte mkey[];
099:
100: /** Work area to prepare the object/generation bytes */
101: byte extra[] = new byte[5];
102:
103: /** The message digest algorithm MD5 */
104: MessageDigest md5;
105:
106: /** The encryption key for the owner */
107: byte ownerKey[] = new byte[32];
108:
109: /** The encryption key for the user */
110: byte userKey[] = new byte[32];
111:
112: /** The public key security handler for certificate encryption */
113: protected PdfPublicKeySecurityHandler publicKeyHandler = null;
114:
115: int permissions;
116:
117: byte documentID[];
118:
119: static long seq = System.currentTimeMillis();
120:
121: private int revision;
122:
123: private ARCFOUREncryption arcfour = new ARCFOUREncryption();
124:
125: /** The generic key length. It may be 40 or 128. */
126: private int keyLength;
127:
128: private boolean encryptMetadata;
129:
130: private int cryptoMode;
131:
132: public PdfEncryption() {
133: try {
134: md5 = MessageDigest.getInstance("MD5");
135: } catch (Exception e) {
136: throw new ExceptionConverter(e);
137: }
138: publicKeyHandler = new PdfPublicKeySecurityHandler();
139: }
140:
141: public PdfEncryption(PdfEncryption enc) {
142: this ();
143: mkey = (byte[]) enc.mkey.clone();
144: ownerKey = (byte[]) enc.ownerKey.clone();
145: userKey = (byte[]) enc.userKey.clone();
146: permissions = enc.permissions;
147: if (enc.documentID != null)
148: documentID = (byte[]) enc.documentID.clone();
149: revision = enc.revision;
150: keyLength = enc.keyLength;
151: encryptMetadata = enc.encryptMetadata;
152: publicKeyHandler = enc.publicKeyHandler;
153: }
154:
155: public void setCryptoMode(int mode, int kl) {
156: cryptoMode = mode;
157: encryptMetadata = (mode & PdfWriter.DO_NOT_ENCRYPT_METADATA) == 0;
158: mode &= PdfWriter.ENCRYPTION_MASK;
159: switch (mode) {
160: case PdfWriter.STANDARD_ENCRYPTION_40:
161: encryptMetadata = true;
162: keyLength = 40;
163: revision = STANDARD_ENCRYPTION_40;
164: break;
165: case PdfWriter.STANDARD_ENCRYPTION_128:
166: if (kl > 0)
167: keyLength = kl;
168: else
169: keyLength = 128;
170: revision = STANDARD_ENCRYPTION_128;
171: break;
172: case PdfWriter.ENCRYPTION_AES_128:
173: keyLength = 128;
174: revision = AES_128;
175: break;
176: default:
177: throw new IllegalArgumentException(
178: "No valid encryption mode");
179: }
180: }
181:
182: public int getCryptoMode() {
183: return cryptoMode;
184: }
185:
186: public boolean isMetadataEncrypted() {
187: return encryptMetadata;
188: }
189:
190: /**
191: */
192: private byte[] padPassword(byte userPassword[]) {
193: byte userPad[] = new byte[32];
194: if (userPassword == null) {
195: System.arraycopy(pad, 0, userPad, 0, 32);
196: } else {
197: System.arraycopy(userPassword, 0, userPad, 0, Math.min(
198: userPassword.length, 32));
199: if (userPassword.length < 32)
200: System.arraycopy(pad, 0, userPad, userPassword.length,
201: 32 - userPassword.length);
202: }
203:
204: return userPad;
205: }
206:
207: /**
208: */
209: private byte[] computeOwnerKey(byte userPad[], byte ownerPad[]) {
210: byte ownerKey[] = new byte[32];
211:
212: byte digest[] = md5.digest(ownerPad);
213: if (revision == STANDARD_ENCRYPTION_128 || revision == AES_128) {
214: byte mkey[] = new byte[keyLength / 8];
215: // only use for the input as many bit as the key consists of
216: for (int k = 0; k < 50; ++k)
217: System.arraycopy(md5.digest(digest), 0, digest, 0,
218: mkey.length);
219: System.arraycopy(userPad, 0, ownerKey, 0, 32);
220: for (int i = 0; i < 20; ++i) {
221: for (int j = 0; j < mkey.length; ++j)
222: mkey[j] = (byte) (digest[j] ^ i);
223: arcfour.prepareARCFOURKey(mkey);
224: arcfour.encryptARCFOUR(ownerKey);
225: }
226: } else {
227: arcfour.prepareARCFOURKey(digest, 0, 5);
228: arcfour.encryptARCFOUR(userPad, ownerKey);
229: }
230:
231: return ownerKey;
232: }
233:
234: /**
235: *
236: * ownerKey, documentID must be setuped
237: */
238: private void setupGlobalEncryptionKey(byte[] documentID,
239: byte userPad[], byte ownerKey[], int permissions) {
240: this .documentID = documentID;
241: this .ownerKey = ownerKey;
242: this .permissions = permissions;
243: // use variable keylength
244: mkey = new byte[keyLength / 8];
245:
246: // fixed by ujihara in order to follow PDF refrence
247: md5.reset();
248: md5.update(userPad);
249: md5.update(ownerKey);
250:
251: byte ext[] = new byte[4];
252: ext[0] = (byte) permissions;
253: ext[1] = (byte) (permissions >> 8);
254: ext[2] = (byte) (permissions >> 16);
255: ext[3] = (byte) (permissions >> 24);
256: md5.update(ext, 0, 4);
257: if (documentID != null)
258: md5.update(documentID);
259: if (!encryptMetadata)
260: md5.update(metadataPad);
261:
262: byte digest[] = new byte[mkey.length];
263: System.arraycopy(md5.digest(), 0, digest, 0, mkey.length);
264:
265: // only use the really needed bits as input for the hash
266: if (revision == STANDARD_ENCRYPTION_128 || revision == AES_128) {
267: for (int k = 0; k < 50; ++k)
268: System.arraycopy(md5.digest(digest), 0, digest, 0,
269: mkey.length);
270: }
271:
272: System.arraycopy(digest, 0, mkey, 0, mkey.length);
273: }
274:
275: /**
276: *
277: * mkey must be setuped
278: */
279: // use the revision to choose the setup method
280: private void setupUserKey() {
281: if (revision == STANDARD_ENCRYPTION_128 || revision == AES_128) {
282: md5.update(pad);
283: byte digest[] = md5.digest(documentID);
284: System.arraycopy(digest, 0, userKey, 0, 16);
285: for (int k = 16; k < 32; ++k)
286: userKey[k] = 0;
287: for (int i = 0; i < 20; ++i) {
288: for (int j = 0; j < mkey.length; ++j)
289: digest[j] = (byte) (mkey[j] ^ i);
290: arcfour.prepareARCFOURKey(digest, 0, mkey.length);
291: arcfour.encryptARCFOUR(userKey, 0, 16);
292: }
293: } else {
294: arcfour.prepareARCFOURKey(mkey);
295: arcfour.encryptARCFOUR(pad, userKey);
296: }
297: }
298:
299: // gets keylength and revision and uses revison to choose the initial values
300: // for permissions
301: public void setupAllKeys(byte userPassword[], byte ownerPassword[],
302: int permissions) {
303: if (ownerPassword == null || ownerPassword.length == 0)
304: ownerPassword = md5.digest(createDocumentId());
305: permissions |= (revision == STANDARD_ENCRYPTION_128 || revision == AES_128) ? 0xfffff0c0
306: : 0xffffffc0;
307: permissions &= 0xfffffffc;
308: // PDF refrence 3.5.2 Standard Security Handler, Algorithum 3.3-1
309: // If there is no owner password, use the user password instead.
310: byte userPad[] = padPassword(userPassword);
311: byte ownerPad[] = padPassword(ownerPassword);
312:
313: this .ownerKey = computeOwnerKey(userPad, ownerPad);
314: documentID = createDocumentId();
315: setupByUserPad(this .documentID, userPad, this .ownerKey,
316: permissions);
317: }
318:
319: public static byte[] createDocumentId() {
320: MessageDigest md5;
321: try {
322: md5 = MessageDigest.getInstance("MD5");
323: } catch (Exception e) {
324: throw new ExceptionConverter(e);
325: }
326: long time = System.currentTimeMillis();
327: long mem = Runtime.getRuntime().freeMemory();
328: String s = time + "+" + mem + "+" + (seq++);
329: return md5.digest(s.getBytes());
330: }
331:
332: /**
333: */
334: public void setupByUserPassword(byte[] documentID,
335: byte userPassword[], byte ownerKey[], int permissions) {
336: setupByUserPad(documentID, padPassword(userPassword), ownerKey,
337: permissions);
338: }
339:
340: /**
341: */
342: private void setupByUserPad(byte[] documentID, byte userPad[],
343: byte ownerKey[], int permissions) {
344: setupGlobalEncryptionKey(documentID, userPad, ownerKey,
345: permissions);
346: setupUserKey();
347: }
348:
349: /**
350: */
351: public void setupByOwnerPassword(byte[] documentID,
352: byte ownerPassword[], byte userKey[], byte ownerKey[],
353: int permissions) {
354: setupByOwnerPad(documentID, padPassword(ownerPassword),
355: userKey, ownerKey, permissions);
356: }
357:
358: private void setupByOwnerPad(byte[] documentID, byte ownerPad[],
359: byte userKey[], byte ownerKey[], int permissions) {
360: byte userPad[] = computeOwnerKey(ownerKey, ownerPad); // userPad will
361: // be set in
362: // this.ownerKey
363: setupGlobalEncryptionKey(documentID, userPad, ownerKey,
364: permissions); // step
365: // 3
366: setupUserKey();
367: }
368:
369: public void setupByEncryptionKey(byte[] key, int keylength) {
370: mkey = new byte[keylength / 8];
371: System.arraycopy(key, 0, mkey, 0, mkey.length);
372: }
373:
374: public void setHashKey(int number, int generation) {
375: md5.reset(); // added by ujihara
376: extra[0] = (byte) number;
377: extra[1] = (byte) (number >> 8);
378: extra[2] = (byte) (number >> 16);
379: extra[3] = (byte) generation;
380: extra[4] = (byte) (generation >> 8);
381: md5.update(mkey);
382: md5.update(extra);
383: if (revision == AES_128)
384: md5.update(salt);
385: key = md5.digest();
386: keySize = mkey.length + 5;
387: if (keySize > 16)
388: keySize = 16;
389: }
390:
391: public static PdfObject createInfoId(byte id[]) {
392: ByteBuffer buf = new ByteBuffer(90);
393: buf.append('[').append('<');
394: for (int k = 0; k < 16; ++k)
395: buf.appendHex(id[k]);
396: buf.append('>').append('<');
397: id = createDocumentId();
398: for (int k = 0; k < 16; ++k)
399: buf.appendHex(id[k]);
400: buf.append('>').append(']');
401: return new PdfLiteral(buf.toByteArray());
402: }
403:
404: public PdfDictionary getEncryptionDictionary() {
405: PdfDictionary dic = new PdfDictionary();
406:
407: if (publicKeyHandler.getRecipientsSize() > 0) {
408: PdfArray recipients = null;
409:
410: dic.put(PdfName.FILTER, PdfName.PUBSEC);
411: dic.put(PdfName.R, new PdfNumber(revision));
412:
413: try {
414: recipients = publicKeyHandler.getEncodedRecipients();
415: } catch (Exception f) {
416: throw new ExceptionConverter(f);
417: }
418:
419: if (revision == STANDARD_ENCRYPTION_40) {
420: dic.put(PdfName.V, new PdfNumber(1));
421: dic.put(PdfName.SUBFILTER, PdfName.ADBE_PKCS7_S4);
422: dic.put(PdfName.RECIPIENTS, recipients);
423: } else if (revision == STANDARD_ENCRYPTION_128
424: && encryptMetadata) {
425: dic.put(PdfName.V, new PdfNumber(2));
426: dic.put(PdfName.LENGTH, new PdfNumber(128));
427: dic.put(PdfName.SUBFILTER, PdfName.ADBE_PKCS7_S4);
428: dic.put(PdfName.RECIPIENTS, recipients);
429: } else {
430: dic.put(PdfName.R, new PdfNumber(AES_128));
431: dic.put(PdfName.V, new PdfNumber(4));
432: dic.put(PdfName.SUBFILTER, PdfName.ADBE_PKCS7_S5);
433:
434: PdfDictionary stdcf = new PdfDictionary();
435: stdcf.put(PdfName.RECIPIENTS, recipients);
436: if (!encryptMetadata)
437: stdcf.put(PdfName.ENCRYPTMETADATA,
438: PdfBoolean.PDFFALSE);
439:
440: if (revision == AES_128)
441: stdcf.put(PdfName.CFM, PdfName.AESV2);
442: else
443: stdcf.put(PdfName.CFM, PdfName.V2);
444: PdfDictionary cf = new PdfDictionary();
445: cf.put(PdfName.DEFAULTCRYPTFILER, stdcf);
446: dic.put(PdfName.CF, cf);
447: dic.put(PdfName.STRF, PdfName.DEFAULTCRYPTFILER);
448: dic.put(PdfName.STMF, PdfName.DEFAULTCRYPTFILER);
449: }
450:
451: MessageDigest md = null;
452: byte[] encodedRecipient = null;
453:
454: try {
455: md = MessageDigest.getInstance("SHA-1");
456: md.update(publicKeyHandler.getSeed());
457: for (int i = 0; i < publicKeyHandler
458: .getRecipientsSize(); i++) {
459: encodedRecipient = publicKeyHandler
460: .getEncodedRecipient(i);
461: md.update(encodedRecipient);
462: }
463: if (!encryptMetadata)
464: md.update(new byte[] { (byte) 255, (byte) 255,
465: (byte) 255, (byte) 255 });
466: } catch (Exception f) {
467: throw new ExceptionConverter(f);
468: }
469:
470: byte[] mdResult = md.digest();
471:
472: setupByEncryptionKey(mdResult, keyLength);
473: } else {
474: dic.put(PdfName.FILTER, PdfName.STANDARD);
475: dic.put(PdfName.O, new PdfLiteral(PdfContentByte
476: .escapeString(ownerKey)));
477: dic.put(PdfName.U, new PdfLiteral(PdfContentByte
478: .escapeString(userKey)));
479: dic.put(PdfName.P, new PdfNumber(permissions));
480: dic.put(PdfName.R, new PdfNumber(revision));
481:
482: if (revision == STANDARD_ENCRYPTION_40) {
483: dic.put(PdfName.V, new PdfNumber(1));
484: } else if (revision == STANDARD_ENCRYPTION_128
485: && encryptMetadata) {
486: dic.put(PdfName.V, new PdfNumber(2));
487: dic.put(PdfName.LENGTH, new PdfNumber(128));
488:
489: } else {
490: if (!encryptMetadata)
491: dic.put(PdfName.ENCRYPTMETADATA,
492: PdfBoolean.PDFFALSE);
493: dic.put(PdfName.R, new PdfNumber(AES_128));
494: dic.put(PdfName.V, new PdfNumber(4));
495: dic.put(PdfName.LENGTH, new PdfNumber(128));
496: PdfDictionary stdcf = new PdfDictionary();
497: stdcf.put(PdfName.LENGTH, new PdfNumber(16));
498: stdcf.put(PdfName.AUTHEVENT, PdfName.DOCOPEN);
499: if (revision == AES_128)
500: stdcf.put(PdfName.CFM, PdfName.AESV2);
501: else
502: stdcf.put(PdfName.CFM, PdfName.V2);
503: PdfDictionary cf = new PdfDictionary();
504: cf.put(PdfName.STDCF, stdcf);
505: dic.put(PdfName.CF, cf);
506: dic.put(PdfName.STRF, PdfName.STDCF);
507: dic.put(PdfName.STMF, PdfName.STDCF);
508: }
509: }
510:
511: return dic;
512: }
513:
514: public PdfObject getFileID() {
515: return createInfoId(documentID);
516: }
517:
518: public OutputStreamEncryption getEncryptionStream(OutputStream os) {
519: return new OutputStreamEncryption(os, key, 0, keySize, revision);
520: }
521:
522: public int calculateStreamSize(int n) {
523: if (revision == AES_128)
524: return (n & 0x7ffffff0) + 32;
525: else
526: return n;
527: }
528:
529: public byte[] encryptByteArray(byte[] b) {
530: try {
531: ByteArrayOutputStream ba = new ByteArrayOutputStream();
532: OutputStreamEncryption os2 = getEncryptionStream(ba);
533: os2.write(b);
534: os2.finish();
535: return ba.toByteArray();
536: } catch (IOException ex) {
537: throw new ExceptionConverter(ex);
538: }
539: }
540:
541: public StandardDecryption getDecryptor() {
542: return new StandardDecryption(key, 0, keySize, revision);
543: }
544:
545: public byte[] decryptByteArray(byte[] b) {
546: try {
547: ByteArrayOutputStream ba = new ByteArrayOutputStream();
548: StandardDecryption dec = getDecryptor();
549: byte[] b2 = dec.update(b, 0, b.length);
550: if (b2 != null)
551: ba.write(b2);
552: b2 = dec.finish();
553: if (b2 != null)
554: ba.write(b2);
555: return ba.toByteArray();
556: } catch (IOException ex) {
557: throw new ExceptionConverter(ex);
558: }
559: }
560:
561: public void addRecipient(Certificate cert, int permission) {
562: documentID = createDocumentId();
563: publicKeyHandler.addRecipient(new PdfPublicKeyRecipient(cert,
564: permission));
565: }
566:
567: public byte[] computeUserPassword(byte[] ownerPassword) {
568: byte[] userPad = computeOwnerKey(ownerKey,
569: padPassword(ownerPassword));
570: for (int i = 0; i < userPad.length; i++) {
571: boolean match = true;
572: for (int j = 0; j < userPad.length - i; j++) {
573: if (userPad[i + j] != pad[j]) {
574: match = false;
575: break;
576: }
577: }
578: if (!match)
579: continue;
580: byte[] userPassword = new byte[i];
581: System.arraycopy(userPad, 0, userPassword, 0, i);
582: return userPassword;
583: }
584: return userPad;
585: }
586: }
|