Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

PDFEncryptionJCE.java 19KB

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