KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > pdfbox > encryption > DocumentEncryption


1 /**
2  * Copyright (c) 2003-2004, 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 package org.pdfbox.encryption;
32
33 import java.io.ByteArrayInputStream JavaDoc;
34 import java.io.ByteArrayOutputStream JavaDoc;
35 import java.io.IOException JavaDoc;
36 import java.io.InputStream JavaDoc;
37 import java.math.BigInteger JavaDoc;
38 import java.security.MessageDigest JavaDoc;
39 import java.security.NoSuchAlgorithmException JavaDoc;
40 import java.util.HashSet JavaDoc;
41 import java.util.Iterator JavaDoc;
42 import java.util.List JavaDoc;
43 import java.util.Set JavaDoc;
44
45 import org.pdfbox.cos.COSArray;
46 import org.pdfbox.cos.COSBase;
47 import org.pdfbox.cos.COSDictionary;
48 import org.pdfbox.cos.COSDocument;
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.exceptions.CryptographyException;
54 import org.pdfbox.exceptions.InvalidPasswordException;
55 import org.pdfbox.pdmodel.PDDocument;
56 import org.pdfbox.pdmodel.encryption.PDStandardEncryption;
57
58 /**
59  * This class will deal with encrypting/decrypting a document.
60  *
61  * @author <a HREF="mailto:ben@benlitchfield.com">Ben Litchfield</a>
62  * @version $Revision: 1.13 $
63  *
64  * @deprecated use the new security API instead.
65  *
66  * @see org.pdfbox.pdmodel.encryption.StandardSecurityHandler
67  */

