KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > lowagie > text > pdf > PdfEncryption


1 /*
2  * $Id: PdfEncryption.java 2730 2007-04-28 12:52:49Z blowagie $
3  *
4  * Copyright 2001-2006 Paulo Soares
5  *
6  * The contents of this file are subject to the Mozilla Public License Version 1.1
7  * (the "License"); you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at http://www.mozilla.org/MPL/
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the License.
13  *
14  * The Original Code is 'iText, a free JAVA-PDF library'.
15  *
16  * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
17  * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
18  * All Rights Reserved.
19  * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
20  * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
21  *
22  * Contributor(s): all the names of the contributors are added in the source code
23  * where applicable.
24  *
25  * Alternatively, the contents of this file may be used under the terms of the
26  * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
27  * provisions of LGPL are applicable instead of those above. If you wish to
28  * allow use of your version of this file only under the terms of the LGPL
29  * License and not to allow others to use your version of this file under
30  * the MPL, indicate your decision by deleting the provisions above and
31  * replace them with the notice and other provisions required by the LGPL.
32  * If you do not delete the provisions above, a recipient may use your version
33  * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
34  *
35  * This library is free software; you can redistribute it and/or modify it
36  * under the terms of the MPL as stated above or under the terms of the GNU
37  * Library General Public License as published by the Free Software Foundation;
38  * either version 2 of the License, or any later version.
39  *
40  * This library is distributed in the hope that it will be useful, but WITHOUT
41  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
42  * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
43  * details.
44  *
45  * If you didn't download this code from the following link, you should check if
46  * you aren't using an obsolete version:
47  * http://www.lowagie.com/iText/
48  */

49
50 package com.lowagie.text.pdf;
51
52 import com.lowagie.text.pdf.crypto.ARCFOUREncryption;
53
54 import java.io.IOException JavaDoc;
55 import java.io.OutputStream JavaDoc;
56 import java.io.ByteArrayOutputStream JavaDoc;
57 import java.security.MessageDigest JavaDoc;
58 import java.security.cert.Certificate JavaDoc;
59
60 import com.lowagie.text.ExceptionConverter;
61
62 /**
63  *
64  * @author Paulo Soares (psoares@consiste.pt)
65  * @author Kazuya Ujihara
66  */

