KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > fop > pdf > PDFEncryptionJCE


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 /* $Id: PDFEncryptionJCE.java 426576 2006-07-28 15:44:37Z jeremias $ */
19  
20 package org.apache.fop.pdf;
21
22 // Java
23
import java.io.InputStream JavaDoc;
24 import java.io.OutputStream JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.security.MessageDigest JavaDoc;
27 import java.security.NoSuchAlgorithmException JavaDoc;
28 import java.security.InvalidKeyException JavaDoc;
29 import javax.crypto.Cipher;
30 import javax.crypto.spec.SecretKeySpec;
31 import javax.crypto.CipherOutputStream;
32 import javax.crypto.IllegalBlockSizeException;
33 import javax.crypto.BadPaddingException;
34 import javax.crypto.NoSuchPaddingException;
35
36 import java.util.Random JavaDoc;
37
38 /**
39  * class representing a /Filter /Standard object.
40  *
41  */

42 public class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
43     
44     private class EncryptionFilter extends PDFFilter {
45         private PDFEncryptionJCE encryption;
46         private int number;
47         private int generation;
48
49         /**
50          * The constructor for the internal PDFEncryptionJCE filter
51          * @param encryption The encryption object to use
52          * @param number The number of the object to be encrypted
53          * @param generation The generation of the object to be encrypted
54          */

55         public EncryptionFilter(PDFEncryptionJCE encryption,
56                                 int number, int generation) {
57             super();
58             this.encryption = encryption;
59             this.number = number;
60             this.generation = generation;
61             log.debug("new encryption filter for number "
62                 + number + " and generation " + generation);
63         }
64
65         /**
66          * Return a PDF string representation of the filter. In this
67          * case no filter name is passed.
68          * @return The filter name, blank in this case
69          */

70         public String JavaDoc getName() {
71             return "";
72         }
73
74         /**
75          * Return a parameter dictionary for this filter, or null
76          * @return The parameter dictionary. In this case, null.
77          */

78         public String JavaDoc getDecodeParms() {
79             return null;
80         }
81
82         /**
83          * Encode the given data with the filter
84          * @param data The data to be encrypted
85          * @return The encrypted data
86          */

87         public byte[] encode(byte[] data) {
88             return encryption.encryptData(data, number, generation);
89         }
90         
91         /**
92          * @see org.apache.fop.pdf.PDFFilter#encode(InputStream, OutputStream, int)
93          */

94         public void encode(InputStream JavaDoc in, OutputStream JavaDoc out, int length)
95                                                         throws IOException JavaDoc {
96             byte[] buffer = new byte[length];
97             in.read(buffer);
98             buffer = encode(buffer);
99             out.write(buffer);
100         }
101         
102         /**
103          * @see org.apache.fop.pdf.PDFFilter#applyFilter(OutputStream)
104          */

105         public OutputStream JavaDoc applyFilter(OutputStream JavaDoc out) throws IOException JavaDoc {
106             return new CipherOutputStream(out,
107                     encryption.initCipher(number, generation));
108         }
109
110     }
111
112     private static final char [] PAD
113                              = {0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41,
114                                 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
115                                 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80,
116                                 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A};
117                                   
118     /** Value of PRINT permission */
119     public static final int PERMISSION_PRINT = 4;
120     /** Value of content editting permission */
121     public static final int PERMISSION_EDIT_CONTENT = 8;
122     /** Value of content extraction permission */
123     public static final int PERMISSION_COPY_CONTENT = 16;
124     /** Value of annotation editting permission */
125     public static final int PERMISSION_EDIT_ANNOTATIONS = 32;
126     
127     // Encryption tools
128
private MessageDigest JavaDoc digest = null;
129     //private Cipher cipher = null;
130
private Random JavaDoc random = new Random JavaDoc();
131     // Control attributes
132
private PDFEncryptionParams params;
133     // Output attributes
134
private byte[] fileID = null;
135     private byte[] encryptionKey = null;
136     private String JavaDoc dictionary = null;
137
138     /**
139      * Create a /Filter /Standard object.
140      *
141      * @param objnum the object's number
142      */

143     public PDFEncryptionJCE(int objnum) {
144         /* generic creation of object */
145         super();
146         setObjectNumber(objnum);
147         try {
148             digest = MessageDigest.getInstance("MD5");
149             //cipher = Cipher.getInstance("RC4");
150
} catch (NoSuchAlgorithmException JavaDoc e) {
151             throw new UnsupportedOperationException JavaDoc(e.getMessage());
152         /*} catch (NoSuchPaddingException e) {
153             throw new UnsupportedOperationException(e.getMessage());*/

154         }
155     }
156
157     /**
158      * Local factory method.
159      * @param objnum PDF object number for the encryption object
160      * @param params PDF encryption parameters
161      * @return PDFEncryption the newly created PDFEncryption object
162      */