68 public class DocumentEncryption
69 {
70     private PDDocument pdDocument = null;
71     private COSDocument document = null;
72
73     private byte[] encryptionKey = null;
74     private PDFEncryption encryption = new PDFEncryption();
75
76     private Set JavaDoc objects = new HashSet JavaDoc();
77     
78     /**
79      * A set that contains potential signature dictionaries. This is used
80      * because the Contents entry of the signature is not encrypted.
81      */

82     private Set JavaDoc potentialSignatures = new HashSet JavaDoc();
83
84     /**
85      * Constructor.
86      *
87      * @param doc The document to decrypt.
88      */

89     public DocumentEncryption( PDDocument doc )
90     {
91         pdDocument = doc;
92         document = doc.getDocument();
93     }
94
95     /**
96      * Constructor.
97      *
98      * @param doc The document to decrypt.
99      */

100     public DocumentEncryption( COSDocument doc )
101     {
102         pdDocument = new PDDocument( doc );
103         document = doc;
104     }
105
106     /**
107      * This will encrypt the given document, given the owner password and user password.
108      * The encryption method used is the standard filter.
109      *
110      * @throws CryptographyException If an error occurs during encryption.
111      * @throws IOException If there is an error accessing the data.
112      */

113     public void initForEncryption()
114         throws CryptographyException, IOException JavaDoc
115     {
116         String JavaDoc ownerPassword = pdDocument.getOwnerPasswordForEncryption();
117         String JavaDoc userPassword = pdDocument.getUserPasswordForEncryption();
118         if( ownerPassword == null )
119         {
120             ownerPassword = "";
121         }
122         if( userPassword == null )
123         {
124             userPassword = "";
125         }
126         PDStandardEncryption encParameters = (PDStandardEncryption)pdDocument.getEncryptionDictionary();
127         int permissionInt = encParameters.getPermissions();
128         int revision = encParameters.getRevision();
129         int length = encParameters.getLength()/8;
130         COSArray idArray = document.getDocumentID();
131
132         //check if the document has an id yet. If it does not then
133
//generate one
134
if( idArray == null || idArray.size() < 2 )
135         {
136             idArray = new COSArray();
137             try
138             {
139                 MessageDigest JavaDoc md = MessageDigest.getInstance( "MD5" );
140                 BigInteger JavaDoc time = BigInteger.valueOf( System.currentTimeMillis() );
141                 md.update( time.toByteArray() );
142                 md.update( ownerPassword.getBytes() );
143                 md.update( userPassword.getBytes() );
144                 md.update( document.toString().getBytes() );
145                 byte[] id = md.digest( this.toString().getBytes() );
146                 COSString idString = new COSString();
147                 idString.append( id );
148                 idArray.add( idString );
149                 idArray.add( idString );
150                 document.setDocumentID( idArray );
151             }
152             catch( NoSuchAlgorithmException JavaDoc e )
153             {
154                 throw new CryptographyException( e );
155             }
156
157         }
158         COSString id = (COSString)idArray.getObject( 0 );
159         encryption = new PDFEncryption();
160
161         byte[] o = encryption.computeOwnerPassword(
162             ownerPassword.getBytes("ISO-8859-1"),
163             userPassword.getBytes("ISO-8859-1"), revision, length);
164
165         byte[] u = encryption.computeUserPassword(
166             userPassword.getBytes("ISO-8859-1"),
167             o, permissionInt, id.getBytes(), revision, length);
168
169         encryptionKey = encryption.computeEncryptedKey(
170             userPassword.getBytes("ISO-8859-1"), o, permissionInt, id.getBytes(), revision, length);
171
172         encParameters.setOwnerKey( o );
173         encParameters.setUserKey( u );
174         
175         document.setEncryptionDictionary( encParameters.getCOSDictionary() );
176     }
177     
178     
179
180     /**
181      * This will decrypt the document.
182      *
183      * @param password The password for the document.
184      *
185      * @throws CryptographyException If there is an error decrypting the document.
186      * @throws IOException If there is an error getting the stream data.
187      * @throws InvalidPasswordException If the password is not a user or owner password.
188      */

189     public void decryptDocument( String JavaDoc password )
190         throws CryptographyException, IOException JavaDoc, InvalidPasswordException
191     {
192         if( password == null )
193         {
194             password = "";
195         }
196
197         PDStandardEncryption encParameters = (PDStandardEncryption)pdDocument.getEncryptionDictionary();
198
199
200         int permissions = encParameters.getPermissions();
201         int revision = encParameters.getRevision();
202         int length = encParameters.getLength()/8;
203
204         COSString id = (COSString)document.getDocumentID().getObject( 0 );
205         byte[] u = encParameters.getUserKey();
206         byte[] o = encParameters.getOwnerKey();
207
208         boolean isUserPassword =
209             encryption.isUserPassword( password.getBytes(), u,
210                 o, permissions, id.getBytes(), revision, length );
211         boolean isOwnerPassword =
212             encryption.isOwnerPassword( password.getBytes(), u,
213                 o, permissions, id.getBytes(), revision, length );
214
215         if( isUserPassword )
216         {
217             encryptionKey =
218                 encryption.computeEncryptedKey(
219                     password.getBytes(), o,
220                     permissions, id.getBytes(), revision, length );
221         }
222         else if( isOwnerPassword )
223         {
224             byte[] computedUserPassword =
225                 encryption.getUserPassword(
226                     password.getBytes(),
227                     o,
228                     revision,
229                     length );
230             encryptionKey =
231                 encryption.computeEncryptedKey(
232                     computedUserPassword, o,
233                     permissions, id.getBytes(), revision, length );
234         }
235         else
236         {
237             throw new InvalidPasswordException( "Error: The supplied password does not match " +
238                                                 "either the owner or user password in the document." );
239         }
240         
241         COSDictionary trailer = document.getTrailer();
242         COSArray fields = (COSArray)trailer.getObjectFromPath( "Root/AcroForm/Fields" );
243         
244         //We need to collect all the signature dictionaries, for some
245
//reason the 'Contents' entry of signatures is not really encrypted
246
if( fields != null )
247         {
248             for( int i=0; i<fields.size(); i++ )
249             {
250                 COSDictionary field = (COSDictionary)fields.getObject( i );
251                 addDictionaryAndSubDictionary( potentialSignatures, field );
252             }
253         }
254
255         List JavaDoc allObjects = document.getObjects();
256         Iterator JavaDoc objectIter = allObjects.iterator();
257         while( objectIter.hasNext() )
258         {
259             decryptObject( (COSObject)objectIter.next() );
260         }
261         document.setEncryptionDictionary( null );
262     }
263     
264     private void addDictionaryAndSubDictionary( Set JavaDoc set, COSDictionary dic )
265     {
266         set.add( dic );
267         COSArray kids = (COSArray)dic.getDictionaryObject( "Kids" );
268         for( int i=0; kids != null && i<kids.size(); i++ )
269         {
270             addDictionaryAndSubDictionary( set, (COSDictionary)kids.getObject( i ) );
271         }
272         COSBase value = dic.getDictionaryObject( "V" );
273         if( value instanceof COSDictionary )
274         {
275             addDictionaryAndSubDictionary( set, (COSDictionary)value );
276         }
277     }
278
279     /**
280      * This will decrypt an object in the document.
281      *
282      * @param object The object to decrypt.
283      *
284      * @throws CryptographyException If there is an error decrypting the stream.
285      * @throws IOException If there is an error getting the stream data.
286      */

287     private void decryptObject( COSObject object )
288         throws CryptographyException, IOException JavaDoc
289     {
290         long objNum = object.getObjectNumber().intValue();
291         long genNum = object.getGenerationNumber().intValue();
292         COSBase base = object.getObject();
293         decrypt( base, objNum, genNum );
294     }
295
296     /**
297      * This will dispatch to the correct method.
298      *
299      * @param obj The object to decrypt.
300      * @param objNum The object number.
301      * @param genNum The object generation Number.
302      *
303      * @throws CryptographyException If there is an error decrypting the stream.
304      * @throws IOException If there is an error getting the stream data.
305      */

306     public void decrypt( Object JavaDoc obj, long objNum, long genNum )
307         throws CryptographyException, IOException JavaDoc
308     {
309         if( !objects.contains( obj ) )
310         {
311             objects.add( obj );
312
313             if( obj instanceof COSString )
314             {
315                 decryptString( (COSString)obj, objNum, genNum );
316             }
317             else if( obj instanceof COSStream )
318             {
319                 decryptStream( (COSStream)obj, objNum, genNum );
320             }
321             else if( obj instanceof COSDictionary )
322             {
323                 decryptDictionary( (COSDictionary)obj, objNum, genNum );
324             }
325             else if( obj instanceof COSArray )
326             {
327                 decryptArray( (COSArray)obj, objNum, genNum );
328             }
329         }
330     }
331
332     /**
333      * This will decrypt a stream.
334      *
335      * @param stream The stream to decrypt.
336      * @param objNum The object number.
337      * @param genNum The object generation number.
338      *
339      * @throws CryptographyException If there is an error getting the stream.
340      * @throws IOException If there is an error getting the stream data.
341      */

342     private void decryptStream( COSStream stream, long objNum, long genNum )
343         throws CryptographyException, IOException JavaDoc
344     {
345         decryptDictionary( stream, objNum, genNum );
346         InputStream JavaDoc encryptedStream = stream.getFilteredStream();
347         encryption.encryptData( objNum,
348                                 genNum,
349                                 encryptionKey,
350                                 encryptedStream,
351                                 stream.createFilteredStream() );
352     }
353
354     /**
355      * This will decrypt a dictionary.
356      *
357      * @param dictionary The dictionary to decrypt.
358      * @param objNum The object number.
359      * @param genNum The object generation number.
360      *
361      * @throws CryptographyException If there is an error decrypting the document.
362      * @throws IOException If there is an error creating a new string.
363      */

364     private void decryptDictionary( COSDictionary dictionary, long objNum, long genNum )
365         throws CryptographyException, IOException JavaDoc
366     {
367         Iterator JavaDoc keys = dictionary.keyList().iterator();
368         while( keys.hasNext() )
369         {
370             COSName key = (COSName)keys.next();
371             Object JavaDoc value = dictionary.getItem( key );
372             //if we are a signature dictionary and contain a Contents entry then
373
//we don't decrypt it.
374
if( !(key.getName().equals( "Contents" ) &&
375                   value instanceof COSString &&
376                   potentialSignatures.contains( dictionary )))
377             {
378                 decrypt( value, objNum, genNum );
379             }
380         }
381     }
382
383     /**
384      * This will decrypt a string.
385      *
386      * @param string the string to decrypt.
387      * @param objNum The object number.
388      * @param genNum The object generation number.
389      *
390      * @throws CryptographyException If an error occurs during decryption.
391      * @throws IOException If an error occurs writing the new string.
392      */

393     private void decryptString( COSString string, long objNum, long genNum )
394         throws CryptographyException, IOException JavaDoc
395     {
396         ByteArrayInputStream JavaDoc data = new ByteArrayInputStream JavaDoc( string.getBytes() );
397         ByteArrayOutputStream JavaDoc buffer = new ByteArrayOutputStream JavaDoc();
398         encryption.encryptData( objNum,
399                                 genNum,
400                                 encryptionKey,
401                                 data,
402                                 buffer );
403         string.reset();
404         string.append( buffer.toByteArray() );
405     }
406
407     /**
408      * This will decrypt an array.
409      *
410      * @param array The array to decrypt.
411      * @param objNum The object number.
412      * @param genNum The object generation number.
413      *
414      * @throws CryptographyException If an error occurs during decryption.
415      * @throws IOException If there is an error accessing the data.
416      */

417     private void decryptArray( COSArray array, long objNum, long genNum )
418         throws CryptographyException, IOException JavaDoc
419     {
420         for( int i=0; i<array.size(); i++ )
421         {
422             decrypt( array.get( i ), objNum, genNum );
423         }
424     }
425 }
Popular Tags