67 public class PdfEncryption {
68
69     public static final int STANDARD_ENCRYPTION_40 = 2;
70
71     public static final int STANDARD_ENCRYPTION_128 = 3;
72
73     public static final int AES_128 = 4;
74
75     private static final byte[] pad = { (byte) 0x28, (byte) 0xBF, (byte) 0x4E,
76             (byte) 0x5E, (byte) 0x4E, (byte) 0x75, (byte) 0x8A, (byte) 0x41,
77             (byte) 0x64, (byte) 0x00, (byte) 0x4E, (byte) 0x56, (byte) 0xFF,
78             (byte) 0xFA, (byte) 0x01, (byte) 0x08, (byte) 0x2E, (byte) 0x2E,
79             (byte) 0x00, (byte) 0xB6, (byte) 0xD0, (byte) 0x68, (byte) 0x3E,
80             (byte) 0x80, (byte) 0x2F, (byte) 0x0C, (byte) 0xA9, (byte) 0xFE,
81             (byte) 0x64, (byte) 0x53, (byte) 0x69, (byte) 0x7A };
82
83     private static final byte[] salt = { (byte) 0x73, (byte) 0x41, (byte) 0x6c,
84             (byte) 0x54 };
85
86     private static final byte[] metadataPad = { (byte) 255, (byte) 255,
87             (byte) 255, (byte) 255 };
88
89     /** The encryption key for a particular object/generation */
90     byte key[];
91
92     /** The encryption key length for a particular object/generation */
93     int keySize;
94
95     /** The global encryption key */
96     byte mkey[];
97
98     /** Work area to prepare the object/generation bytes */
99     byte extra[] = new byte[5];
100
101     /** The message digest algorithm MD5 */
102     MessageDigest JavaDoc md5;
103
104     /** The encryption key for the owner */
105     byte ownerKey[] = new byte[32];
106
107     /** The encryption key for the user */
108     byte userKey[] = new byte[32];
109
110     /** The public key security handler for certificate encryption */
111     protected PdfPublicKeySecurityHandler publicKeyHandler = null;
112
113     int permissions;
114
115     byte documentID[];
116
117     static long seq = System.currentTimeMillis();
118
119     private int revision;
120
121     private ARCFOUREncryption arcfour = new ARCFOUREncryption();
122
123     /** The generic key length. It may be 40 or 128. */
124     private int keyLength;
125
126     private boolean encryptMetadata;
127
128     private int cryptoMode;
129
130     public PdfEncryption() {
131         try {
132             md5 = MessageDigest.getInstance("MD5");
133         } catch (Exception JavaDoc e) {
134             throw new ExceptionConverter(e);
135         }
136         publicKeyHandler = new PdfPublicKeySecurityHandler();
137     }
138
139     public PdfEncryption(PdfEncryption enc) {
140         this();
141         mkey = (byte[]) enc.mkey.clone();
142         ownerKey = (byte[]) enc.ownerKey.clone();
143         userKey = (byte[]) enc.userKey.clone();
144         permissions = enc.permissions;
145         if (enc.documentID != null)
146             documentID = (byte[]) enc.documentID.clone();
147         revision = enc.revision;
148         keyLength = enc.keyLength;
149         encryptMetadata = enc.encryptMetadata;
150         publicKeyHandler = enc.publicKeyHandler;
151     }
152
153     public void setCryptoMode(int mode, int kl) {
154         cryptoMode = mode;
155         encryptMetadata = (mode & PdfWriter.DO_NOT_ENCRYPT_METADATA) == 0;
156         mode &= PdfWriter.ENCRYPTION_MASK;
157         switch (mode) {
158         case PdfWriter.STANDARD_ENCRYPTION_40:
159             encryptMetadata = true;
160             keyLength = 40;
161             revision = STANDARD_ENCRYPTION_40;
162             break;
163         case PdfWriter.STANDARD_ENCRYPTION_128:
164             if (kl > 0)
165                 keyLength = kl;
166             else
167                 keyLength = 128;
168             revision = STANDARD_ENCRYPTION_128;
169             break;
170         case PdfWriter.ENCRYPTION_AES_128:
171             keyLength = 128;
172             revision = AES_128;
173             break;
174         default:
175             throw new IllegalArgumentException JavaDoc("No valid encryption mode");
176         }
177     }
178
179     public int getCryptoMode() {
180         return cryptoMode;
181     }
182
183     public boolean isMetadataEncrypted() {
184         return encryptMetadata;
185     }
186
187     /**
188      */

189     private byte[] padPassword(byte userPassword[]) {
190         byte userPad[] = new byte[32];
191         if (userPassword == null) {
192             System.arraycopy(pad, 0, userPad, 0, 32);
193         } else {
194             System.arraycopy(userPassword, 0, userPad, 0, Math.min(
195                     userPassword.length, 32));
196             if (userPassword.length < 32)
197                 System.arraycopy(pad, 0, userPad, userPassword.length,
198                         32 - userPassword.length);
199         }
200
201         return userPad;
202     }
203
204     /**
205      */

206     private byte[] computeOwnerKey(byte userPad[], byte ownerPad[]) {
207         byte ownerKey[] = new byte[32];
208
209         byte digest[] = md5.digest(ownerPad);
210         if (revision == STANDARD_ENCRYPTION_128 || revision == AES_128) {
211             byte mkey[] = new byte[keyLength / 8];
212             // only use for the input as many bit as the key consists of
213
for (int k = 0; k < 50; ++k)
214                 System.arraycopy(md5.digest(digest), 0, digest, 0, mkey.length);
215             System.arraycopy(userPad, 0, ownerKey, 0, 32);
216             for (int i = 0; i < 20; ++i) {
217                 for (int j = 0; j < mkey.length; ++j)
218                     mkey[j] = (byte) (digest[j] ^ i);
219                 arcfour.prepareARCFOURKey(mkey);
220                 arcfour.encryptARCFOUR(ownerKey);
221             }
222         } else {
223             arcfour.prepareARCFOURKey(digest, 0, 5);
224             arcfour.encryptARCFOUR(userPad, ownerKey);
225         }
226
227         return ownerKey;
228     }
229
230     /**
231      *
232      * ownerKey, documentID must be setuped
233      */

234     private void setupGlobalEncryptionKey(byte[] documentID, byte userPad[],
235             byte ownerKey[], int permissions) {
236         this.documentID = documentID;
237         this.ownerKey = ownerKey;
238         this.permissions = permissions;
239         // use variable keylength
240
mkey = new byte[keyLength / 8];
241
242         // fixed by ujihara in order to follow PDF refrence
243
md5.reset();
244         md5.update(userPad);
245         md5.update(ownerKey);
246
247         byte ext[] = new byte[4];
248         ext[0] = (byte) permissions;
249         ext[1] = (byte) (permissions >> 8);
250         ext[2] = (byte) (permissions >> 16);
251         ext[3] = (byte) (permissions >> 24);
252         md5.update(ext, 0, 4);
253         if (documentID != null)
254             md5.update(documentID);
255         if (!encryptMetadata)
256             md5.update(metadataPad);
257
258         byte digest[] = new byte[mkey.length];
259         System.arraycopy(md5.digest(), 0, digest, 0, mkey.length);
260
261         // only use the really needed bits as input for the hash
262
if (revision == STANDARD_ENCRYPTION_128 || revision == AES_128) {
263             for (int k = 0; k < 50; ++k)
264                 System.arraycopy(md5.digest(digest), 0, digest, 0, mkey.length);
265         }
266
267         System.arraycopy(digest, 0, mkey, 0, mkey.length);
268     }
269
270     /**
271      *
272      * mkey must be setuped
273      */

274     // use the revision to choose the setup method
275
private void setupUserKey() {
276         if (revision == STANDARD_ENCRYPTION_128 || revision == AES_128) {
277             md5.update(pad);
278             byte digest[] = md5.digest(documentID);
279             System.arraycopy(digest, 0, userKey, 0, 16);
280             for (int k = 16; k < 32; ++k)
281                 userKey[k] = 0;
282             for (int i = 0; i < 20; ++i) {
283                 for (int j = 0; j < mkey.length; ++j)
284                     digest[j] = (byte) (mkey[j] ^ i);
285                 arcfour.prepareARCFOURKey(digest, 0, mkey.length);
286                 arcfour.encryptARCFOUR(userKey, 0, 16);
287             }
288         } else {
289             arcfour.prepareARCFOURKey(mkey);
290             arcfour.encryptARCFOUR(pad, userKey);
291         }
292     }
293
294     // gets keylength and revision and uses revison to choose the initial values
295
// for permissions
296
public void setupAllKeys(byte userPassword[], byte ownerPassword[],
297             int permissions) {
298         if (ownerPassword == null || ownerPassword.length == 0)
299             ownerPassword = md5.digest(createDocumentId());
300         permissions |= (revision == STANDARD_ENCRYPTION_128 || revision == AES_128) ? 0xfffff0c0
301                 : 0xffffffc0;
302         permissions &= 0xfffffffc;
303         // PDF refrence 3.5.2 Standard Security Handler, Algorithum 3.3-1
304
// If there is no owner password, use the user password instead.
305
byte userPad[] = padPassword(userPassword);
306         byte ownerPad[] = padPassword(ownerPassword);
307
308         this.ownerKey = computeOwnerKey(userPad, ownerPad);
309         documentID = createDocumentId();
310         setupByUserPad(this.documentID, userPad, this.ownerKey, permissions);
311     }
312
313     public static byte[] createDocumentId() {
314         MessageDigest JavaDoc md5;
315         try {
316             md5 = MessageDigest.getInstance("MD5");
317         } catch (Exception JavaDoc e) {
318             throw new ExceptionConverter(e);
319         }
320         long time = System.currentTimeMillis();
321         long mem = Runtime.getRuntime().freeMemory();
322         String JavaDoc s = time + "+" + mem + "+" + (seq++);
323         return md5.digest(s.getBytes());
324     }
325
326     /**
327      */

328     public void setupByUserPassword(byte[] documentID, byte userPassword[],
329             byte ownerKey[], int permissions) {
330         setupByUserPad(documentID, padPassword(userPassword), ownerKey,
331                 permissions);
332     }
333
334     /**
335      */

336     private void setupByUserPad(byte[] documentID, byte userPad[],
337             byte ownerKey[], int permissions) {
338         setupGlobalEncryptionKey(documentID, userPad, ownerKey, permissions);
339         setupUserKey();
340     }
341
342     /**
343      */

344     public void setupByOwnerPassword(byte[] documentID, byte ownerPassword[],
345             byte userKey[], byte ownerKey[], int permissions) {
346         setupByOwnerPad(documentID, padPassword(ownerPassword), userKey,
347                 ownerKey, permissions);
348     }
349
350     private void setupByOwnerPad(byte[] documentID, byte ownerPad[],
351             byte userKey[], byte ownerKey[], int permissions) {
352         byte userPad[] = computeOwnerKey(ownerKey, ownerPad); // userPad will
353
// be set in
354
// this.ownerKey
355
setupGlobalEncryptionKey(documentID, userPad, ownerKey, permissions); // step
356
// 3
357
setupUserKey();
358     }
359
360     public void setupByEncryptionKey(byte[] key, int keylength) {
361         mkey = new byte[keylength / 8];
362         System.arraycopy(key, 0, mkey, 0, mkey.length);
363     }
364
365     public void setHashKey(int number, int generation) {
366         md5.reset(); // added by ujihara
367
extra[0] = (byte) number;
368         extra[1] = (byte) (number >> 8);
369         extra[2] = (byte) (number >> 16);
370         extra[3] = (byte) generation;
371         extra[4] = (byte) (generation >> 8);
372         md5.update(mkey);
373         md5.update(extra);
374         if (revision == AES_128)
375             md5.update(salt);
376         key = md5.digest();
377         keySize = mkey.length + 5;
378         if (keySize > 16)
379             keySize = 16;
380     }
381
382     public static PdfObject createInfoId(byte id[]) {
383         ByteBuffer buf = new ByteBuffer(90);
384         buf.append('[').append('<');
385         for (int k = 0; k < 16; ++k)
386             buf.appendHex(id[k]);
387         buf.append('>').append('<');
388         id = createDocumentId();
389         for (int k = 0; k < 16; ++k)
390             buf.appendHex(id[k]);
391         buf.append('>').append(']');
392         return new PdfLiteral(buf.toByteArray());
393     }
394
395     public PdfDictionary getEncryptionDictionary() {
396         PdfDictionary dic = new PdfDictionary();
397
398         if (publicKeyHandler.getRecipientsSize() > 0) {
399             PdfArray recipients = null;
400
401             dic.put(PdfName.FILTER, PdfName.PUBSEC);
402             dic.put(PdfName.R, new PdfNumber(revision));
403
404             try {
405                 recipients = publicKeyHandler.getEncodedRecipients();
406             } catch (Exception JavaDoc f) {
407                 throw new ExceptionConverter(f);
408             }
409
410             if (revision == STANDARD_ENCRYPTION_40) {
411                 dic.put(PdfName.V, new PdfNumber(1));
412                 dic.put(PdfName.SUBFILTER, PdfName.ADBE_PKCS7_S4);
413                 dic.put(PdfName.RECIPIENTS, recipients);
414             } else if (revision == STANDARD_ENCRYPTION_128 && encryptMetadata) {
415                 dic.put(PdfName.V, new PdfNumber(2));
416                 dic.put(PdfName.LENGTH, new PdfNumber(128));
417                 dic.put(PdfName.SUBFILTER, PdfName.ADBE_PKCS7_S4);
418                 dic.put(PdfName.RECIPIENTS, recipients);
419             } else {
420                 dic.put(PdfName.R, new PdfNumber(AES_128));
421                 dic.put(PdfName.V, new PdfNumber(4));
422                 dic.put(PdfName.SUBFILTER, PdfName.ADBE_PKCS7_S5);
423
424                 PdfDictionary stdcf = new PdfDictionary();
425                 stdcf.put(PdfName.RECIPIENTS, recipients);
426                 if (!encryptMetadata)
427                     stdcf.put(PdfName.ENCRYPTMETADATA, PdfBoolean.PDFFALSE);
428
429                 if (revision == AES_128)
430                     stdcf.put(PdfName.CFM, PdfName.AESV2);
431                 else
432                     stdcf.put(PdfName.CFM, PdfName.V2);
433                 PdfDictionary cf = new PdfDictionary();
434                 cf.put(PdfName.DEFAULTCRYPTFILER, stdcf);
435                 dic.put(PdfName.CF, cf);
436                 dic.put(PdfName.STRF, PdfName.DEFAULTCRYPTFILER);
437                 dic.put(PdfName.STMF, PdfName.DEFAULTCRYPTFILER);
438             }
439
440             MessageDigest JavaDoc md = null;
441             byte[] encodedRecipient = null;
442
443             try {
444                 md = MessageDigest.getInstance("SHA-1");
445                 md.update(publicKeyHandler.getSeed());
446                 for (int i = 0; i < publicKeyHandler.getRecipientsSize(); i++) {
447                     encodedRecipient = publicKeyHandler.getEncodedRecipient(i);
448                     md.update(encodedRecipient);
449                 }
450                 if (!encryptMetadata)
451                     md.update(new byte[] { (byte) 255, (byte) 255, (byte) 255,
452                             (byte) 255 });
453             } catch (Exception JavaDoc f) {
454                 throw new ExceptionConverter(f);
455             }
456
457             byte[] mdResult = md.digest();
458
459             setupByEncryptionKey(mdResult, keyLength);
460         } else {
461             dic.put(PdfName.FILTER, PdfName.STANDARD);
462             dic.put(PdfName.O, new PdfLiteral(PdfContentByte
463                     .escapeString(ownerKey)));
464             dic.put(PdfName.U, new PdfLiteral(PdfContentByte
465                     .escapeString(userKey)));
466             dic.put(PdfName.P, new PdfNumber(permissions));
467             dic.put(PdfName.R, new PdfNumber(revision));
468
469             if (revision == STANDARD_ENCRYPTION_40) {
470                 dic.put(PdfName.V, new PdfNumber(1));
471             } else if (revision == STANDARD_ENCRYPTION_128 && encryptMetadata) {
472                 dic.put(PdfName.V, new PdfNumber(2));
473                 dic.put(PdfName.LENGTH, new PdfNumber(128));
474
475             } else {
476                 if (!encryptMetadata)
477                     dic.put(PdfName.ENCRYPTMETADATA, PdfBoolean.PDFFALSE);
478                 dic.put(PdfName.R, new PdfNumber(AES_128));
479                 dic.put(PdfName.V, new PdfNumber(4));
480                 dic.put(PdfName.LENGTH, new PdfNumber(128));
481                 PdfDictionary stdcf = new PdfDictionary();
482                 stdcf.put(PdfName.LENGTH, new PdfNumber(16));
483                 stdcf.put(PdfName.AUTHEVENT, PdfName.DOCOPEN);
484                 if (revision == AES_128)
485                     stdcf.put(PdfName.CFM, PdfName.AESV2);
486                 else
487                     stdcf.put(PdfName.CFM, PdfName.V2);
488                 PdfDictionary cf = new PdfDictionary();
489                 cf.put(PdfName.STDCF, stdcf);
490                 dic.put(PdfName.CF, cf);
491                 dic.put(PdfName.STRF, PdfName.STDCF);
492                 dic.put(PdfName.STMF, PdfName.STDCF);
493             }
494         }
495
496         return dic;
497     }
498
499     public PdfObject getFileID() {
500         return createInfoId(documentID);
501     }
502
503     public OutputStreamEncryption getEncryptionStream(OutputStream JavaDoc os) {
504         return new OutputStreamEncryption(os, key, 0, keySize, revision);
505     }
506
507     public int calculateStreamSize(int n) {
508         if (revision == AES_128)
509             return (n & 0x7ffffff0) + 32;
510         else
511             return n;
512     }
513
514     public byte[] encryptByteArray(byte[] b) {
515         try {
516             ByteArrayOutputStream JavaDoc ba = new ByteArrayOutputStream JavaDoc();
517             OutputStreamEncryption os2 = getEncryptionStream(ba);
518             os2.write(b);
519             os2.finish();
520             return ba.toByteArray();
521         } catch (IOException JavaDoc ex) {
522             throw new ExceptionConverter(ex);
523         }
524     }
525
526     public StandardDecryption getDecryptor() {
527         return new StandardDecryption(key, 0, keySize, revision);
528     }
529
530     public byte[] decryptByteArray(byte[] b) {
531         try {
532             ByteArrayOutputStream JavaDoc ba = new ByteArrayOutputStream JavaDoc();
533             StandardDecryption dec = getDecryptor();
534             byte[] b2 = dec.update(b, 0, b.length);
535             if (b2 != null)
536                 ba.write(b2);
537             b2 = dec.finish();
538             if (b2 != null)
539                 ba.write(b2);
540             return ba.toByteArray();
541         } catch (IOException JavaDoc ex) {
542             throw new ExceptionConverter(ex);
543         }
544     }
545
546     public void addRecipient(Certificate JavaDoc cert, int permission) {
547         documentID = createDocumentId();
548         publicKeyHandler.addRecipient(new PdfPublicKeyRecipient(cert,
549                 permission));
550     }
551
552     public byte[] computeUserPassword(byte[] ownerPassword) {
553         byte[] userPad = computeOwnerKey(ownerKey, padPassword(ownerPassword));
554         for (int i = 0; i < userPad.length; i++) {
555             boolean match = true;
556             for (int j = 0; j < userPad.length - i; j++) {
557                 if (userPad[i + j] != pad[j])
558                     match = false;
559                     break;
560             }
561             if (!match) continue;
562             byte[] userPassword = new byte[i];
563             System.arraycopy(userPad, 0, userPassword, 0, i);
564             return userPassword;
565         }
566         return userPad;
567     }
568 }
Popular Tags