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 package org.apache.pdfbox.encryption; 18 19 import java.io.ByteArrayInputStream; 20 import java.io.ByteArrayOutputStream; 21 import java.io.IOException; 22 import java.io.InputStream; 23 import java.io.OutputStream; 24 25 import java.security.MessageDigest; 26 import java.security.NoSuchAlgorithmException; 27 28 import org.apache.pdfbox.exceptions.CryptographyException; 29 30 /** 31 * This class will deal with PDF encryption algorithms. 32 * 33 * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a> 34 * @version $Revision: 1.15 $ 35 * 36 * @deprecated use the new security layer instead 37 * 38 * @see org.apache.pdfbox.pdmodel.encryption.StandardSecurityHandler 39 */ 40 public final class PDFEncryption 41 { 42 private ARCFour rc4 = new ARCFour(); 43 /** 44 * The encryption padding defined in the PDF 1.4 Spec algorithm 3.2. 45 */ 46 public static final byte[] ENCRYPT_PADDING = 47 { 48 (byte)0x28, (byte)0xBF, (byte)0x4E, (byte)0x5E, (byte)0x4E, 49 (byte)0x75, (byte)0x8A, (byte)0x41, (byte)0x64, (byte)0x00, 50 (byte)0x4E, (byte)0x56, (byte)0xFF, (byte)0xFA, (byte)0x01, 51 (byte)0x08, (byte)0x2E, (byte)0x2E, (byte)0x00, (byte)0xB6, 52 (byte)0xD0, (byte)0x68, (byte)0x3E, (byte)0x80, (byte)0x2F, 53 (byte)0x0C, (byte)0xA9, (byte)0xFE, (byte)0x64, (byte)0x53, 54 (byte)0x69, (byte)0x7A 55 }; 56 57 /** 58 * This will encrypt a piece of data. 59 * 60 * @param objectNumber The id for the object. 61 * @param genNumber The generation id for the object. 62 * @param key The key used to encrypt the data. 63 * @param data The data to encrypt/decrypt. 64 * @param output The stream to write to. 65 * 66 * @throws CryptographyException If there is an error encrypting the data. 67 * @throws IOException If there is an io error. 68 */ 69 public final void encryptData( 70 long objectNumber, 71 long genNumber, 72 byte[] key, 73 InputStream data, 74 OutputStream output ) 75 throws CryptographyException, IOException 76 { 77 byte[] newKey = new byte[ key.length + 5 ]; 78 System.arraycopy( key, 0, newKey, 0, key.length ); 79 //PDF 1.4 reference pg 73 80 //step 1 81 //we have the reference 82 83 //step 2 84 newKey[newKey.length -5] = (byte)(objectNumber & 0xff); 85 newKey[newKey.length -4] = (byte)((objectNumber >> 8) & 0xff); 86 newKey[newKey.length -3] = (byte)((objectNumber >> 16) & 0xff); 87 newKey[newKey.length -2] = (byte)(genNumber & 0xff); 88 newKey[newKey.length -1] = (byte)((genNumber >> 8) & 0xff); 89 90 91 //step 3 92 byte[] digestedKey = null; 93 try 94 { 95 MessageDigest md = MessageDigest.getInstance( "MD5" ); 96 digestedKey = md.digest( newKey ); 97 } 98 catch( NoSuchAlgorithmException e ) 99 { 100 throw new CryptographyException( e ); 101 } 102 103 //step 4 104 int length = Math.min( newKey.length, 16 ); 105 byte[] finalKey = new byte[ length ]; 106 System.arraycopy( digestedKey, 0, finalKey, 0, length ); 107 108 rc4.setKey( finalKey ); 109 rc4.write( data, output ); 110 output.flush(); 111 } 112 113 /** 114 * This will get the user password from the owner password and the documents o value. 115 * 116 * @param ownerPassword The plaintext owner password. 117 * @param o The document's o entry. 118 * @param revision The document revision number. 119 * @param length The length of the encryption. 120 * 121 * @return The plaintext padded user password. 122 * 123 * @throws CryptographyException If there is an error getting the user password. 124 * @throws IOException If there is an error reading data. 125 */ 126 public final byte[] getUserPassword( 127 byte[] ownerPassword, 128 byte[] o, 129 int revision, 130 long length ) 131 throws CryptographyException, IOException 132 { 133 try 134 { 135 ByteArrayOutputStream result = new ByteArrayOutputStream(); 136 137 //3.3 STEP 1 138 byte[] ownerPadded = truncateOrPad( ownerPassword ); 139 140 //3.3 STEP 2 141 MessageDigest md = MessageDigest.getInstance( "MD5" ); 142 md.update( ownerPadded ); 143 byte[] digest = md.digest(); 144 145 //3.3 STEP 3 146 if( revision == 3 || revision == 4 ) 147 { 148 for( int i=0; i<50; i++ ) 149 { 150 md.reset(); 151 md.update( digest ); 152 digest = md.digest(); 153 } 154 } 155 if( revision == 2 && length != 5 ) 156 { 157 throw new CryptographyException( 158 "Error: Expected length=5 actual=" + length ); 159 } 160 161 //3.3 STEP 4 162 byte[] rc4Key = new byte[ (int)length ]; 163 System.arraycopy( digest, 0, rc4Key, 0, (int)length ); 164 165 //3.7 step 2 166 if( revision == 2 ) 167 { 168 rc4.setKey( rc4Key ); 169 rc4.write( o, result ); 170 } 171 else if( revision == 3 || revision == 4) 172 { 173 /** 174 byte[] iterationKey = new byte[ rc4Key.length ]; 175 byte[] dataToEncrypt = o; 176 for( int i=19; i>=0; i-- ) 177 { 178 System.arraycopy( rc4Key, 0, iterationKey, 0, rc4Key.length ); 179 for( int j=0; j< iterationKey.length; j++ ) 180 { 181 iterationKey[j] = (byte)(iterationKey[j] ^ (byte)i); 182 } 183 rc4.setKey( iterationKey ); 184 rc4.write( dataToEncrypt, result ); 185 dataToEncrypt = result.toByteArray(); 186 result.reset(); 187 } 188 result.write( dataToEncrypt, 0, dataToEncrypt.length ); 189 */ 190 byte[] iterationKey = new byte[ rc4Key.length ]; 191 192 193 byte[] otemp = new byte[ o.length ]; //sm 194 System.arraycopy( o, 0, otemp, 0, o.length ); //sm 195 rc4.write( o, result);//sm 196 197 for( int i=19; i>=0; i-- ) 198 { 199 System.arraycopy( rc4Key, 0, iterationKey, 0, rc4Key.length ); 200 for( int j=0; j< iterationKey.length; j++ ) 201 { 202 iterationKey[j] = (byte)(iterationKey[j] ^ (byte)i); 203 } 204 rc4.setKey( iterationKey ); 205 result.reset(); //sm 206 rc4.write( otemp, result ); //sm 207 otemp = result.toByteArray(); //sm 208 } 209 } 210 211 212 return result.toByteArray(); 213 214 } 215 catch( NoSuchAlgorithmException e ) 216 { 217 throw new CryptographyException( e ); 218 } 219 } 220 221 /** 222 * This will tell if this is the owner password or not. 223 * 224 * @param ownerPassword The plaintext owner password. 225 * @param u The U value from the PDF Document. 226 * @param o The owner password hash. 227 * @param permissions The document permissions. 228 * @param id The document id. 229 * @param revision The revision of the encryption. 230 * @param length The length of the encryption key. 231 * 232 * @return true if the owner password matches the one from the document. 233 * 234 * @throws CryptographyException If there is an error while executing crypt functions. 235 * @throws IOException If there is an error while checking owner password. 236 */ 237 public final boolean isOwnerPassword( 238 byte[] ownerPassword, 239 byte[] u, 240 byte[] o, 241 int permissions, 242 byte[] id, 243 int revision, 244 int length) 245 throws CryptographyException, IOException 246 { 247 byte[] userPassword = getUserPassword( ownerPassword, o, revision, length ); 248 return isUserPassword( userPassword, u, o, permissions, id, revision, length ); 249 } 250 251 /** 252 * This will tell if this is a valid user password. 253 * 254 * Algorithm 3.6 pg 80 255 * 256 * @param password The password to test. 257 * @param u The U value from the PDF Document. 258 * @param o The owner password hash. 259 * @param permissions The document permissions. 260 * @param id The document id. 261 * @param revision The revision of the encryption. 262 * @param length The length of the encryption key. 263 * 264 * @return true If this is the correct user password. 265 * 266 * @throws CryptographyException If there is an error computing the value. 267 * @throws IOException If there is an IO error while computing the owners password. 268 */ 269 public final boolean isUserPassword( 270 byte[] password, 271 byte[] u, 272 byte[] o, 273 int permissions, 274 byte[] id, 275 int revision, 276 int length) 277 throws CryptographyException, IOException 278 { 279 boolean matches = false; 280 //STEP 1 281 byte[] computedValue = computeUserPassword( password, o, permissions, id, revision, length ); 282 if( revision == 2 ) 283 { 284 //STEP 2 285 matches = arraysEqual( u, computedValue ); 286 } 287 else if( revision == 3 || revision == 4 ) 288 { 289 //STEP 2 290 matches = arraysEqual( u, computedValue, 16 ); 291 } 292 return matches; 293 } 294 295 /** 296 * This will compare two byte[] for equality for count number of bytes. 297 * 298 * @param first The first byte array. 299 * @param second The second byte array. 300 * @param count The number of bytes to compare. 301 * 302 * @return true If the arrays contain the exact same data. 303 */ 304 private final boolean arraysEqual( byte[] first, byte[] second, int count ) 305 { 306 boolean equal = first.length >= count && second.length >= count; 307 for( int i=0; i<count && equal; i++ ) 308 { 309 equal = first[i] == second[i]; 310 } 311 return equal; 312 } 313 314 /** 315 * This will compare two byte[] for equality. 316 * 317 * @param first The first byte array. 318 * @param second The second byte array. 319 * 320 * @return true If the arrays contain the exact same data. 321 */ 322 private final boolean arraysEqual( byte[] first, byte[] second ) 323 { 324 boolean equal = first.length == second.length; 325 for( int i=0; i<first.length && equal; i++ ) 326 { 327 equal = first[i] == second[i]; 328 } 329 return equal; 330 } 331 332 /** 333 * This will compute the user password hash. 334 * 335 * @param password The plain text password. 336 * @param o The owner password hash. 337 * @param permissions The document permissions. 338 * @param id The document id. 339 * @param revision The revision of the encryption. 340 * @param length The length of the encryption key. 341 * 342 * @return The user password. 343 * 344 * @throws CryptographyException If there is an error computing the user password. 345 * @throws IOException If there is an IO error. 346 */ 347 public final byte[] computeUserPassword( 348 byte[] password, 349 byte[] o, 350 int permissions, 351 byte[] id, 352 int revision, 353 int length ) 354 throws CryptographyException, IOException 355 { 356 ByteArrayOutputStream result = new ByteArrayOutputStream(); 357 //STEP 1 358 byte[] encryptionKey = computeEncryptedKey( password, o, permissions, id, revision, length ); 359 360 if( revision == 2 ) 361 { 362 //STEP 2 363 rc4.setKey( encryptionKey ); 364 rc4.write( ENCRYPT_PADDING, result ); 365 } 366 else if( revision == 3 || revision == 4 ) 367 { 368 try 369 { 370 //STEP 2 371 MessageDigest md = MessageDigest.getInstance("MD5"); 372 //md.update( truncateOrPad( password ) ); 373 md.update( ENCRYPT_PADDING ); 374 375 //STEP 3 376 md.update( id ); 377 result.write( md.digest() ); 378 379 //STEP 4 and 5 380 byte[] iterationKey = new byte[ encryptionKey.length ]; 381 for( int i=0; i<20; i++ ) 382 { 383 System.arraycopy( encryptionKey, 0, iterationKey, 0, iterationKey.length ); 384 for( int j=0; j< iterationKey.length; j++ ) 385 { 386 iterationKey[j] = (byte)(iterationKey[j] ^ i); 387 } 388 rc4.setKey( iterationKey ); 389 ByteArrayInputStream input = new ByteArrayInputStream( result.toByteArray() ); 390 result.reset(); 391 rc4.write( input, result ); 392 } 393 394 //step 6 395 byte[] finalResult = new byte[32]; 396 System.arraycopy( result.toByteArray(), 0, finalResult, 0, 16 ); 397 System.arraycopy( ENCRYPT_PADDING, 0, finalResult, 16, 16 ); 398 result.reset(); 399 result.write( finalResult ); 400 } 401 catch( NoSuchAlgorithmException e ) 402 { 403 throw new CryptographyException( e ); 404 } 405 } 406 return result.toByteArray(); 407 } 408 409 /** 410 * This will compute the encrypted key. 411 * 412 * @param password The password used to compute the encrypted key. 413 * @param o The owner password hash. 414 * @param permissions The permissions for the document. 415 * @param id The document id. 416 * @param revision The security revision. 417 * @param length The length of the encryption key. 418 * 419 * @return The encryption key. 420 * 421 * @throws CryptographyException If there is an error computing the key. 422 */ 423 public final byte[] computeEncryptedKey( 424 byte[] password, 425 byte[] o, 426 int permissions, 427 byte[] id, 428 int revision, 429 int length ) 430 throws CryptographyException 431 { 432 byte[] result = new byte[ length ]; 433 try 434 { 435 //PDFReference 1.4 pg 78 436 //step1 437 byte[] padded = truncateOrPad( password ); 438 439 //step 2 440 MessageDigest md = MessageDigest.getInstance("MD5"); 441 md.update( padded ); 442 443 //step 3 444 md.update( o ); 445 446 //step 4 447 byte zero = (byte)(permissions >>> 0); 448 byte one = (byte)(permissions >>> 8); 449 byte two = (byte)(permissions >>> 16); 450 byte three = (byte)(permissions >>> 24); 451 452 md.update( zero ); 453 md.update( one ); 454 md.update( two ); 455 md.update( three ); 456 457 //step 5 458 md.update( id ); 459 byte[] digest = md.digest(); 460 461 //step 6 462 if( revision == 3 || revision == 4) 463 { 464 for( int i=0; i<50; i++ ) 465 { 466 md.reset(); 467 md.update( digest, 0, length ); 468 digest = md.digest(); 469 } 470 } 471 472 //step 7 473 if( revision == 2 && length != 5 ) 474 { 475 throw new CryptographyException( 476 "Error: length should be 5 when revision is two actual=" + length ); 477 } 478 System.arraycopy( digest, 0, result, 0, length ); 479 } 480 catch( NoSuchAlgorithmException e ) 481 { 482 throw new CryptographyException( e ); 483 } 484 return result; 485 } 486 487 /** 488 * This algorithm is taked from PDF Reference 1.4 Algorithm 3.3 Page 79. 489 * 490 * @param ownerPassword The plain owner password. 491 * @param userPassword The plain user password. 492 * @param revision The version of the security. 493 * @param length The length of the document. 494 * 495 * @return The computed owner password. 496 * 497 * @throws CryptographyException If there is an error computing O. 498 * @throws IOException If there is an error computing O. 499 */ 500 public final byte[] computeOwnerPassword( 501 byte[] ownerPassword, 502 byte[] userPassword, 503 int revision, 504 int length ) 505 throws CryptographyException, IOException 506 { 507 try 508 { 509 //STEP 1 510 byte[] ownerPadded = truncateOrPad( ownerPassword ); 511 512 //STEP 2 513 MessageDigest md = MessageDigest.getInstance( "MD5" ); 514 md.update( ownerPadded ); 515 byte[] digest = md.digest(); 516 517 //STEP 3 518 if( revision == 3 || revision == 4) 519 { 520 for( int i=0; i<50; i++ ) 521 { 522 md.reset(); 523 md.update( digest, 0, length ); 524 digest = md.digest(); 525 } 526 } 527 if( revision == 2 && length != 5 ) 528 { 529 throw new CryptographyException( 530 "Error: Expected length=5 actual=" + length ); 531 } 532 533 //STEP 4 534 byte[] rc4Key = new byte[ length ]; 535 System.arraycopy( digest, 0, rc4Key, 0, length ); 536 537 //STEP 5 538 byte[] paddedUser = truncateOrPad( userPassword ); 539 540 541 //STEP 6 542 rc4.setKey( rc4Key ); 543 ByteArrayOutputStream crypted = new ByteArrayOutputStream(); 544 rc4.write( new ByteArrayInputStream( paddedUser ), crypted ); 545 546 547 //STEP 7 548 if( revision == 3 || revision == 4 ) 549 { 550 byte[] iterationKey = new byte[ rc4Key.length ]; 551 for( int i=1; i<20; i++ ) 552 { 553 System.arraycopy( rc4Key, 0, iterationKey, 0, rc4Key.length ); 554 for( int j=0; j< iterationKey.length; j++ ) 555 { 556 iterationKey[j] = (byte)(iterationKey[j] ^ (byte)i); 557 } 558 rc4.setKey( iterationKey ); 559 ByteArrayInputStream input = new ByteArrayInputStream( crypted.toByteArray() ); 560 crypted.reset(); 561 rc4.write( input, crypted ); 562 } 563 } 564 565 //STEP 8 566 return crypted.toByteArray(); 567 } 568 catch( NoSuchAlgorithmException e ) 569 { 570 throw new CryptographyException( e.getMessage() ); 571 } 572 } 573 574 /** 575 * This will take the password and truncate or pad it as necessary. 576 * 577 * @param password The password to pad or truncate. 578 * 579 * @return The padded or truncated password. 580 */ 581 private final byte[] truncateOrPad( byte[] password ) 582 { 583 byte[] padded = new byte[ ENCRYPT_PADDING.length ]; 584 int bytesBeforePad = Math.min( password.length, padded.length ); 585 System.arraycopy( password, 0, padded, 0, bytesBeforePad ); 586 System.arraycopy( ENCRYPT_PADDING, 0, padded, bytesBeforePad, ENCRYPT_PADDING.length-bytesBeforePad ); 587 return padded; 588 } 589 }