163     public static PDFEncryption make(int objnum, PDFEncryptionParams params) {
164         PDFEncryptionJCE impl = new PDFEncryptionJCE(objnum);
165         impl.setParams(params);
166         impl.init();
167         return impl;
168     }
169
170
171     /**
172      * Returns the encryption parameters.
173      * @return the encryption parameters
174      */

175     public PDFEncryptionParams getParams() {
176         return this.params;
177     }
178
179     /**
180      * Sets the encryption parameters.
181      * @param params The parameterss to set
182      */

183     public void setParams(PDFEncryptionParams params) {
184         this.params = params;
185     }
186
187     // Internal procedures
188

189     private byte[] prepPassword(String JavaDoc password) {
190         byte[] obuffer = new byte[32];
191         byte[] pbuffer = password.getBytes();
192
193         int i = 0;
194         int j = 0;
195         
196         while (i < obuffer.length && i < pbuffer.length) {
197             obuffer[i] = pbuffer[i];
198             i++;
199         }
200         while (i < obuffer.length) {
201             obuffer[i++] = (byte) PAD[j++];
202         }
203
204         return obuffer;
205     }
206
207     /**
208      * Returns the document file ID
209      * @return The file ID
210      */

211     public byte[] getFileID() {
212         if (fileID == null) {
213             fileID = new byte[16];
214             random.nextBytes(fileID);
215         }
216         
217         return fileID;
218     }
219     
220     /**
221      * This method returns the indexed file ID
222      * @param index The index to access the file ID
223      * @return The file ID
224      */

225     public String JavaDoc getFileID(int index) {
226         if (index == 1) {
227             return PDFText.toHex(getFileID());
228         }
229         
230         byte[] id = new byte[16];
231         random.nextBytes(id);
232         return PDFText.toHex(id);
233     }
234         
235     private byte[] encryptWithKey(byte[] data, byte[] key) {
236         try {
237             final Cipher c = initCipher(key);
238             return c.doFinal(data);
239         } catch (IllegalBlockSizeException e) {
240             throw new IllegalStateException JavaDoc(e.getMessage());
241         } catch (BadPaddingException e) {
242             throw new IllegalStateException JavaDoc(e.getMessage());
243         }
244     }
245     
246     private Cipher initCipher(byte[] key) {
247         try {
248             Cipher c = Cipher.getInstance("RC4");
249             SecretKeySpec keyspec = new SecretKeySpec(key, "RC4");
250             c.init(Cipher.ENCRYPT_MODE, keyspec);
251             return c;
252         } catch (InvalidKeyException JavaDoc e) {
253             throw new IllegalStateException JavaDoc(e.getMessage());
254         } catch (NoSuchAlgorithmException JavaDoc e) {
255             throw new UnsupportedOperationException JavaDoc(e.getMessage());
256         } catch (NoSuchPaddingException e) {
257             throw new UnsupportedOperationException JavaDoc(e.getMessage());
258         }
259     }
260     
261     private Cipher initCipher(int number, int generation) {
262         byte[] hash = calcHash(number, generation);
263         int size = hash.length;
264         hash = digest.digest(hash);
265         byte[] key = calcKey(hash, size);
266         return initCipher(key);
267     }
268     
269     private byte[] encryptWithHash(byte[] data, byte[] hash, int size) {
270         hash = digest.digest(hash);
271
272         byte[] key = calcKey(hash, size);
273         
274         return encryptWithKey(data, key);
275     }
276
277     private byte[] calcKey(byte[] hash, int size) {
278         byte[] key = new byte[size];
279
280         for (int i = 0; i < size; i++) {
281             key[i] = hash[i];
282         }
283         return key;
284     }
285
286     /**
287      * This method initializes the encryption algorithms and values
288      */

