1 31 package org.pdfbox.encryption; 32 33 import java.io.ByteArrayInputStream ; 34 import java.io.ByteArrayOutputStream ; 35 import java.io.IOException ; 36 import java.io.InputStream ; 37 import java.io.OutputStream ; 38 39 import java.security.MessageDigest ; 40 import java.security.NoSuchAlgorithmException ; 41 42 import org.pdfbox.exceptions.CryptographyException; 43 44 54 public final class PDFEncryption 55 { 56 private ARCFour rc4 = new ARCFour(); 57 60 public static final byte[] ENCRYPT_PADDING = 61 { 62 (byte)0x28, (byte)0xBF, (byte)0x4E, (byte)0x5E, (byte)0x4E, 63 (byte)0x75, (byte)0x8A, (byte)0x41, (byte)0x64, (byte)0x00, 64 (byte)0x4E, (byte)0x56, (byte)0xFF, (byte)0xFA, (byte)0x01, 65 (byte)0x08, (byte)0x2E, (byte)0x2E, (byte)0x00, (byte)0xB6, 66 (byte)0xD0, (byte)0x68, (byte)0x3E, (byte)0x80, (byte)0x2F, 67 (byte)0x0C, (byte)0xA9, (byte)0xFE, (byte)0x64, (byte)0x53, 68 (byte)0x69, (byte)0x7A 69 }; 70 71 83 public final void encryptData( 84 long objectNumber, 85 long genNumber, 86 byte[] key, 87 InputStream data, 88 OutputStream output ) 89 throws CryptographyException, IOException 90 { 91 byte[] newKey = new byte[ key.length + 5 ]; 92 System.arraycopy( key, 0, newKey, 0, key.length ); 93 97 newKey[newKey.length -5] = (byte)(objectNumber & 0xff); 99 newKey[newKey.length -4] = (byte)((objectNumber >> 8) & 0xff); 100 newKey[newKey.length -3] = (byte)((objectNumber >> 16) & 0xff); 101 newKey[newKey.length -2] = (byte)(genNumber & 0xff); 102 newKey[newKey.length -1] = (byte)((genNumber >> 8) & 0xff); 103 104 105 byte[] digestedKey = null; 107 try 108 { 109 MessageDigest md = MessageDigest.getInstance( "MD5" ); 110 digestedKey = md.digest( newKey ); 111 } 112 catch( NoSuchAlgorithmException e ) 113 { 114 throw new CryptographyException( e ); 115 } 116 117 int length = Math.min( newKey.length, 16 ); 119 byte[] finalKey = new byte[ length ]; 120 System.arraycopy( digestedKey, 0, finalKey, 0, length ); 121 122 rc4.setKey( finalKey ); 123 rc4.write( data, output ); 124 output.flush(); 125 } 126 127 140 public final byte[] getUserPassword( 141 byte[] ownerPassword, 142 byte[] o, 143 int revision, 144 long length ) 145 throws CryptographyException, IOException 146 { 147 try 148 { 149 ByteArrayOutputStream result = new ByteArrayOutputStream (); 150 151 byte[] ownerPadded = truncateOrPad( ownerPassword ); 153 154 MessageDigest md = MessageDigest.getInstance( "MD5" ); 156 md.update( ownerPadded ); 157 byte[] digest = md.digest(); 158 159 if( revision == 3 || revision == 4 ) 161 { 162 for( int i=0; i<50; i++ ) 163 { 164 md.reset(); 165 md.update( digest ); 166 digest = md.digest(); 167 } 168 } 169 if( revision == 2 && length != 5 ) 170 { 171 throw new CryptographyException( 172 "Error: Expected length=5 actual=" + length ); 173 } 174 175 byte[] rc4Key = new byte[ (int)length ]; 177 System.arraycopy( digest, 0, rc4Key, 0, (int)length ); 178 179 if( revision == 2 ) 181 { 182 rc4.setKey( rc4Key ); 183 rc4.write( o, result ); 184 } 185 else if( revision == 3 || revision == 4) 186 { 187 204 byte[] iterationKey = new byte[ rc4Key.length ]; 205 206 207 byte[] otemp = new byte[ o.length ]; System.arraycopy( o, 0, otemp, 0, o.length ); rc4.write( o, result); 211 for( int i=19; i>=0; i-- ) 212 { 213 System.arraycopy( rc4Key, 0, iterationKey, 0, rc4Key.length ); 214 for( int j=0; j< iterationKey.length; j++ ) 215 { 216 iterationKey[j] = (byte)(iterationKey[j] ^ (byte)i); 217 } 218 rc4.setKey( iterationKey ); 219 result.reset(); rc4.write( otemp, result ); otemp = result.toByteArray(); } 223 } 224 225 226 return result.toByteArray(); 227 228 } 229 catch( NoSuchAlgorithmException e ) 230 { 231 throw new CryptographyException( e ); 232 } 233 } 234 235 251 public final boolean isOwnerPassword( 252 byte[] ownerPassword, 253 byte[] u, 254 byte[] o, 255 int permissions, 256 byte[] id, 257 int revision, 258 int length) 259 throws CryptographyException, IOException 260 { 261 byte[] userPassword = getUserPassword( ownerPassword, o, revision, length ); 262 return isUserPassword( userPassword, u, o, permissions, id, revision, length ); 263 } 264 265 283 public final boolean isUserPassword( 284 byte[] password, 285 byte[] u, 286 byte[] o, 287 int permissions, 288 byte[] id, 289 int revision, 290 int length) 291 throws CryptographyException, IOException 292 { 293 boolean matches = false; 294 byte[] computedValue = computeUserPassword( password, o, permissions, id, revision, length ); 296 if( revision == 2 ) 297 { 298 matches = arraysEqual( u, computedValue ); 300 } 301 else if( revision == 3 || revision == 4 ) 302 { 303 matches = arraysEqual( u, computedValue, 16 ); 305 } 306 return matches; 307 } 308 309 318 private final boolean arraysEqual( byte[] first, byte[] second, int count ) 319 { 320 boolean equal = first.length >= count && second.length >= count; 321 for( int i=0; i<count && equal; i++ ) 322 { 323 equal = first[i] == second[i]; 324 } 325 return equal; 326 } 327 328 336 private final boolean arraysEqual( byte[] first, byte[] second ) 337 { 338 boolean equal = first.length == second.length; 339 for( int i=0; i<first.length && equal; i++ ) 340 { 341 equal = first[i] == second[i]; 342 } 343 return equal; 344 } 345 346 361 public final byte[] computeUserPassword( 362 byte[] password, 363 byte[] o, 364 int permissions, 365 byte[] id, 366 int revision, 367 int length ) 368 throws CryptographyException, IOException 369 { 370 ByteArrayOutputStream result = new ByteArrayOutputStream (); 371 byte[] encryptionKey = computeEncryptedKey( password, o, permissions, id, revision, length ); 373 374 if( revision == 2 ) 375 { 376 rc4.setKey( encryptionKey ); 378 rc4.write( ENCRYPT_PADDING, result ); 379 } 380 else if( revision == 3 || revision == 4 ) 381 { 382 try 383 { 384 MessageDigest md = MessageDigest.getInstance("MD5"); 386 md.update( ENCRYPT_PADDING ); 388 389 md.update( id ); 391 result.write( md.digest() ); 392 393 byte[] iterationKey = new byte[ encryptionKey.length ]; 395 for( int i=0; i<20; i++ ) 396 { 397 System.arraycopy( encryptionKey, 0, iterationKey, 0, iterationKey.length ); 398 for( int j=0; j< iterationKey.length; j++ ) 399 { 400 iterationKey[j] = (byte)(iterationKey[j] ^ i); 401 } 402 rc4.setKey( iterationKey ); 403 ByteArrayInputStream input = new ByteArrayInputStream ( result.toByteArray() ); 404 result.reset(); 405 rc4.write( input, result ); 406 } 407 408 byte[] finalResult = new byte[32]; 410 System.arraycopy( result.toByteArray(), 0, finalResult, 0, 16 ); 411 System.arraycopy( ENCRYPT_PADDING, 0, finalResult, 16, 16 ); 412 result.reset(); 413 result.write( finalResult ); 414 } 415 catch( NoSuchAlgorithmException e ) 416 { 417 throw new CryptographyException( e ); 418 } 419 } 420 return result.toByteArray(); 421 } 422 423 437 public final byte[] computeEncryptedKey( 438 byte[] password, 439 byte[] o, 440 int permissions, 441 byte[] id, 442 int revision, 443 int length ) 444 throws CryptographyException 445 { 446 byte[] result = new byte[ length ]; 447 try 448 { 449 byte[] padded = truncateOrPad( password ); 452 453 MessageDigest md = MessageDigest.getInstance("MD5"); 455 md.update( padded ); 456 457 md.update( o ); 459 460 byte zero = (byte)(permissions >>> 0); 462 byte one = (byte)(permissions >>> 8); 463 byte two = (byte)(permissions >>> 16); 464 byte three = (byte)(permissions >>> 24); 465 466 md.update( zero ); 467 md.update( one ); 468 md.update( two ); 469 md.update( three ); 470 471 md.update( id ); 473 byte[] digest = md.digest(); 474 475 if( revision == 3 || revision == 4) 477 { 478 for( int i=0; i<50; i++ ) 479 { 480 md.reset(); 481 md.update( digest, 0, length ); 482 digest = md.digest(); 483 } 484 } 485 486 if( revision == 2 && length != 5 ) 488 { 489 throw new CryptographyException( 490 "Error: length should be 5 when revision is two actual=" + length ); 491 } 492 System.arraycopy( digest, 0, result, 0, length ); 493 } 494 catch( NoSuchAlgorithmException e ) 495 { 496 throw new CryptographyException( e ); 497 } 498 return result; 499 } 500 501 514 public final byte[] computeOwnerPassword( 515 byte[] ownerPassword, 516 byte[] userPassword, 517 int revision, 518 int length ) 519 throws CryptographyException, IOException 520 { 521 try 522 { 523 byte[] ownerPadded = truncateOrPad( ownerPassword ); 525 526 MessageDigest md = MessageDigest.getInstance( "MD5" ); 528 md.update( ownerPadded ); 529 byte[] digest = md.digest(); 530 531 if( revision == 3 || revision == 4) 533 { 534 for( int i=0; i<50; i++ ) 535 { 536 md.reset(); 537 md.update( digest, 0, length ); 538 digest = md.digest(); 539 } 540 } 541 if( revision == 2 && length != 5 ) 542 { 543 throw new CryptographyException( 544 "Error: Expected length=5 actual=" + length ); 545 } 546 547 byte[] rc4Key = new byte[ length ]; 549 System.arraycopy( digest, 0, rc4Key, 0, length ); 550 551 byte[] paddedUser = truncateOrPad( userPassword ); 553 554 555 rc4.setKey( rc4Key ); 557 ByteArrayOutputStream crypted = new ByteArrayOutputStream (); 558 rc4.write( new ByteArrayInputStream ( paddedUser ), crypted ); 559 560 561 if( revision == 3 || revision == 4 ) 563 { 564 byte[] iterationKey = new byte[ rc4Key.length ]; 565 for( int i=1; i<20; i++ ) 566 { 567 System.arraycopy( rc4Key, 0, iterationKey, 0, rc4Key.length ); 568 for( int j=0; j< iterationKey.length; j++ ) 569 { 570 iterationKey[j] = (byte)(iterationKey[j] ^ (byte)i); 571 } 572 rc4.setKey( iterationKey ); 573 ByteArrayInputStream input = new ByteArrayInputStream ( crypted.toByteArray() ); 574 crypted.reset(); 575 rc4.write( input, crypted ); 576 } 577 } 578 579 return crypted.toByteArray(); 581 } 582 catch( NoSuchAlgorithmException e ) 583 { 584 throw new CryptographyException( e.getMessage() ); 585 } 586 } 587 588 595 private final byte[] truncateOrPad( byte[] password ) 596 { 597 byte[] padded = new byte[ ENCRYPT_PADDING.length ]; 598 int bytesBeforePad = Math.min( password.length, padded.length ); 599 System.arraycopy( password, 0, padded, 0, bytesBeforePad ); 600 System.arraycopy( ENCRYPT_PADDING, 0, padded, bytesBeforePad, ENCRYPT_PADDING.length-bytesBeforePad ); 601 return padded; 602 } 603 } | Popular Tags |