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 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  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.security.InvalidKeyException;
  22. import java.security.MessageDigest;
  23. import java.security.NoSuchAlgorithmException;
  24. import java.util.Arrays;
  25. import javax.crypto.BadPaddingException;
  26. import javax.crypto.Cipher;
  27. import javax.crypto.CipherOutputStream;
  28. import javax.crypto.IllegalBlockSizeException;
  29. import javax.crypto.NoSuchPaddingException;
  30. import javax.crypto.spec.SecretKeySpec;
  31. /**
  32. * An implementation of the Standard Security Handler.
  33. */
  34. public final class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
  35. private final MessageDigest digest;
  36. private byte[] encryptionKey;
  37. private String encryptionDictionary;
  38. private class EncryptionInitializer {
  39. private final PDFEncryptionParams encryptionParams;
  40. private int encryptionLength;
  41. private int version;
  42. private int revision;
  43. EncryptionInitializer(PDFEncryptionParams params) {
  44. this.encryptionParams = new PDFEncryptionParams(params);
  45. }
  46. void init() {
  47. encryptionLength = encryptionParams.getEncryptionLengthInBits();
  48. determineEncryptionAlgorithm();
  49. int permissions = Permission.computePermissions(encryptionParams);
  50. EncryptionSettings encryptionSettings = new EncryptionSettings(
  51. encryptionLength, permissions,
  52. encryptionParams.getUserPassword(), encryptionParams.getOwnerPassword());
  53. InitializationEngine initializationEngine = (revision == 2)
  54. ? new Rev2Engine(encryptionSettings)
  55. : new Rev3Engine(encryptionSettings);
  56. initializationEngine.run();
  57. encryptionDictionary = createEncryptionDictionary(permissions,
  58. initializationEngine.oValue,
  59. initializationEngine.uValue);
  60. }
  61. private void determineEncryptionAlgorithm() {
  62. if (isVersion1Revision2Algorithm()) {
  63. version = 1;
  64. revision = 2;
  65. } else {
  66. version = 2;
  67. revision = 3;
  68. }
  69. }
  70. private boolean isVersion1Revision2Algorithm() {
  71. return encryptionLength == 40
  72. && encryptionParams.isAllowFillInForms()
  73. && encryptionParams.isAllowAccessContent()
  74. && encryptionParams.isAllowAssembleDocument()
  75. && encryptionParams.isAllowPrintHq();
  76. }
  77. private String createEncryptionDictionary(final int permissions, final byte[] oValue,
  78. final byte[] uValue) {
  79. return "<< /Filter /Standard\n"
  80. + "/V " + version + "\n"
  81. + "/R " + revision + "\n"
  82. + "/Length " + encryptionLength + "\n"
  83. + "/P " + permissions + "\n"
  84. + "/O " + PDFText.toHex(oValue) + "\n"
  85. + "/U " + PDFText.toHex(uValue) + "\n"
  86. + ">>";
  87. }
  88. }
  89. private static enum Permission {
  90. PRINT(3),
  91. EDIT_CONTENT(4),
  92. COPY_CONTENT(5),
  93. EDIT_ANNOTATIONS(6),
  94. FILL_IN_FORMS(9),
  95. ACCESS_CONTENT(10),
  96. ASSEMBLE_DOCUMENT(11),
  97. PRINT_HQ(12);
  98. private final int mask;
  99. /**
  100. * Creates a new permission.
  101. *
  102. * @param bit bit position for this permission, 1-based to match the PDF Reference
  103. */
  104. private Permission(int bit) {
  105. mask = 1 << (bit - 1);
  106. }
  107. private int removeFrom(int permissions) {
  108. return permissions - mask;
  109. }
  110. static int computePermissions(PDFEncryptionParams encryptionParams) {
  111. int permissions = -4;
  112. if (!encryptionParams.isAllowPrint()) {
  113. permissions = PRINT.removeFrom(permissions);
  114. }
  115. if (!encryptionParams.isAllowCopyContent()) {
  116. permissions = COPY_CONTENT.removeFrom(permissions);
  117. }
  118. if (!encryptionParams.isAllowEditContent()) {
  119. permissions = EDIT_CONTENT.removeFrom(permissions);
  120. }
  121. if (!encryptionParams.isAllowEditAnnotations()) {
  122. permissions = EDIT_ANNOTATIONS.removeFrom(permissions);
  123. }
  124. if (!encryptionParams.isAllowFillInForms()) {
  125. permissions = FILL_IN_FORMS.removeFrom(permissions);
  126. }
  127. if (!encryptionParams.isAllowAccessContent()) {
  128. permissions = ACCESS_CONTENT.removeFrom(permissions);
  129. }
  130. if (!encryptionParams.isAllowAssembleDocument()) {
  131. permissions = ASSEMBLE_DOCUMENT.removeFrom(permissions);
  132. }
  133. if (!encryptionParams.isAllowPrintHq()) {
  134. permissions = PRINT_HQ.removeFrom(permissions);
  135. }
  136. return permissions;
  137. }
  138. }
  139. private static final class EncryptionSettings {
  140. final int encryptionLength; // CSOK: VisibilityModifier
  141. final int permissions; // CSOK: VisibilityModifier
  142. final String userPassword; // CSOK: VisibilityModifier
  143. final String ownerPassword; // CSOK: VisibilityModifier
  144. EncryptionSettings(int encryptionLength, int permissions,
  145. String userPassword, String ownerPassword) {
  146. this.encryptionLength = encryptionLength;
  147. this.permissions = permissions;
  148. this.userPassword = userPassword;
  149. this.ownerPassword = ownerPassword;
  150. }
  151. }
  152. private abstract class InitializationEngine {
  153. /** Padding for passwords. */
  154. protected final byte[] padding = new byte[] {
  155. (byte) 0x28, (byte) 0xBF, (byte) 0x4E, (byte) 0x5E,
  156. (byte) 0x4E, (byte) 0x75, (byte) 0x8A, (byte) 0x41,
  157. (byte) 0x64, (byte) 0x00, (byte) 0x4E, (byte) 0x56,
  158. (byte) 0xFF, (byte) 0xFA, (byte) 0x01, (byte) 0x08,
  159. (byte) 0x2E, (byte) 0x2E, (byte) 0x00, (byte) 0xB6,
  160. (byte) 0xD0, (byte) 0x68, (byte) 0x3E, (byte) 0x80,
  161. (byte) 0x2F, (byte) 0x0C, (byte) 0xA9, (byte) 0xFE,
  162. (byte) 0x64, (byte) 0x53, (byte) 0x69, (byte) 0x7A};
  163. protected final int encryptionLengthInBytes;
  164. private final int permissions;
  165. private byte[] oValue;
  166. private byte[] uValue;
  167. private final byte[] preparedUserPassword;
  168. protected final String ownerPassword;
  169. InitializationEngine(EncryptionSettings encryptionSettings) {
  170. this.encryptionLengthInBytes = encryptionSettings.encryptionLength / 8;
  171. this.permissions = encryptionSettings.permissions;
  172. this.preparedUserPassword = preparePassword(encryptionSettings.userPassword);
  173. this.ownerPassword = encryptionSettings.ownerPassword;
  174. }
  175. void run() {
  176. oValue = computeOValue();
  177. createEncryptionKey();
  178. uValue = computeUValue();
  179. }
  180. /**
  181. * Applies Algorithm 3.3 Page 79 of the PDF 1.4 Reference.
  182. *
  183. * @return the O value
  184. */
  185. private byte[] computeOValue() {
  186. // Step 1
  187. byte[] md5Input = prepareMD5Input();
  188. // Step 2
  189. digest.reset();
  190. byte[] hash = digest.digest(md5Input);
  191. // Step 3
  192. hash = computeOValueStep3(hash);
  193. // Step 4
  194. byte[] key = new byte[encryptionLengthInBytes];
  195. System.arraycopy(hash, 0, key, 0, encryptionLengthInBytes);
  196. // Steps 5, 6
  197. byte[] encryptionResult = encryptWithKey(key, preparedUserPassword);
  198. // Step 7
  199. encryptionResult = computeOValueStep7(key, encryptionResult);
  200. // Step 8
  201. return encryptionResult;
  202. }
  203. /**
  204. * Applies Algorithm 3.2 Page 78 of the PDF 1.4 Reference.
  205. */
  206. private void createEncryptionKey() {
  207. // Steps 1, 2
  208. digest.reset();
  209. digest.update(preparedUserPassword);
  210. // Step 3
  211. digest.update(oValue);
  212. // Step 4
  213. digest.update((byte) (permissions >>> 0));
  214. digest.update((byte) (permissions >>> 8));
  215. digest.update((byte) (permissions >>> 16));
  216. digest.update((byte) (permissions >>> 24));
  217. // Step 5
  218. digest.update(getDocumentSafely().getFileIDGenerator().getOriginalFileID());
  219. byte[] hash = digest.digest();
  220. // Step 6
  221. hash = createEncryptionKeyStep6(hash);
  222. // Step 7
  223. encryptionKey = new byte[encryptionLengthInBytes];
  224. System.arraycopy(hash, 0, encryptionKey, 0, encryptionLengthInBytes);
  225. }
  226. protected abstract byte[] computeUValue();
  227. /**
  228. * Adds padding to the password as directed in page 78 of the PDF 1.4 Reference.
  229. *
  230. * @param password the password
  231. * @return the password with additional padding if necessary
  232. */
  233. private byte[] preparePassword(String password) {
  234. int finalLength = 32;
  235. byte[] preparedPassword = new byte[finalLength];
  236. byte[] passwordBytes = password.getBytes();
  237. System.arraycopy(passwordBytes, 0, preparedPassword, 0, passwordBytes.length);
  238. System.arraycopy(padding, 0, preparedPassword, passwordBytes.length,
  239. finalLength - passwordBytes.length);
  240. return preparedPassword;
  241. }
  242. private byte[] prepareMD5Input() {
  243. if (ownerPassword.length() != 0) {
  244. return preparePassword(ownerPassword);
  245. } else {
  246. return preparedUserPassword;
  247. }
  248. }
  249. protected abstract byte[] computeOValueStep3(byte[] hash);
  250. protected abstract byte[] computeOValueStep7(byte[] key, byte[] encryptionResult);
  251. protected abstract byte[] createEncryptionKeyStep6(byte[] hash);
  252. }
  253. private class Rev2Engine extends InitializationEngine {
  254. Rev2Engine(EncryptionSettings encryptionSettings) {
  255. super(encryptionSettings);
  256. }
  257. @Override
  258. protected byte[] computeOValueStep3(byte[] hash) {
  259. return hash;
  260. }
  261. @Override
  262. protected byte[] computeOValueStep7(byte[] key, byte[] encryptionResult) {
  263. return encryptionResult;
  264. }
  265. @Override
  266. protected byte[] createEncryptionKeyStep6(byte[] hash) {
  267. return hash;
  268. }
  269. @Override
  270. protected byte[] computeUValue() {
  271. return encryptWithKey(encryptionKey, padding);
  272. }
  273. }
  274. private class Rev3Engine extends InitializationEngine {
  275. Rev3Engine(EncryptionSettings encryptionSettings) {
  276. super(encryptionSettings);
  277. }
  278. @Override
  279. protected byte[] computeOValueStep3(byte[] hash) {
  280. for (int i = 0; i < 50; i++) {
  281. hash = digest.digest(hash);
  282. }
  283. return hash;
  284. }
  285. @Override
  286. protected byte[] computeOValueStep7(byte[] key, byte[] encryptionResult) {
  287. return xorKeyAndEncrypt19Times(key, encryptionResult);
  288. }
  289. @Override
  290. protected byte[] createEncryptionKeyStep6(byte[] hash) {
  291. for (int i = 0; i < 50; i++) {
  292. digest.update(hash, 0, encryptionLengthInBytes);
  293. hash = digest.digest();
  294. }
  295. return hash;
  296. }
  297. @Override
  298. protected byte[] computeUValue() {
  299. // Step 1 is encryptionKey
  300. // Step 2
  301. digest.reset();
  302. digest.update(padding);
  303. // Step 3
  304. digest.update(getDocumentSafely().getFileIDGenerator().getOriginalFileID());
  305. // Step 4
  306. byte[] encryptionResult = encryptWithKey(encryptionKey, digest.digest());
  307. // Step 5
  308. encryptionResult = xorKeyAndEncrypt19Times(encryptionKey, encryptionResult);
  309. // Step 6
  310. byte[] uValue = new byte[32];
  311. System.arraycopy(encryptionResult, 0, uValue, 0, 16);
  312. // Add the arbitrary padding
  313. Arrays.fill(uValue, 16, 32, (byte) 0);
  314. return uValue;
  315. }
  316. private byte[] xorKeyAndEncrypt19Times(byte[] key, byte[] input) {
  317. byte[] result = input;
  318. byte[] encryptionKey = new byte[key.length];
  319. for (int i = 1; i <= 19; i++) {
  320. for (int j = 0; j < key.length; j++) {
  321. encryptionKey[j] = (byte) (key[j] ^ i);
  322. }
  323. result = encryptWithKey(encryptionKey, result);
  324. }
  325. return result;
  326. }
  327. }
  328. private class EncryptionFilter extends PDFFilter {
  329. private int streamNumber;
  330. private int streamGeneration;
  331. EncryptionFilter(int streamNumber, int streamGeneration) {
  332. this.streamNumber = streamNumber;
  333. this.streamGeneration = streamGeneration;
  334. }
  335. /**
  336. * Returns a PDF string representation of this filter.
  337. *
  338. * @return the empty string
  339. */
  340. public String getName() {
  341. return "";
  342. }
  343. /**
  344. * Returns a parameter dictionary for this filter.
  345. *
  346. * @return null, this filter has no parameters
  347. */
  348. public PDFObject getDecodeParms() {
  349. return null;
  350. }
  351. /** {@inheritDoc} */
  352. public OutputStream applyFilter(OutputStream out) throws IOException {
  353. byte[] key = createEncryptionKey(streamNumber, streamGeneration);
  354. Cipher cipher = initCipher(key);
  355. return new CipherOutputStream(out, cipher);
  356. }
  357. }
  358. private PDFEncryptionJCE(int objectNumber, PDFEncryptionParams params, PDFDocument pdf) {
  359. setObjectNumber(objectNumber);
  360. try {
  361. digest = MessageDigest.getInstance("MD5");
  362. } catch (NoSuchAlgorithmException e) {
  363. throw new UnsupportedOperationException(e.getMessage());
  364. }
  365. setDocument(pdf);
  366. EncryptionInitializer encryptionInitializer = new EncryptionInitializer(params);
  367. encryptionInitializer.init();
  368. }
  369. /**
  370. * Creates and returns an encryption object.
  371. *
  372. * @param objectNumber the object number for the encryption dictionary
  373. * @param params the encryption parameters
  374. * @param pdf the PDF document to be encrypted
  375. * @return the newly created encryption object
  376. */
  377. public static PDFEncryption make(
  378. int objectNumber, PDFEncryptionParams params, PDFDocument pdf) {
  379. return new PDFEncryptionJCE(objectNumber, params, pdf);
  380. }
  381. /** {@inheritDoc} */
  382. public byte[] encrypt(byte[] data, PDFObject refObj) {
  383. PDFObject o = refObj;
  384. while (o != null && !o.hasObjectNumber()) {
  385. o = o.getParent();
  386. }
  387. if (o == null) {
  388. throw new IllegalStateException("No object number could be obtained for a PDF object");
  389. }
  390. byte[] key = createEncryptionKey(o.getObjectNumber(), o.getGeneration());
  391. return encryptWithKey(key, data);
  392. }
  393. /** {@inheritDoc} */
  394. public void applyFilter(AbstractPDFStream stream) {
  395. stream.getFilterList().addFilter(
  396. new EncryptionFilter(stream.getObjectNumber(), stream.getGeneration()));
  397. }
  398. /**
  399. * Prepares the encryption dictionary for output to a PDF file.
  400. *
  401. * @return the encryption dictionary as a byte array
  402. */
  403. public byte[] toPDF() {
  404. assert encryptionDictionary != null;
  405. return encode(this.encryptionDictionary);
  406. }
  407. /** {@inheritDoc} */
  408. public String getTrailerEntry() {
  409. return "/Encrypt " + getObjectNumber() + " " + getGeneration() + " R\n";
  410. }
  411. private static byte[] encryptWithKey(byte[] key, byte[] data) {
  412. try {
  413. final Cipher c = initCipher(key);
  414. return c.doFinal(data);
  415. } catch (IllegalBlockSizeException e) {
  416. throw new IllegalStateException(e.getMessage());
  417. } catch (BadPaddingException e) {
  418. throw new IllegalStateException(e.getMessage());
  419. }
  420. }
  421. private static Cipher initCipher(byte[] key) {
  422. try {
  423. Cipher c = Cipher.getInstance("RC4");
  424. SecretKeySpec keyspec = new SecretKeySpec(key, "RC4");
  425. c.init(Cipher.ENCRYPT_MODE, keyspec);
  426. return c;
  427. } catch (InvalidKeyException e) {
  428. throw new IllegalStateException(e);
  429. } catch (NoSuchAlgorithmException e) {
  430. throw new UnsupportedOperationException(e);
  431. } catch (NoSuchPaddingException e) {
  432. throw new UnsupportedOperationException(e);
  433. }
  434. }
  435. /**
  436. * Applies Algorithm 3.1 from the PDF 1.4 Reference.
  437. *
  438. * @param objectNumber the object number
  439. * @param generationNumber the generation number
  440. * @return the key to use for encryption
  441. */
  442. private byte[] createEncryptionKey(int objectNumber, int generationNumber) {
  443. // Step 1 passed in
  444. // Step 2
  445. byte[] md5Input = prepareMD5Input(objectNumber, generationNumber);
  446. // Step 3
  447. digest.reset();
  448. byte[] hash = digest.digest(md5Input);
  449. // Step 4
  450. int keyLength = Math.min(16, md5Input.length);
  451. byte[] key = new byte[keyLength];
  452. System.arraycopy(hash, 0, key, 0, keyLength);
  453. return key;
  454. }
  455. private byte[] prepareMD5Input(int objectNumber, int generationNumber) {
  456. byte[] md5Input = new byte[encryptionKey.length + 5];
  457. System.arraycopy(encryptionKey, 0, md5Input, 0, encryptionKey.length);
  458. int i = encryptionKey.length;
  459. md5Input[i++] = (byte) (objectNumber >>> 0);
  460. md5Input[i++] = (byte) (objectNumber >>> 8);
  461. md5Input[i++] = (byte) (objectNumber >>> 16);
  462. md5Input[i++] = (byte) (generationNumber >>> 0);
  463. md5Input[i++] = (byte) (generationNumber >>> 8);
  464. return md5Input;
  465. }
  466. }