289     public void init() {
290         // Generate the owner value
291
byte[] oValue;
292         if (params.getOwnerPassword().length() > 0) {
293             oValue = encryptWithHash(
294                     prepPassword(params.getUserPassword()),
295                     prepPassword(params.getOwnerPassword()), 5);
296         } else {
297             oValue = encryptWithHash(
298                     prepPassword(params.getUserPassword()),
299                     prepPassword(params.getUserPassword()), 5);
300         }
301
302         // Generate permissions value
303
int permissions = -4;
304
305         if (!params.isAllowPrint()) {
306             permissions -= PERMISSION_PRINT;
307         }
308         if (!params.isAllowCopyContent()) {
309             permissions -= PERMISSION_COPY_CONTENT;
310         }
311         if (!params.isAllowEditContent()) {
312             permissions -= PERMISSION_EDIT_CONTENT;
313         }
314         if (!params.isAllowEditAnnotations()) {
315             permissions -= PERMISSION_EDIT_ANNOTATIONS;
316         }
317
318         // Create the encrption key
319
digest.update(prepPassword(params.getUserPassword()));
320         digest.update(oValue);
321         digest.update((byte) (permissions >>> 0));
322         digest.update((byte) (permissions >>> 8));
323         digest.update((byte) (permissions >>> 16));
324         digest.update((byte) (permissions >>> 24));
325         digest.update(getFileID());
326
327         byte [] hash = digest.digest();
328         this.encryptionKey = new byte[5];
329
330         for (int i = 0; i < 5; i++) {
331             this.encryptionKey[i] = hash[i];
332         }
333         
334         // Create the user value
335
byte[] uValue = encryptWithKey(prepPassword(""), this.encryptionKey);
336         
337         // Create the dictionary
338
this.dictionary = getObjectID()
339                         + "<< /Filter /Standard\n"
340                         + "/V 1\n"
341                         + "/R 2\n"
342                         + "/Length 40\n"
343                         + "/P " + permissions + "\n"
344                         + "/O " + PDFText.toHex(oValue) + "\n"
345                         + "/U " + PDFText.toHex(uValue) + "\n"
346                         + ">>\n"
347                         + "endobj\n";
348     }
349
350     /**
351      * This method encrypts the passed data using the generated keys.
352      * @param data The data to be encrypted
353      * @param number The block number
354      * @param generation The block generation
355      * @return The encrypted data
356      */

357     public byte[] encryptData(byte[] data, int number, int generation) {
358         if (this.encryptionKey == null) {
359             throw new IllegalStateException JavaDoc("PDF Encryption has not been initialized");
360         }
361         log.debug("encrypting with for " + number + " " + generation);
362
363         byte[] hash = calcHash(number, generation);
364         return encryptWithHash(data, hash, hash.length);
365     }
366
367     /**
368      * @see org.apache.fop.pdf.PDFEncryption#encrypt(byte[], PDFObject)
369      */

370     public byte[] encrypt(byte[] data, PDFObject refObj) {
371         return encryptData(data, refObj.getObjectNumber(), refObj.getGeneration());
372     }
373
374     private byte[] calcHash(int number, int generation) {
375         byte[] hash = new byte[this.encryptionKey.length + 5];
376             
377         int i = 0;
378         while (i < this.encryptionKey.length) {
379             hash[i] = this.encryptionKey[i]; i++;
380         }
381             
382         hash[i++] = (byte) (number >>> 0);
383         hash[i++] = (byte) (number >>> 8);
384         hash[i++] = (byte) (number >>> 16);
385         hash[i++] = (byte) (generation >>> 0);
386         hash[i++] = (byte) (generation >>> 8);
387         return hash;
388     }
389
390     /**
391      * Creates PDFFilter for the encryption object
392      * @param number The object number
393      * @param generation The objects generation
394      * @return The resulting filter
395      */

396     public PDFFilter makeFilter(int number, int generation) {
397         return new EncryptionFilter(this, number, generation);
398     }
399
400     /**
401      * Adds a PDFFilter to the PDFStream object
402      * @param stream the stream to add an encryption filter to
403      */

404     public void applyFilter(AbstractPDFStream stream) {
405         stream.getFilterList().addFilter(
406                 this.makeFilter(stream.getObjectNumber(), stream.getGeneration()));
407     }
408     
409     /**
410      * Represent the object in PDF
411      *
412      * @return the PDF
413      */

414     public byte[] toPDF() {
415         if (this.dictionary == null) {
416             throw new IllegalStateException JavaDoc("PDF Encryption has not been initialized");
417         }
418         
419         return encode(this.dictionary);
420     }
421
422     /**
423      * @see org.apache.fop.pdf.PDFEncryption#getTrailerEntry()
424      */

425     public String JavaDoc getTrailerEntry() {
426         return "/Encrypt " + getObjectNumber() + " "
427                     + getGeneration() + " R\n"
428                     + "/ID[" + getFileID(1) + getFileID(2) + "]\n";
429     }
430 }
431
Popular Tags