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 18 /* $Id: PDFEncryptionJCE.java 1661887 2015-02-24 11:23:44Z ssteiner $ */ 19 20 package org.apache.fop.pdf; 21 22 import java.io.IOException; 23 import java.io.OutputStream; 24 import java.io.UnsupportedEncodingException; 25 import java.security.InvalidAlgorithmParameterException; 26 import java.security.InvalidKeyException; 27 import java.security.MessageDigest; 28 import java.security.NoSuchAlgorithmException; 29 import java.security.SecureRandom; 30 import java.util.Arrays; 31 32 import javax.crypto.BadPaddingException; 33 import javax.crypto.Cipher; 34 import javax.crypto.CipherOutputStream; 35 import javax.crypto.IllegalBlockSizeException; 36 import javax.crypto.NoSuchPaddingException; 37 import javax.crypto.spec.IvParameterSpec; 38 import javax.crypto.spec.SecretKeySpec; 39 40 /** 41 * An implementation of the Standard Security Handler. 42 */ 43 public final class PDFEncryptionJCE extends PDFObject implements PDFEncryption { 44 45 private final MessageDigest digest; 46 47 private SecureRandom random; 48 49 private byte[] encryptionKey; 50 51 private String encryptionDictionary; 52 53 private boolean useAlgorithm31a; 54 55 private boolean encryptMetadata = true; 56 57 private Version pdfVersion = Version.V1_4; 58 59 private static byte[] ivZero = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 60 61 private class EncryptionInitializer { 62 63 private final PDFEncryptionParams encryptionParams; 64 65 private int encryptionLength; 66 67 private int version; 68 69 private int revision; 70 EncryptionInitializer(PDFEncryptionParams params)71 EncryptionInitializer(PDFEncryptionParams params) { 72 this.encryptionParams = new PDFEncryptionParams(params); 73 } 74 init()75 void init() { 76 encryptionLength = encryptionParams.getEncryptionLengthInBits(); 77 determineEncryptionAlgorithm(); 78 int permissions = Permission.computePermissions(encryptionParams); 79 EncryptionSettings encryptionSettings = new EncryptionSettings( 80 encryptionLength, permissions, 81 encryptionParams.getUserPassword(), encryptionParams.getOwnerPassword(), 82 encryptionParams.encryptMetadata()); 83 InitializationEngine initializationEngine = createEngine(encryptionSettings); 84 initializationEngine.run(); 85 encryptionDictionary = createEncryptionDictionary(permissions, initializationEngine); 86 encryptMetadata = encryptionParams.encryptMetadata(); 87 } 88 createEngine(EncryptionSettings encryptionSettings)89 private InitializationEngine createEngine(EncryptionSettings encryptionSettings) { 90 if (revision == 5) { 91 return new Rev5Engine(encryptionSettings); 92 } else if (revision == 2) { 93 return new Rev2Engine(encryptionSettings); 94 } else { 95 return new Rev3Engine(encryptionSettings); 96 } 97 } 98 determineEncryptionAlgorithm()99 private void determineEncryptionAlgorithm() { 100 if (isVersion5Revision5Algorithm()) { 101 version = 5; 102 revision = 5; 103 pdfVersion = Version.V1_7; 104 } else if (isVersion1Revision2Algorithm()) { 105 version = 1; 106 revision = 2; 107 } else { 108 version = 2; 109 revision = 3; 110 } 111 } 112 isVersion1Revision2Algorithm()113 private boolean isVersion1Revision2Algorithm() { 114 return encryptionLength == 40 115 && encryptionParams.isAllowFillInForms() 116 && encryptionParams.isAllowAccessContent() 117 && encryptionParams.isAllowAssembleDocument() 118 && encryptionParams.isAllowPrintHq(); 119 } 120 isVersion5Revision5Algorithm()121 private boolean isVersion5Revision5Algorithm() { 122 return encryptionLength == 256; 123 } 124 createEncryptionDictionary(final int permissions, InitializationEngine engine)125 private String createEncryptionDictionary(final int permissions, InitializationEngine engine) { 126 String encryptionDict = "<<\n" 127 + "/Filter /Standard\n" 128 + "/V " + version + "\n" 129 + "/R " + revision + "\n" 130 + "/Length " + encryptionLength + "\n" 131 + "/P " + permissions + "\n" 132 + engine.getEncryptionDictionaryPart() 133 + ">>"; 134 return encryptionDict; 135 } 136 137 } 138 139 private static enum Permission { 140 141 PRINT(3), 142 EDIT_CONTENT(4), 143 COPY_CONTENT(5), 144 EDIT_ANNOTATIONS(6), 145 FILL_IN_FORMS(9), 146 ACCESS_CONTENT(10), 147 ASSEMBLE_DOCUMENT(11), 148 PRINT_HQ(12); 149 150 private final int mask; 151 152 /** 153 * Creates a new permission. 154 * 155 * @param bit bit position for this permission, 1-based to match the PDF Reference 156 */ Permission(int bit)157 private Permission(int bit) { 158 mask = 1 << (bit - 1); 159 } 160 removeFrom(int permissions)161 private int removeFrom(int permissions) { 162 return permissions - mask; 163 } 164 computePermissions(PDFEncryptionParams encryptionParams)165 static int computePermissions(PDFEncryptionParams encryptionParams) { 166 int permissions = -4; 167 168 if (!encryptionParams.isAllowPrint()) { 169 permissions = PRINT.removeFrom(permissions); 170 } 171 if (!encryptionParams.isAllowCopyContent()) { 172 permissions = COPY_CONTENT.removeFrom(permissions); 173 } 174 if (!encryptionParams.isAllowEditContent()) { 175 permissions = EDIT_CONTENT.removeFrom(permissions); 176 } 177 if (!encryptionParams.isAllowEditAnnotations()) { 178 permissions = EDIT_ANNOTATIONS.removeFrom(permissions); 179 } 180 if (!encryptionParams.isAllowFillInForms()) { 181 permissions = FILL_IN_FORMS.removeFrom(permissions); 182 } 183 if (!encryptionParams.isAllowAccessContent()) { 184 permissions = ACCESS_CONTENT.removeFrom(permissions); 185 } 186 if (!encryptionParams.isAllowAssembleDocument()) { 187 permissions = ASSEMBLE_DOCUMENT.removeFrom(permissions); 188 } 189 if (!encryptionParams.isAllowPrintHq()) { 190 permissions = PRINT_HQ.removeFrom(permissions); 191 } 192 return permissions; 193 } 194 } 195 196 private static final class EncryptionSettings { 197 198 final int encryptionLength; 199 200 final int permissions; 201 202 final String userPassword; 203 204 final String ownerPassword; 205 206 final boolean encryptMetadata; 207 EncryptionSettings(int encryptionLength, int permissions, String userPassword, String ownerPassword, boolean encryptMetadata)208 EncryptionSettings(int encryptionLength, int permissions, 209 String userPassword, String ownerPassword, boolean encryptMetadata) { 210 this.encryptionLength = encryptionLength; 211 this.permissions = permissions; 212 this.userPassword = userPassword; 213 this.ownerPassword = ownerPassword; 214 this.encryptMetadata = encryptMetadata; 215 } 216 217 } 218 219 private abstract class InitializationEngine { 220 221 protected final int encryptionLengthInBytes; 222 223 protected final int permissions; 224 225 private final String userPassword; 226 227 private final String ownerPassword; 228 229 protected byte[] oValue; 230 231 protected byte[] uValue; 232 233 protected byte[] preparedUserPassword; 234 235 protected byte[] preparedOwnerPassword; 236 InitializationEngine(EncryptionSettings encryptionSettings)237 InitializationEngine(EncryptionSettings encryptionSettings) { 238 this.encryptionLengthInBytes = encryptionSettings.encryptionLength / 8; 239 this.permissions = encryptionSettings.permissions; 240 this.userPassword = encryptionSettings.userPassword; 241 this.ownerPassword = encryptionSettings.ownerPassword; 242 } 243 run()244 void run() { 245 preparedUserPassword = preparePassword(userPassword); 246 if (ownerPassword == null || ownerPassword.length() == 0) { 247 preparedOwnerPassword = preparedUserPassword; 248 } else { 249 preparedOwnerPassword = preparePassword(ownerPassword); 250 } 251 } 252 getEncryptionDictionaryPart()253 protected String getEncryptionDictionaryPart() { 254 String encryptionDictionaryPart = "/O " + PDFText.toHex(oValue) + "\n" 255 + "/U " + PDFText.toHex(uValue) + "\n"; 256 return encryptionDictionaryPart; 257 } 258 computeOValue()259 protected abstract void computeOValue(); 260 computeUValue()261 protected abstract void computeUValue(); 262 createEncryptionKey()263 protected abstract void createEncryptionKey(); 264 preparePassword(String password)265 protected abstract byte[] preparePassword(String password); 266 } 267 268 private abstract class RevBefore5Engine extends InitializationEngine { 269 270 /** Padding for passwords. */ 271 protected final byte[] padding = new byte[] {(byte) 0x28, (byte) 0xBF, (byte) 0x4E, (byte) 0x5E, 272 (byte) 0x4E, (byte) 0x75, (byte) 0x8A, (byte) 0x41, (byte) 0x64, (byte) 0x00, (byte) 0x4E, 273 (byte) 0x56, (byte) 0xFF, (byte) 0xFA, (byte) 0x01, (byte) 0x08, (byte) 0x2E, (byte) 0x2E, 274 (byte) 0x00, (byte) 0xB6, (byte) 0xD0, (byte) 0x68, (byte) 0x3E, (byte) 0x80, (byte) 0x2F, 275 (byte) 0x0C, (byte) 0xA9, (byte) 0xFE, (byte) 0x64, (byte) 0x53, (byte) 0x69, (byte) 0x7A}; 276 RevBefore5Engine(EncryptionSettings encryptionSettings)277 RevBefore5Engine(EncryptionSettings encryptionSettings) { 278 super(encryptionSettings); 279 } 280 281 /** 282 * Applies Algorithm 3.3 Page 79 of the PDF 1.4 Reference. 283 * 284 */ computeOValue()285 protected void computeOValue() { 286 // Step 1 287 byte[] md5Input = preparedOwnerPassword; 288 // Step 2 289 digest.reset(); 290 byte[] hash = digest.digest(md5Input); 291 // Step 3 292 hash = computeOValueStep3(hash); 293 // Step 4 294 byte[] key = new byte[encryptionLengthInBytes]; 295 System.arraycopy(hash, 0, key, 0, encryptionLengthInBytes); 296 // Steps 5, 6 297 byte[] encryptionResult = encryptWithKey(key, preparedUserPassword); 298 // Step 7 299 oValue = computeOValueStep7(key, encryptionResult); 300 } 301 302 /** 303 * Applies Algorithm 3.2 Page 78 of the PDF 1.4 Reference. 304 */ createEncryptionKey()305 protected void createEncryptionKey() { 306 // Steps 1, 2 307 digest.reset(); 308 digest.update(preparedUserPassword); 309 // Step 3 310 digest.update(oValue); 311 // Step 4 312 digest.update((byte) (permissions >>> 0)); 313 digest.update((byte) (permissions >>> 8)); 314 digest.update((byte) (permissions >>> 16)); 315 digest.update((byte) (permissions >>> 24)); 316 // Step 5 317 digest.update(getDocumentSafely().getFileIDGenerator().getOriginalFileID()); 318 byte[] hash = digest.digest(); 319 // Step 6 320 hash = createEncryptionKeyStep6(hash); 321 // Step 7 322 encryptionKey = new byte[encryptionLengthInBytes]; 323 System.arraycopy(hash, 0, encryptionKey, 0, encryptionLengthInBytes); 324 } 325 326 /** 327 * Adds padding to the password as directed in page 78 of the PDF 1.4 Reference. 328 * 329 * @param password the password 330 * @return the password with additional padding if necessary 331 */ preparePassword(String password)332 protected byte[] preparePassword(String password) { 333 int finalLength = 32; 334 byte[] preparedPassword = new byte[finalLength]; 335 try { 336 byte[] passwordBytes = password.getBytes("UTF-8"); 337 if (passwordBytes.length >= finalLength) { 338 System.arraycopy(passwordBytes, 0, preparedPassword, 0, finalLength); 339 } else { 340 System.arraycopy(passwordBytes, 0, preparedPassword, 0, passwordBytes.length); 341 System.arraycopy(padding, 0, preparedPassword, passwordBytes.length, finalLength 342 - passwordBytes.length); 343 } 344 return preparedPassword; 345 } catch (UnsupportedEncodingException e) { 346 throw new UnsupportedOperationException(e); 347 } 348 } 349 run()350 void run() { 351 super.run(); 352 computeOValue(); 353 createEncryptionKey(); 354 computeUValue(); 355 } 356 computeOValueStep3(byte[] hash)357 protected abstract byte[] computeOValueStep3(byte[] hash); 358 computeOValueStep7(byte[] key, byte[] encryptionResult)359 protected abstract byte[] computeOValueStep7(byte[] key, byte[] encryptionResult); 360 createEncryptionKeyStep6(byte[] hash)361 protected abstract byte[] createEncryptionKeyStep6(byte[] hash); 362 363 } 364 365 private class Rev2Engine extends RevBefore5Engine { 366 Rev2Engine(EncryptionSettings encryptionSettings)367 Rev2Engine(EncryptionSettings encryptionSettings) { 368 super(encryptionSettings); 369 } 370 371 @Override computeOValueStep3(byte[] hash)372 protected byte[] computeOValueStep3(byte[] hash) { 373 return hash; 374 } 375 376 @Override computeOValueStep7(byte[] key, byte[] encryptionResult)377 protected byte[] computeOValueStep7(byte[] key, byte[] encryptionResult) { 378 return encryptionResult; 379 } 380 381 @Override createEncryptionKeyStep6(byte[] hash)382 protected byte[] createEncryptionKeyStep6(byte[] hash) { 383 return hash; 384 } 385 386 @Override computeUValue()387 protected void computeUValue() { 388 uValue = encryptWithKey(encryptionKey, padding); 389 } 390 391 } 392 393 private class Rev3Engine extends RevBefore5Engine { 394 Rev3Engine(EncryptionSettings encryptionSettings)395 Rev3Engine(EncryptionSettings encryptionSettings) { 396 super(encryptionSettings); 397 } 398 399 @Override computeOValueStep3(byte[] hash)400 protected byte[] computeOValueStep3(byte[] hash) { 401 for (int i = 0; i < 50; i++) { 402 hash = digest.digest(hash); 403 } 404 return hash; 405 } 406 407 @Override computeOValueStep7(byte[] key, byte[] encryptionResult)408 protected byte[] computeOValueStep7(byte[] key, byte[] encryptionResult) { 409 return xorKeyAndEncrypt19Times(key, encryptionResult); 410 } 411 412 @Override createEncryptionKeyStep6(byte[] hash)413 protected byte[] createEncryptionKeyStep6(byte[] hash) { 414 for (int i = 0; i < 50; i++) { 415 digest.update(hash, 0, encryptionLengthInBytes); 416 hash = digest.digest(); 417 } 418 return hash; 419 } 420 421 @Override computeUValue()422 protected void computeUValue() { 423 // Step 1 is encryptionKey 424 // Step 2 425 digest.reset(); 426 digest.update(padding); 427 // Step 3 428 digest.update(getDocumentSafely().getFileIDGenerator().getOriginalFileID()); 429 // Step 4 430 byte[] encryptionResult = encryptWithKey(encryptionKey, digest.digest()); 431 // Step 5 432 encryptionResult = xorKeyAndEncrypt19Times(encryptionKey, encryptionResult); 433 // Step 6 434 uValue = new byte[32]; 435 System.arraycopy(encryptionResult, 0, uValue, 0, 16); 436 // Add the arbitrary padding 437 Arrays.fill(uValue, 16, 32, (byte) 0); 438 } 439 xorKeyAndEncrypt19Times(byte[] key, byte[] input)440 private byte[] xorKeyAndEncrypt19Times(byte[] key, byte[] input) { 441 byte[] result = input; 442 byte[] encryptionKey = new byte[key.length]; 443 for (int i = 1; i <= 19; i++) { 444 for (int j = 0; j < key.length; j++) { 445 encryptionKey[j] = (byte) (key[j] ^ i); 446 } 447 result = encryptWithKey(encryptionKey, result); 448 } 449 return result; 450 } 451 452 } 453 454 private class Rev5Engine extends InitializationEngine { 455 456 // private SecureRandom random = new SecureRandom(); 457 private byte[] userValidationSalt = new byte[8]; 458 private byte[] userKeySalt = new byte[8]; 459 private byte[] ownerValidationSalt = new byte[8]; 460 private byte[] ownerKeySalt = new byte[8]; 461 private byte[] ueValue; 462 private byte[] oeValue; 463 private final boolean encryptMetadata; 464 Rev5Engine(EncryptionSettings encryptionSettings)465 Rev5Engine(EncryptionSettings encryptionSettings) { 466 super(encryptionSettings); 467 this.encryptMetadata = encryptionSettings.encryptMetadata; 468 } 469 run()470 void run() { 471 super.run(); 472 random = new SecureRandom(); 473 createEncryptionKey(); 474 computeUValue(); 475 computeOValue(); 476 computeUEValue(); 477 computeOEValue(); 478 } 479 getEncryptionDictionaryPart()480 protected String getEncryptionDictionaryPart() { 481 String encryptionDictionaryPart = super.getEncryptionDictionaryPart(); 482 encryptionDictionaryPart += "/OE " + PDFText.toHex(oeValue) + "\n" 483 + "/UE " + PDFText.toHex(ueValue) + "\n" 484 + "/Perms " + PDFText.toHex(computePermsValue(permissions)) + "\n" 485 + "/EncryptMetadata " + encryptMetadata + "\n" 486 // note: I think Length below should be 256 but Acrobat 9 uses 32... 487 + "/CF <</StdCF <</AuthEvent /DocOpen /CFM /AESV3 /Length 32>>>>\n" 488 + "/StmF /StdCF /StrF /StdCF\n"; 489 return encryptionDictionaryPart; 490 } 491 492 /** 493 * Algorithm 3.8-1 (page 20, Adobe Supplement to the ISO 32000, BaseVersion: 1.7, ExtensionLevel: 3) 494 */ 495 @Override computeUValue()496 protected void computeUValue() { 497 byte[] userBytes = new byte[16]; 498 random.nextBytes(userBytes); 499 System.arraycopy(userBytes, 0, userValidationSalt, 0, 8); 500 System.arraycopy(userBytes, 8, userKeySalt, 0, 8); 501 digest.reset(); 502 byte[] prepared = preparedUserPassword; 503 byte[] concatenated = new byte[prepared.length + 8]; 504 System.arraycopy(prepared, 0, concatenated, 0, prepared.length); 505 System.arraycopy(userValidationSalt, 0, concatenated, prepared.length, 8); 506 digest.update(concatenated); 507 byte[] sha256 = digest.digest(); 508 uValue = new byte[48]; 509 System.arraycopy(sha256, 0, uValue, 0, 32); 510 System.arraycopy(userValidationSalt, 0, uValue, 32, 8); 511 System.arraycopy(userKeySalt, 0, uValue, 40, 8); 512 } 513 514 /** 515 * Algorithm 3.9-1 (page 20, Adobe Supplement to the ISO 32000, BaseVersion: 1.7, ExtensionLevel: 3) 516 */ 517 @Override computeOValue()518 protected void computeOValue() { 519 byte[] ownerBytes = new byte[16]; 520 random.nextBytes(ownerBytes); 521 System.arraycopy(ownerBytes, 0, ownerValidationSalt, 0, 8); 522 System.arraycopy(ownerBytes, 8, ownerKeySalt, 0, 8); 523 digest.reset(); 524 byte[] prepared = preparedOwnerPassword; 525 byte[] concatenated = new byte[prepared.length + 56]; 526 System.arraycopy(prepared, 0, concatenated, 0, prepared.length); 527 System.arraycopy(ownerValidationSalt, 0, concatenated, prepared.length, 8); 528 System.arraycopy(uValue, 0, concatenated, prepared.length + 8, 48); 529 digest.update(concatenated); 530 byte[] sha256 = digest.digest(); 531 oValue = new byte[48]; 532 System.arraycopy(sha256, 0, oValue, 0, 32); 533 System.arraycopy(ownerValidationSalt, 0, oValue, 32, 8); 534 System.arraycopy(ownerKeySalt, 0, oValue, 40, 8); 535 } 536 537 /** 538 * See Adobe Supplement to the ISO 32000, BaseVersion: 1.7, ExtensionLevel: 3, page 20, paragraph 5. 539 */ createEncryptionKey()540 protected void createEncryptionKey() { 541 encryptionKey = new byte[encryptionLengthInBytes]; 542 random.nextBytes(encryptionKey); 543 } 544 545 /** 546 * Algorithm 3.2a-1 (page 19, Adobe Supplement to the ISO 32000, BaseVersion: 1.7, ExtensionLevel: 3) 547 */ preparePassword(String password)548 protected byte[] preparePassword(String password) { 549 byte[] passwordBytes; 550 byte[] preparedPassword; 551 try { 552 // the password needs to be normalized first but we are bypassing that step for now 553 passwordBytes = password.getBytes("UTF-8"); 554 if (passwordBytes.length > 127) { 555 preparedPassword = new byte[127]; 556 System.arraycopy(passwordBytes, 0, preparedPassword, 0, 127); 557 } else { 558 preparedPassword = new byte[passwordBytes.length]; 559 System.arraycopy(passwordBytes, 0, preparedPassword, 0, passwordBytes.length); 560 } 561 return preparedPassword; 562 } catch (UnsupportedEncodingException e) { 563 throw new UnsupportedOperationException(e.getMessage()); 564 } 565 } 566 567 /** 568 * Algorithm 3.8-2 (page 20, Adobe Supplement to the ISO 32000, BaseVersion: 1.7, ExtensionLevel: 3) 569 */ computeUEValue()570 private void computeUEValue() { 571 digest.reset(); 572 byte[] prepared = preparedUserPassword; 573 byte[] concatenated = new byte[prepared.length + 8]; 574 System.arraycopy(prepared, 0, concatenated, 0, prepared.length); 575 System.arraycopy(userKeySalt, 0, concatenated, prepared.length, 8); 576 digest.update(concatenated); 577 byte[] ueEncryptionKey = digest.digest(); 578 ueValue = encryptWithKey(ueEncryptionKey, encryptionKey, true, ivZero); 579 } 580 581 /** 582 * Algorithm 3.9-2 (page 20, Adobe Supplement to the ISO 32000, BaseVersion: 1.7, ExtensionLevel: 3) 583 */ computeOEValue()584 private void computeOEValue() { 585 digest.reset(); 586 byte[] prepared = preparedOwnerPassword; 587 byte[] concatenated = new byte[prepared.length + 56]; 588 System.arraycopy(prepared, 0, concatenated, 0, prepared.length); 589 System.arraycopy(ownerKeySalt, 0, concatenated, prepared.length, 8); 590 System.arraycopy(uValue, 0, concatenated, prepared.length + 8, 48); 591 digest.update(concatenated); 592 byte[] oeEncryptionKey = digest.digest(); 593 oeValue = encryptWithKey(oeEncryptionKey, encryptionKey, true, ivZero); 594 } 595 596 /** 597 * Algorithm 3.10 (page 20, Adobe Supplement to the ISO 32000, BaseVersion: 1.7, ExtensionLevel: 3) 598 */ computePermsValue(int permissions)599 public byte[] computePermsValue(int permissions) { 600 byte[] perms = new byte[16]; 601 long extendedPermissions = 0xffffffff00000000L | permissions; 602 for (int k = 0; k < 8; k++) { 603 perms[k] = (byte) (extendedPermissions & 0xff); 604 extendedPermissions >>= 8; 605 } 606 if (encryptMetadata) { 607 perms[8] = 'T'; 608 } else { 609 perms[8] = 'F'; 610 } 611 perms[9] = 'a'; 612 perms[10] = 'd'; 613 perms[11] = 'b'; 614 byte[] randomBytes = new byte[4]; 615 random.nextBytes(randomBytes); 616 System.arraycopy(randomBytes, 0, perms, 12, 4); 617 byte[] encryptedPerms = encryptWithKey(encryptionKey, perms, true, ivZero); 618 return encryptedPerms; 619 } 620 } 621 622 private class EncryptionFilter extends PDFFilter { 623 624 private PDFObjectNumber streamNumber; 625 626 private int streamGeneration; 627 EncryptionFilter(PDFObjectNumber streamNumber, int streamGeneration)628 EncryptionFilter(PDFObjectNumber streamNumber, int streamGeneration) { 629 this.streamNumber = streamNumber; 630 this.streamGeneration = streamGeneration; 631 } 632 633 /** 634 * Returns a PDF string representation of this filter. 635 * 636 * @return the empty string 637 */ getName()638 public String getName() { 639 return ""; 640 } 641 642 /** 643 * Returns a parameter dictionary for this filter. 644 * 645 * @return null, this filter has no parameters 646 */ getDecodeParms()647 public PDFObject getDecodeParms() { 648 return null; 649 } 650 651 /** {@inheritDoc} */ applyFilter(OutputStream out)652 public OutputStream applyFilter(OutputStream out) throws IOException { 653 if (useAlgorithm31a) { 654 byte[] iv = new byte[16]; 655 random.nextBytes(iv); 656 Cipher cipher = initCipher(encryptionKey, false, iv); 657 out.write(iv); 658 out.flush(); 659 return new CipherOutputStream(out, cipher); 660 } else { 661 byte[] key = createEncryptionKey(streamNumber.getNumber(), streamGeneration); 662 Cipher cipher = initCipher(key); 663 return new CipherOutputStream(out, cipher); 664 } 665 } 666 667 } 668 PDFEncryptionJCE(PDFObjectNumber objectNumber, PDFEncryptionParams params, PDFDocument pdf)669 private PDFEncryptionJCE(PDFObjectNumber objectNumber, PDFEncryptionParams params, PDFDocument pdf) { 670 setObjectNumber(objectNumber); 671 try { 672 if (params.getEncryptionLengthInBits() == 256) { 673 digest = MessageDigest.getInstance("SHA-256"); 674 } else { 675 digest = MessageDigest.getInstance("MD5"); 676 } 677 } catch (NoSuchAlgorithmException e) { 678 throw new UnsupportedOperationException(e.getMessage()); 679 } 680 setDocument(pdf); 681 EncryptionInitializer encryptionInitializer = new EncryptionInitializer(params); 682 encryptionInitializer.init(); 683 useAlgorithm31a = encryptionInitializer.isVersion5Revision5Algorithm(); 684 } 685 686 /** 687 * Creates and returns an encryption object. 688 * 689 * @param objectNumber the object number for the encryption dictionary 690 * @param params the encryption parameters 691 * @param pdf the PDF document to be encrypted 692 * @return the newly created encryption object 693 */ make( PDFObjectNumber objectNumber, PDFEncryptionParams params, PDFDocument pdf)694 public static PDFEncryption make( 695 PDFObjectNumber objectNumber, PDFEncryptionParams params, PDFDocument pdf) { 696 return new PDFEncryptionJCE(objectNumber, params, pdf); 697 } 698 699 /** {@inheritDoc} */ encrypt(byte[] data, PDFObject refObj)700 public byte[] encrypt(byte[] data, PDFObject refObj) { 701 PDFObject o = refObj; 702 while (o != null && !o.hasObjectNumber()) { 703 o = o.getParent(); 704 } 705 if (o == null && !useAlgorithm31a) { 706 throw new IllegalStateException("No object number could be obtained for a PDF object"); 707 } 708 if (useAlgorithm31a) { 709 byte[] iv = new byte[16]; 710 random.nextBytes(iv); 711 byte[] encryptedData = encryptWithKey(encryptionKey, data, false, iv); 712 byte[] storedData = new byte[encryptedData.length + 16]; 713 System.arraycopy(iv, 0, storedData, 0, 16); 714 System.arraycopy(encryptedData, 0, storedData, 16, encryptedData.length); 715 return storedData; 716 } else { 717 byte[] key = createEncryptionKey(o.getObjectNumber().getNumber(), o.getGeneration()); 718 return encryptWithKey(key, data); 719 } 720 } 721 722 /** {@inheritDoc} */ applyFilter(AbstractPDFStream stream)723 public void applyFilter(AbstractPDFStream stream) { 724 if (!encryptMetadata && stream instanceof PDFMetadata) { 725 return; 726 } 727 stream.getFilterList().addFilter( 728 new EncryptionFilter(stream.getObjectNumber(), stream.getGeneration())); 729 } 730 731 /** 732 * Prepares the encryption dictionary for output to a PDF file. 733 * 734 * @return the encryption dictionary as a byte array 735 */ toPDF()736 public byte[] toPDF() { 737 assert encryptionDictionary != null; 738 return encode(this.encryptionDictionary); 739 } 740 741 /** {@inheritDoc} */ getTrailerEntry()742 public String getTrailerEntry() { 743 return "/Encrypt " + getObjectNumber() + " " + getGeneration() + " R\n"; 744 } 745 encryptWithKey(byte[] key, byte[] data)746 private static byte[] encryptWithKey(byte[] key, byte[] data) { 747 try { 748 final Cipher c = initCipher(key); 749 return c.doFinal(data); 750 } catch (IllegalBlockSizeException e) { 751 throw new IllegalStateException(e.getMessage()); 752 } catch (BadPaddingException e) { 753 throw new IllegalStateException(e.getMessage()); 754 } 755 } 756 encryptWithKey(byte[] key, byte[] data, boolean noPadding, byte[] iv)757 private static byte[] encryptWithKey(byte[] key, byte[] data, boolean noPadding, byte[] iv) { 758 try { 759 final Cipher c = initCipher(key, noPadding, iv); 760 return c.doFinal(data); 761 } catch (IllegalBlockSizeException e) { 762 throw new IllegalStateException(e.getMessage()); 763 } catch (BadPaddingException e) { 764 throw new IllegalStateException(e.getMessage()); 765 } 766 } 767 initCipher(byte[] key)768 private static Cipher initCipher(byte[] key) { 769 try { 770 SecretKeySpec keyspec = new SecretKeySpec(key, "RC4"); 771 Cipher cipher = Cipher.getInstance("RC4"); 772 cipher.init(Cipher.ENCRYPT_MODE, keyspec); 773 return cipher; 774 } catch (InvalidKeyException e) { 775 throw new IllegalStateException(e); 776 } catch (NoSuchAlgorithmException e) { 777 throw new UnsupportedOperationException(e); 778 } catch (NoSuchPaddingException e) { 779 throw new UnsupportedOperationException(e); 780 } 781 } 782 initCipher(byte[] key, boolean noPadding, byte[] iv)783 private static Cipher initCipher(byte[] key, boolean noPadding, byte[] iv) { 784 try { 785 SecretKeySpec skeySpec = new SecretKeySpec(key, "AES"); 786 IvParameterSpec ivspec = new IvParameterSpec(iv); 787 Cipher cipher = noPadding ? Cipher.getInstance("AES/CBC/NoPadding") : Cipher 788 .getInstance("AES/CBC/PKCS5Padding"); 789 cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivspec); 790 return cipher; 791 } catch (InvalidKeyException e) { 792 throw new IllegalStateException(e); 793 } catch (NoSuchAlgorithmException e) { 794 throw new UnsupportedOperationException(e); 795 } catch (NoSuchPaddingException e) { 796 throw new UnsupportedOperationException(e); 797 } catch (InvalidAlgorithmParameterException e) { 798 throw new UnsupportedOperationException(e); 799 } 800 } 801 802 /** 803 * Applies Algorithm 3.1 from the PDF 1.4 Reference. 804 * 805 * @param objectNumber the object number 806 * @param generationNumber the generation number 807 * @return the key to use for encryption 808 */ createEncryptionKey(int objectNumber, int generationNumber)809 private byte[] createEncryptionKey(int objectNumber, int generationNumber) { 810 // Step 1 passed in 811 // Step 2 812 byte[] md5Input = prepareMD5Input(objectNumber, generationNumber); 813 // Step 3 814 digest.reset(); 815 byte[] hash = digest.digest(md5Input); 816 // Step 4 817 int keyLength = Math.min(16, md5Input.length); 818 byte[] key = new byte[keyLength]; 819 System.arraycopy(hash, 0, key, 0, keyLength); 820 return key; 821 } 822 prepareMD5Input(int objectNumber, int generationNumber)823 private byte[] prepareMD5Input(int objectNumber, int generationNumber) { 824 byte[] md5Input = new byte[encryptionKey.length + 5]; 825 System.arraycopy(encryptionKey, 0, md5Input, 0, encryptionKey.length); 826 int i = encryptionKey.length; 827 md5Input[i++] = (byte) (objectNumber >>> 0); 828 md5Input[i++] = (byte) (objectNumber >>> 8); 829 md5Input[i++] = (byte) (objectNumber >>> 16); 830 md5Input[i++] = (byte) (generationNumber >>> 0); 831 md5Input[i++] = (byte) (generationNumber >>> 8); 832 return md5Input; 833 } 834 835 /** {@inheritDoc} */ getPDFVersion()836 public Version getPDFVersion() { 837 return pdfVersion; 838 } 839 840 } 841