KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > pdfbox > pdmodel > encryption > SecurityHandler


1 /**
2  * Copyright (c) 2003-2006, www.pdfbox.org
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  * 3. Neither the name of pdfbox; nor the names of its
14  * contributors may be used to endorse or promote products derived from this
15  * software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
24  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * http://www.pdfbox.org
29  *
30  */

31
32 package org.pdfbox.pdmodel.encryption;
33
34 import java.io.ByteArrayInputStream JavaDoc;
35 import java.io.ByteArrayOutputStream JavaDoc;
36 import java.io.IOException JavaDoc;
37 import java.io.InputStream JavaDoc;
38 import java.io.OutputStream JavaDoc;
39 import java.security.MessageDigest JavaDoc;
40 import java.security.NoSuchAlgorithmException JavaDoc;
41 import java.util.HashSet JavaDoc;
42 import java.util.Iterator JavaDoc;
43 import java.util.List JavaDoc;
44 import java.util.Set JavaDoc;
45
46 import org.pdfbox.cos.COSArray;
47 import org.pdfbox.cos.COSBase;
48 import org.pdfbox.cos.COSDictionary;
49 import org.pdfbox.cos.COSName;
50 import org.pdfbox.cos.COSObject;
51 import org.pdfbox.cos.COSStream;
52 import org.pdfbox.cos.COSString;
53 import org.pdfbox.encryption.ARCFour;
54 import org.pdfbox.exceptions.CryptographyException;
55 import org.pdfbox.pdmodel.PDDocument;
56
57 /**
58  * This class represents a security handler as described in the PDF specifications.
59  * A security handler is responsible of documents protection.
60  *
61  * @author <a HREF="mailto:ben@benlitchfield.com">Ben Litchfield</a>
62  * @author Benoit Guillon (benoit.guillon@snv.jussieu.fr)
63  *
64  * @version $Revision: 1.4 $
65  */

