You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PDFEncryptionJCE.java 31KB

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