KickJava   Java API By Example, From Geeks To Geeks.

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


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 package org.pdfbox.pdmodel.encryption;
32
33 import java.io.ByteArrayInputStream JavaDoc;
34 import java.io.ByteArrayOutputStream JavaDoc;
35 import java.io.IOException JavaDoc;
36 import java.math.BigInteger JavaDoc;
37 import java.security.MessageDigest JavaDoc;
38 import java.security.NoSuchAlgorithmException JavaDoc;
39
40 import org.pdfbox.cos.COSArray;
41 import org.pdfbox.cos.COSString;
42 import org.pdfbox.encryption.ARCFour;
43 import org.pdfbox.exceptions.CryptographyException;
44 import org.pdfbox.pdmodel.PDDocument;
45
46 /**
47  *
48  * The class implements the standard security handler as decribed
49  * in the PDF specifications. This security handler protects document
50  * with password.
51  *
52  * @see StandardProtectionPolicy to see how to protect document with this security handler.
53  *
54  * @author <a HREF="mailto:ben@benlitchfield.com">Ben Litchfield</a>
55  * @author Benoit Guillon (benoit.guillon@snv.jussieu.fr)
56  *
57  * @version $Revision: 1.3 $
58  */

59
60 public class StandardSecurityHandler extends SecurityHandler
61 {
62     /**
63      * Type of security handler.
64      */

65     public static final String JavaDoc FILTER = "Standard";
66     
67     private static final int DEFAULT_VERSION = 1;
68     
69     private static final int DEFAULT_REVISION = 3;
70     
71     private int revision = DEFAULT_REVISION;
72     
73     private StandardProtectionPolicy policy;
74     
75     private ARCFour rc4 = new ARCFour();
76     
77     /**
78      * Protection policy class for this handler.
79      */

80     public static final Class JavaDoc PROTECTION_POLICY_CLASS = StandardProtectionPolicy.class;
81     
82     /**
83      * Standard padding for encryption.
84      */

85     public static final byte[] ENCRYPT_PADDING =
86     {
87         (byte)0x28, (byte)0xBF, (byte)0x4E, (byte)0x5E, (byte)0x4E,
88         (byte)0x75, (byte)0x8A, (byte)0x41, (byte)0x64, (byte)0x00,
89         (byte)0x4E, (byte)0x56, (byte)0xFF, (byte)0xFA, (byte)0x01,
90         (byte)0x08, (byte)0x2E, (byte)0x2E, (byte)0x00, (byte)0xB6,
91         (byte)0xD0, (byte)0x68, (byte)0x3E, (byte)0x80, (byte)0x2F,
92         (byte)0x0C, (byte)0xA9, (byte)0xFE, (byte)0x64, (byte)0x53,
93         (byte)0x69, (byte)0x7A
94     };
95     
96     /**
97      * Constructor.
98      */

99     public StandardSecurityHandler()
100     {
101     }
102     
103     /**
104      * Constructor used for encryption.
105      *
106      * @param p The protection policy.
107      */

108     public StandardSecurityHandler(StandardProtectionPolicy p)
109     {
110         policy = p;
111         keyLength = policy.getEncryptionKeyLength();
112     }
113     
114
115     /**
116      * Computes the version number of the StandardSecurityHandler
117      * regarding the encryption key length.
118      * See PDF Spec 1.6 p 93
119      *
120      * @return The computed cersion number.
121      */

122     private int computeVersionNumber()
123     {
124         if(keyLength == 40)
125         {
126             return DEFAULT_VERSION;
127         }
128         return 2;
129     }
130     
131     /**
132      * Computes the revision version of the StandardSecurityHandler to
133      * use regarding the version number and the permissions bits set.
134      * See PDF Spec 1.6 p98
135      *
136      * @return The computed revision number.
137      */

138     private int computeRevisionNumber()
139     {
140         if(version == 2
141             && !policy.getPermissions().canFillInForm()
142             && !policy.getPermissions().canExtractForAccessibility()
143             && !policy.getPermissions().canPrintDegraded() )
144         {
145             return 2;
146         }
147         return 3;
148     }
149     
150     /**
151      * Decrypt the document.
152      *
153      * @param doc The document to be decrypted.
154      * @param decryptionMaterial Information used to decrypt the document.
155      *
156      * @throws IOException If there is an error accessing data.
157      * @throws CryptographyException If there is an error with decryption.
158      */

159     public void decryptDocument(PDDocument doc, DecryptionMaterial decryptionMaterial)
160         throws CryptographyException, IOException JavaDoc
161     {
162         document = doc;
163         
164         PDEncryptionDictionary dictionary = document.getEncryptionDictionary();
165         if(!(decryptionMaterial instanceof StandardDecryptionMaterial))
166         {
167             throw new CryptographyException("Provided decryption material is not compatible with the document");
168         }
169         
170         StandardDecryptionMaterial material = (StandardDecryptionMaterial)decryptionMaterial;
171         
172         String JavaDoc password = material.getPassword();
173         if(password == null)
174         {
175             password = "";
176         }
177         
178         int dicPermissions = dictionary.getPermissions();
179         int dicRevision = dictionary.getRevision();
180         int dicLength = dictionary.getLength()/8;
181
182         COSString id = (COSString)document.getDocument().getDocumentID().getObject( 0 );
183         byte[] u = dictionary.getUserKey();
184         byte[] o = dictionary.getOwnerKey();
185
186         boolean isUserPassword =
187             isUserPassword(
188                 password.getBytes(),
189                 u,
190                 o,
191                 dicPermissions,
192                 id.getBytes(),
193                 dicRevision,
194                 dicLength );
195         boolean isOwnerPassword =
196             isOwnerPassword(
197                 password.getBytes(),
198                 u,
199                 o,
200                 dicPermissions,
201                 id.getBytes(),
202                 dicRevision,
203                 dicLength );
204
205         if( isUserPassword )
206         {
207             encryptionKey =
208                 computeEncryptedKey(
209                     password.getBytes(),
210                     o,
211                     dicPermissions,
212                     id.getBytes(),
213                     dicRevision,
214                     dicLength );
215         }
216         else if( isOwnerPassword )
217         {
218             byte[] computedUserPassword = getUserPassword(password.getBytes(),o,dicRevision,dicLength );
219             encryptionKey =
220                 computeEncryptedKey(
221                     computedUserPassword,
222                     o,
223                     dicPermissions,
224                     id.getBytes(),
225                     dicRevision,
226                     dicLength );
227         }
228         else
229         {
230             throw new CryptographyException(
231                 "Error: The supplied password does not match either the owner or user password in the document." );
232         }
233         
234         this.proceedDecryption();
235     }
236     
237     /**
238      * Prepare document for encryption.
239      *
240      * @param doc The documeent to encrypt.
241      *
242      * @throws IOException If there is an error accessing data.
243      * @throws CryptographyException If there is an error with decryption.
244      */

245     public void prepareDocumentForEncryption(PDDocument doc) throws CryptographyException, IOException JavaDoc
246     {
247         document = doc;
248         PDEncryptionDictionary encryptionDictionary = document.getEncryptionDictionary();
249         if(encryptionDictionary == null)
250         {
251             encryptionDictionary = new PDEncryptionDictionary();
252         }
253         version = computeVersionNumber();
254         revision = computeRevisionNumber();
255         encryptionDictionary.setFilter(FILTER);
256         encryptionDictionary.setVersion(version);
257         encryptionDictionary.setRevision(revision);
258         encryptionDictionary.setLength(keyLength);
259         
260         String JavaDoc ownerPassword = policy.getOwnerPassword();
261         String JavaDoc userPassword = policy.getUserPassword();
262         if( ownerPassword == null )
263         {
264             ownerPassword = "";
265         }
266         if( userPassword == null )
267         {
268             userPassword = "";
269         }
270         
271         int permissionInt = policy.getPermissions().getPermissionBytes();
272         
273         encryptionDictionary.setPermissions(permissionInt);
274         
275         int length = keyLength/8;
276         
277         COSArray idArray = document.getDocument().getDocumentID();
278
279         //check if the document has an id yet. If it does not then
280
//generate one
281
if( idArray == null || idArray.size() < 2 )
282         {
283             idArray = new COSArray();
284             try
285             {
286                 MessageDigest JavaDoc md = MessageDigest.getInstance( "MD5" );
287                 BigInteger JavaDoc time = BigInteger.valueOf( System.currentTimeMillis() );
288                 md.update( time.toByteArray() );
289                 md.update( ownerPassword.getBytes() );
290                 md.update( userPassword.getBytes() );
291                 md.update( document.getDocument().toString().getBytes() );
292                 byte[] id = md.digest( this.toString().getBytes() );
293                 COSString idString = new COSString();
294                 idString.append( id );
295                 idArray.add( idString );
296                 idArray.add( idString );
297                 document.getDocument().setDocumentID( idArray );
298             }
299             catch( NoSuchAlgorithmException JavaDoc e )
300             {
301                 throw new CryptographyException( e );
302             }
303             catch(IOException JavaDoc e )
304             {
305                 throw new CryptographyException( e );
306             }
307         }
308         
309         COSString id = (COSString)idArray.getObject( 0 );
310         
311         byte[] o = computeOwnerPassword(
312             ownerPassword.getBytes("ISO-8859-1"),
313             userPassword.getBytes("ISO-8859-1"), revision, length);
314
315         byte[] u = computeUserPassword(
316             userPassword.getBytes("ISO-8859-1"),
317             o, permissionInt, id.getBytes(), revision, length);
318
319         encryptionKey = computeEncryptedKey(
320             userPassword.getBytes("ISO-8859-1"), o, permissionInt, id.getBytes(), revision, length);
321
322         encryptionDictionary.setOwnerKey(o);
323         encryptionDictionary.setUserKey(u);
324         
325         document.setEncryptionDictionary( encryptionDictionary );
326         document.getDocument().setEncryptionDictionary(encryptionDictionary.getCOSDictionary());
327         
328     }
329
330     /**
331      * Check for owner password.
332      *
333      * @param ownerPassword The owner password.
334      * @param u The u entry of the encryption dictionary.
335      * @param o The o entry of the encryption dictionary.
336      * @param permissions The set of permissions on the document.
337      * @param id The document id.
338      * @param encRevision The encryption algorithm revision.
339      * @param length The encryption key length.
340      *
341      * @return True If the ownerPassword param is the owner password.
342      *
343      * @throws CryptographyException If there is an error during encryption.
344      * @throws IOException If there is an error accessing data.
345      */

346     public final boolean isOwnerPassword(
347             byte[] ownerPassword,
348             byte[] u,
349             byte[] o,
350             int permissions,
351             byte[] id,
352             int encRevision,
353             int length)
354             throws CryptographyException, IOException JavaDoc
355     {
356         byte[] userPassword = getUserPassword( ownerPassword, o, encRevision, length );
357         return isUserPassword( userPassword, u, o, permissions, id, encRevision, length );
358     }
359     
360     /**
361      * Get the user password based on the owner password.
362      *
363      * @param ownerPassword The plaintext owner password.
364      * @param o The o entry of the encryption dictionary.
365      * @param encRevision The encryption revision number.
366      * @param length The key length.
367      *
368      * @return The u entry of the encryption dictionary.
369      *
370      * @throws CryptographyException If there is an error generating the user password.
371      * @throws IOException If there is an error accessing data while generating the user password.
372      */

373     public final byte[] getUserPassword(
374             byte[] ownerPassword,
375             byte[] o,
376             int encRevision,
377             long length )
378             throws CryptographyException, IOException JavaDoc
379     {
380         try
381         {
382             ByteArrayOutputStream JavaDoc result = new ByteArrayOutputStream JavaDoc();
383
384             //3.3 STEP 1
385
byte[] ownerPadded = truncateOrPad( ownerPassword );
386
387             //3.3 STEP 2
388
MessageDigest JavaDoc md = MessageDigest.getInstance( "MD5" );
389             md.update( ownerPadded );
390             byte[] digest = md.digest();
391
392             //3.3 STEP 3
393
if( encRevision == 3 || encRevision == 4 )
394             {
395                 for( int i=0; i<50; i++ )
396                 {
397                     md.reset();
398                     md.update( digest );
399                     digest = md.digest();
400                 }
401             }
402             if( encRevision == 2 && length != 5 )
403             {
404                 throw new CryptographyException(
405                     "Error: Expected length=5 actual=" + length );
406             }
407
408             //3.3 STEP 4
409
byte[] rc4Key = new byte[ (int)length ];
410             System.arraycopy( digest, 0, rc4Key, 0, (int)length );
411
412             //3.7 step 2
413
if( encRevision == 2 )
414             {
415                 rc4.setKey( rc4Key );
416                 rc4.write( o, result );
417             }
418             else if( encRevision == 3 || encRevision == 4)
419             {
420                 /**
421                 byte[] iterationKey = new byte[ rc4Key.length ];
422                 byte[] dataToEncrypt = o;
423                 for( int i=19; i>=0; i-- )
424                 {
425                     System.arraycopy( rc4Key, 0, iterationKey, 0, rc4Key.length );
426                     for( int j=0; j< iterationKey.length; j++ )
427                     {
428                         iterationKey[j] = (byte)(iterationKey[j] ^ (byte)i);
429                     }
430                     rc4.setKey( iterationKey );
431                     rc4.write( dataToEncrypt, result );
432                     dataToEncrypt = result.toByteArray();
433                     result.reset();
434                 }
435                 result.write( dataToEncrypt, 0, dataToEncrypt.length );
436                 */

437                 byte[] iterationKey = new byte[ rc4Key.length ];
438                 
439               
440                 byte[] otemp = new byte[ o.length ]; //sm
441
System.arraycopy( o, 0, otemp, 0, o.length ); //sm
442
rc4.write( o, result);//sm
443

444                 for( int i=19; i>=0; i-- )
445                 {
446                     System.arraycopy( rc4Key, 0, iterationKey, 0, rc4Key.length );
447                     for( int j=0; j< iterationKey.length; j++ )
448                     {
449                         iterationKey[j] = (byte)(iterationKey[j] ^ (byte)i);
450                     }
451                     rc4.setKey( iterationKey );
452                     result.reset(); //sm
453
rc4.write( otemp, result ); //sm
454
otemp = result.toByteArray(); //sm
455
}
456             }
457
458
459             return result.toByteArray();
460
461         }
462         catch( NoSuchAlgorithmException JavaDoc e )
463         {
464             throw new CryptographyException( e );
465         }
466     }
467     
468     /**
469      * Compute the encryption key.
470      *
471      * @param password The password to compute the encrypted key.
472      * @param o The o entry of the encryption dictionary.
473      * @param permissions The permissions for the document.
474      * @param id The document id.
475      * @param encRevision The revision of the encryption algorithm.
476      * @param length The length of the encryption key.
477      *
478      * @return The encrypted key bytes.
479      *
480      * @throws CryptographyException If there is an error with encryption.
481      */

482     public final byte[] computeEncryptedKey(
483             byte[] password,
484             byte[] o,
485             int permissions,
486             byte[] id,
487             int encRevision,
488             int length )
489             throws CryptographyException
490         {
491             byte[] result = new byte[ length ];
492             try
493             {
494                 //PDFReference 1.4 pg 78
495
//step1
496
byte[] padded = truncateOrPad( password );
497
498                 //step 2
499
MessageDigest JavaDoc md = MessageDigest.getInstance("MD5");
500                 md.update( padded );
501
502                 //step 3
503
md.update( o );
504
505                 //step 4
506
byte zero = (byte)(permissions >>> 0);
507                 byte one = (byte)(permissions >>> 8);
508                 byte two = (byte)(permissions >>> 16);
509                 byte three = (byte)(permissions >>> 24);
510
511                 md.update( zero );
512                 md.update( one );
513                 md.update( two );
514                 md.update( three );
515
516                 //step 5
517
md.update( id );
518                 byte[] digest = md.digest();
519
520                 //step 6
521
if( encRevision == 3 || encRevision == 4)
522                 {
523                     for( int i=0; i<50; i++ )
524                     {
525                         md.reset();
526                         md.update( digest, 0, length );
527                         digest = md.digest();
528                     }
529                 }
530
531                 //step 7
532
if( encRevision == 2 && length != 5 )
533                 {
534                     throw new CryptographyException(
535                         "Error: length should be 5 when revision is two actual=" + length );
536                 }
537                 System.arraycopy( digest, 0, result, 0, length );
538             }
539             catch( NoSuchAlgorithmException JavaDoc e )
540             {
541                 throw new CryptographyException( e );
542             }
543             return result;
544         }
545     
546     /**
547      * This will compute the user password hash.
548      *
549      * @param password The plain text password.
550      * @param o The owner password hash.
551      * @param permissions The document permissions.
552      * @param id The document id.
553      * @param encRevision The revision of the encryption.
554      * @param length The length of the encryption key.
555      *
556      * @return The user password.
557      *
558      * @throws CryptographyException If there is an error computing the user password.
559      * @throws IOException If there is an IO error.
560      */

561     
562     public final byte[] computeUserPassword(
563             byte[] password,
564             byte[] o,
565             int permissions,
566             byte[] id,
567             int encRevision,
568             int length )
569             throws CryptographyException, IOException JavaDoc
570         {
571             ByteArrayOutputStream JavaDoc result = new ByteArrayOutputStream JavaDoc();
572             //STEP 1
573
byte[] encryptionKey = computeEncryptedKey( password, o, permissions, id, encRevision, length );
574
575             if( encRevision == 2 )
576             {
577                 //STEP 2
578
rc4.setKey( encryptionKey );
579                 rc4.write( ENCRYPT_PADDING, result );
580             }
581             else if( encRevision == 3 || encRevision == 4 )
582             {
583                 try
584                 {
585                     //STEP 2
586
MessageDigest JavaDoc md = MessageDigest.getInstance("MD5");
587                     //md.update( truncateOrPad( password ) );
588
md.update( ENCRYPT_PADDING );
589
590                     //STEP 3
591
md.update( id );
592                     result.write( md.digest() );
593
594                     //STEP 4 and 5
595
byte[] iterationKey = new byte[ encryptionKey.length ];
596                     for( int i=0; i<20; i++ )
597                     {
598                         System.arraycopy( encryptionKey, 0, iterationKey, 0, iterationKey.length );
599                         for( int j=0; j< iterationKey.length; j++ )
600                         {
601                             iterationKey[j] = (byte)(iterationKey[j] ^ i);
602                         }
603                         rc4.setKey( iterationKey );
604                         ByteArrayInputStream JavaDoc input = new ByteArrayInputStream JavaDoc( result.toByteArray() );
605                         result.reset();
606                         rc4.write( input, result );
607                     }
608
609                     //step 6
610
byte[] finalResult = new byte[32];
611                     System.arraycopy( result.toByteArray(), 0, finalResult, 0, 16 );
612                     System.arraycopy( ENCRYPT_PADDING, 0, finalResult, 16, 16 );
613                     result.reset();
614                     result.write( finalResult );
615                 }
616                 catch( NoSuchAlgorithmException JavaDoc e )
617                 {
618                     throw new CryptographyException( e );
619                 }
620             }
621             return result.toByteArray();
622         }
623     
624     /**
625      * Compute the owner entry in the encryption dictionary.
626      *
627      * @param ownerPassword The plaintext owner password.
628      * @param userPassword The plaintext user password.
629      * @param encRevision The revision number of the encryption algorithm.
630      * @param length The length of the encryption key.
631      *
632      * @return The o entry of the encryption dictionary.
633      *
634      * @throws CryptographyException If there is an error with encryption.
635      * @throws IOException If there is an error accessing data.
636      */

637     public final byte[] computeOwnerPassword(
638             byte[] ownerPassword,
639             byte[] userPassword,
640             int encRevision,
641             int length )
642             throws CryptographyException, IOException JavaDoc
643         {
644             try
645             {
646                 //STEP 1
647
byte[] ownerPadded = truncateOrPad( ownerPassword );
648
649                 //STEP 2
650
MessageDigest JavaDoc md = MessageDigest.getInstance( "MD5" );
651                 md.update( ownerPadded );
652                 byte[] digest = md.digest();
653
654                 //STEP 3
655
if( encRevision == 3 || encRevision == 4)
656                 {
657                     for( int i=0; i<50; i++ )
658                     {
659                         md.reset();
660                         md.update( digest, 0, length );
661                         digest = md.digest();
662                     }
663                 }
664                 if( encRevision == 2 && length != 5 )
665                 {
666                     throw new CryptographyException(
667                         "Error: Expected length=5 actual=" + length );
668                 }
669
670                 //STEP 4
671
byte[] rc4Key = new byte[ length ];
672                 System.arraycopy( digest, 0, rc4Key, 0, length );
673
674                 //STEP 5
675
byte[] paddedUser = truncateOrPad( userPassword );
676
677
678                 //STEP 6
679
rc4.setKey( rc4Key );
680                 ByteArrayOutputStream JavaDoc crypted = new ByteArrayOutputStream JavaDoc();
681                 rc4.write( new ByteArrayInputStream JavaDoc( paddedUser ), crypted );
682
683
684                 //STEP 7
685
if( encRevision == 3 || encRevision == 4 )
686                 {
687                     byte[] iterationKey = new byte[ rc4Key.length ];
688                     for( int i=1; i<20; i++ )
689                     {
690                         System.arraycopy( rc4Key, 0, iterationKey, 0, rc4Key.length );
691                         for( int j=0; j< iterationKey.length; j++ )
692                         {
693                             iterationKey[j] = (byte)(iterationKey[j] ^ (byte)i);
694                         }
695                         rc4.setKey( iterationKey );
696                         ByteArrayInputStream JavaDoc input = new ByteArrayInputStream JavaDoc( crypted.toByteArray() );
697                         crypted.reset();
698                         rc4.write( input, crypted );
699                     }
700                 }
701
702                 //STEP 8
703
return crypted.toByteArray();
704             }
705             catch( NoSuchAlgorithmException JavaDoc e )
706             {
707                 throw new CryptographyException( e.getMessage() );
708             }
709         }
710     
711
712     /**
713      * This will take the password and truncate or pad it as necessary.
714      *
715      * @param password The password to pad or truncate.
716      *
717      * @return The padded or truncated password.
718      */

719     private final byte[] truncateOrPad( byte[] password )
720     {
721         byte[] padded = new byte[ ENCRYPT_PADDING.length ];
722         int bytesBeforePad = Math.min( password.length, padded.length );
723         System.arraycopy( password, 0, padded, 0, bytesBeforePad );
724         System.arraycopy( ENCRYPT_PADDING, 0, padded, bytesBeforePad, ENCRYPT_PADDING.length-bytesBeforePad );
725         return padded;
726     }
727     
728     /**
729      * Check if a plaintext password is the user password.
730      *
731      * @param password The plaintext password.
732      * @param u The u entry of the encryption dictionary.
733      * @param o The o entry of the encryption dictionary.
734      * @param permissions The permissions set in the the PDF.
735      * @param id The document id used for encryption.
736      * @param encRevision The revision of the encryption algorithm.
737      * @param length The length of the encryption key.
738      *
739      * @return true If the plaintext password is the user password.
740      *
741      * @throws CryptographyException If there is an error during encryption.
742      * @throws IOException If there is an error accessing data.
743      */

744     public final boolean isUserPassword(
745             byte[] password,
746             byte[] u,
747             byte[] o,
748             int permissions,
749             byte[] id,
750             int encRevision,
751             int length)
752             throws CryptographyException, IOException JavaDoc
753         {
754             boolean matches = false;
755             //STEP 1
756
byte[] computedValue = computeUserPassword( password, o, permissions, id, encRevision, length );
757             if( encRevision == 2 )
758             {
759                 //STEP 2
760
matches = arraysEqual( u, computedValue );
761             }
762             else if( encRevision == 3 || encRevision == 4 )
763             {
764                 //STEP 2
765
matches = arraysEqual( u, computedValue, 16 );
766             }
767             return matches;
768         }
769     
770     private static final boolean arraysEqual( byte[] first, byte[] second, int count )
771     {
772         boolean equal = first.length >= count && second.length >= count;
773         for( int i=0; i<count && equal; i++ )
774         {
775             equal = first[i] == second[i];
776         }
777         return equal;
778     }
779
780     /**
781      * This will compare two byte[] for equality.
782      *
783      * @param first The first byte array.
784      * @param second The second byte array.
785      *
786      * @return true If the arrays contain the exact same data.
787      */

788     private static final boolean arraysEqual( byte[] first, byte[] second )
789     {
790         boolean equal = first.length == second.length;
791         for( int i=0; i<first.length && equal; i++ )
792         {
793             equal = first[i] == second[i];
794         }
795         return equal;
796     }
797 }
798
Popular Tags