66
67 public abstract class SecurityHandler
68 {
69     
70     /* ------------------------------------------------
71      * CONSTANTS
72      -------------------------------------------------- */

73     
74     private static final int DEFAULT_KEY_LENGTH = 40;
75     
76     /**
77      * The value of V field of the Encryption dictionary.
78      */

79     protected int version;
80     
81     /**
82      * The length of the secret key used to encrypt the document.
83      */

84     protected int keyLength = DEFAULT_KEY_LENGTH;
85     
86     /**
87      * The encryption key that will used to encrypt / decrypt.
88      */

89     protected byte[] encryptionKey;
90     
91     /**
92      * The document whose security is handled by this security handler.
93      */

94     
95     protected PDDocument document;
96     
97     /**
98      * The RC4 implementation used for cryptographic functions.
99      */

100     protected ARCFour rc4 = new ARCFour();
101     
102     private Set JavaDoc objects = new HashSet JavaDoc();
103     
104     private Set JavaDoc potentialSignatures = new HashSet JavaDoc();
105     
106     /**
107      * The access permission granted to the current user for the document. These
108      * permissions are computed during decryption and are in read only mode.
109      */

110     
111     protected AccessPermission currentAccessPermission = null;
112     
113     /**
114      * Prepare the document for encryption.
115      *
116      * @param doc The document that will be encrypted.
117      *
118      * @throws CryptographyException If there is an error while preparing.
119      * @throws IOException If there is an error with the document.
120      */

121     public abstract void prepareDocumentForEncryption(PDDocument doc) throws CryptographyException, IOException JavaDoc;
122     
123     /**
124      * Prepare the document for decryption.
125      *
126      * @param doc The document to decrypt.
127      * @param mat Information required to decrypt the document.
128      * @throws CryptographyException If there is an error while preparing.
129      * @throws IOException If there is an error with the document.
130      */

131     public abstract void decryptDocument(PDDocument doc, DecryptionMaterial mat)
132         throws CryptographyException, IOException JavaDoc;
133     
134     
135     /**
136      * This method must be called by an implementation of this class to really proceed
137      * to decryption.
138      *
139      * @throws IOException If there is an error in the decryption.
140      * @throws CryptographyException If there is an error in the decryption.
141      */

142     protected void proceedDecryption() throws IOException JavaDoc, CryptographyException
143     {
144     
145         COSDictionary trailer = document.getDocument().getTrailer();
146         COSArray fields = (COSArray)trailer.getObjectFromPath( "Root/AcroForm/Fields" );
147     
148         //We need to collect all the signature dictionaries, for some
149
//reason the 'Contents' entry of signatures is not really encrypted
150
if( fields != null )
151         {
152             for( int i=0; i<fields.size(); i++ )
153             {
154                 COSDictionary field = (COSDictionary)fields.getObject( i );
155                 addDictionaryAndSubDictionary( potentialSignatures, field );
156             }
157         }
158
159         List JavaDoc allObjects = document.getDocument().getObjects();
160         Iterator JavaDoc objectIter = allObjects.iterator();
161         while( objectIter.hasNext() )
162         {
163             decryptObject( (COSObject)objectIter.next() );
164         }
165         document.setEncryptionDictionary( null );
166     }
167     
168     private void addDictionaryAndSubDictionary( Set JavaDoc set, COSDictionary dic )
169     {
170         set.add( dic );
171         COSArray kids = (COSArray)dic.getDictionaryObject( "Kids" );
172         for( int i=0; kids != null && i<kids.size(); i++ )
173         {
174             addDictionaryAndSubDictionary( set, (COSDictionary)kids.getObject( i ) );
175         }
176         COSBase value = dic.getDictionaryObject( "V" );
177         if( value instanceof COSDictionary )
178         {
179             addDictionaryAndSubDictionary( set, (COSDictionary)value );
180         }
181     }
182     
183     
184     /**
185      * Encrypt a set of data.
186      *
187      * @param objectNumber The data object number.
188      * @param genNumber The data generation number.
189      * @param data The data to encrypt.
190      * @param output The output to write the encrypted data to.
191      *
192      * @throws CryptographyException If there is an error during the encryption.
193      * @throws IOException If there is an error reading the data.
194      */

195     public void encryptData(long objectNumber, long genNumber, InputStream JavaDoc data, OutputStream JavaDoc output)
196         throws CryptographyException, IOException JavaDoc
197     {
198         byte[] newKey = new byte[ encryptionKey.length + 5 ];
199         System.arraycopy( encryptionKey, 0, newKey, 0, encryptionKey.length );
200         //PDF 1.4 reference pg 73
201
//step 1
202
//we have the reference
203

204         //step 2
205
newKey[newKey.length -5] = (byte)(objectNumber & 0xff);
206         newKey[newKey.length -4] = (byte)((objectNumber >> 8) & 0xff);
207         newKey[newKey.length -3] = (byte)((objectNumber >> 16) & 0xff);
208         newKey[newKey.length -2] = (byte)(genNumber & 0xff);
209         newKey[newKey.length -1] = (byte)((genNumber >> 8) & 0xff);
210
211
212         //step 3
213
byte[] digestedKey = null;
214         try
215         {
216             MessageDigest JavaDoc md = MessageDigest.getInstance( "MD5" );
217             digestedKey = md.digest( newKey );
218         }
219         catch( NoSuchAlgorithmException JavaDoc e )
220         {
221             throw new CryptographyException( e );
222         }
223
224         //step 4
225
int length = Math.min( newKey.length, 16 );
226         byte[] finalKey = new byte[ length ];
227         System.arraycopy( digestedKey, 0, finalKey, 0, length );
228
229         rc4.setKey( finalKey );
230         rc4.write( data, output );
231         output.flush();
232         
233     }
234     
235     
236     /**
237      * This will decrypt an object in the document.
238      *
239      * @param object The object to decrypt.
240      *
241      * @throws CryptographyException If there is an error decrypting the stream.
242      * @throws IOException If there is an error getting the stream data.
243      */

244     private void decryptObject( COSObject object )
245         throws CryptographyException, IOException JavaDoc
246     {
247         long objNum = object.getObjectNumber().intValue();
248         long genNum = object.getGenerationNumber().intValue();
249         COSBase base = object.getObject();
250         decrypt( base, objNum, genNum );
251     }
252
253     /**
254      * This will dispatch to the correct method.
255      *
256      * @param obj The object to decrypt.
257      * @param objNum The object number.
258      * @param genNum The object generation Number.
259      *
260      * @throws CryptographyException If there is an error decrypting the stream.
261      * @throws IOException If there is an error getting the stream data.
262      */

263     private void decrypt( Object JavaDoc obj, long objNum, long genNum )
264         throws CryptographyException, IOException JavaDoc
265     {
266         if( !objects.contains( obj ) )
267         {
268             objects.add( obj );
269
270             if( obj instanceof COSString )
271             {
272                 decryptString( (COSString)obj, objNum, genNum );
273             }
274             else if( obj instanceof COSStream )
275             {
276                 decryptStream( (COSStream)obj, objNum, genNum );
277             }
278             else if( obj instanceof COSDictionary )
279             {
280                 decryptDictionary( (COSDictionary)obj, objNum, genNum );
281             }
282             else if( obj instanceof COSArray )
283             {
284                 decryptArray( (COSArray)obj, objNum, genNum );
285             }
286         }
287     }
288
289     /**
290      * This will decrypt a stream.
291      *
292      * @param stream The stream to decrypt.
293      * @param objNum The object number.
294      * @param genNum The object generation number.
295      *
296      * @throws CryptographyException If there is an error getting the stream.
297      * @throws IOException If there is an error getting the stream data.
298      */

299     public void decryptStream( COSStream stream, long objNum, long genNum )
300         throws CryptographyException, IOException JavaDoc
301     {
302         decryptDictionary( stream, objNum, genNum );
303         InputStream JavaDoc encryptedStream = stream.getFilteredStream();
304         encryptData( objNum,
305                                 genNum,
306                                 encryptedStream,
307                                 stream.createFilteredStream() );
308     }
309
310     /**
311      * This will decrypt a dictionary.
312      *
313      * @param dictionary The dictionary to decrypt.
314      * @param objNum The object number.
315      * @param genNum The object generation number.
316      *
317      * @throws CryptographyException If there is an error decrypting the document.
318      * @throws IOException If there is an error creating a new string.
319      */

320     private void decryptDictionary( COSDictionary dictionary, long objNum, long genNum )
321         throws CryptographyException, IOException JavaDoc
322     {
323         Iterator JavaDoc keys = dictionary.keyList().iterator();
324         while( keys.hasNext() )
325         {
326             COSName key = (COSName)keys.next();
327             Object JavaDoc value = dictionary.getItem( key );
328             //if we are a signature dictionary and contain a Contents entry then
329
//we don't decrypt it.
330
if( !(key.getName().equals( "Contents" ) &&
331                   value instanceof COSString &&
332                   potentialSignatures.contains( dictionary )))
333             {
334                 decrypt( value, objNum, genNum );
335             }
336         }
337     }
338
339     /**
340      * This will decrypt a string.
341      *
342      * @param string the string to decrypt.
343      * @param objNum The object number.
344      * @param genNum The object generation number.
345      *
346      * @throws CryptographyException If an error occurs during decryption.
347      * @throws IOException If an error occurs writing the new string.
348      */

349     public void decryptString( COSString string, long objNum, long genNum )
350         throws CryptographyException, IOException JavaDoc
351     {
352         ByteArrayInputStream JavaDoc data = new ByteArrayInputStream JavaDoc( string.getBytes() );
353         ByteArrayOutputStream JavaDoc buffer = new ByteArrayOutputStream JavaDoc();
354         encryptData( objNum, genNum, data, buffer );
355         string.reset();
356         string.append( buffer.toByteArray() );
357     }
358
359     /**
360      * This will decrypt an array.
361      *
362      * @param array The array to decrypt.
363      * @param objNum The object number.
364      * @param genNum The object generation number.
365      *
366      * @throws CryptographyException If an error occurs during decryption.
367      * @throws IOException If there is an error accessing the data.
368      */

369     private void decryptArray( COSArray array, long objNum, long genNum )
370         throws CryptographyException, IOException JavaDoc
371     {
372         for( int i=0; i<array.size(); i++ )
373         {
374             decrypt( array.get( i ), objNum, genNum );
375         }
376     }
377         
378     /**
379      * Getter of the property <tt>keyLength</tt>.
380      * @return Returns the keyLength.
381      * @uml.property name="keyLength"
382      */

383     public int getKeyLength()
384     {
385         return keyLength;
386     }
387     
388     /**
389      * Setter of the property <tt>keyLength</tt>.
390      *
391      * @param keyLen The keyLength to set.
392      */

393     public void setKeyLength(int keyLen)
394     {
395         this.keyLength = keyLen;
396     }
397     
398     /**
399      * Returns the access permissions that were computed during document decryption.
400      * The returned object is in read only mode.
401      *
402      * @return the access permissions or null if the document was not decrypted.
403      */

404     public AccessPermission getCurrentAccessPermission()
405     {
406         return currentAccessPermission;
407     }
408 }
Popular Tags