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.

PDFEncryptionJCETestCase.java 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  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.ByteArrayOutputStream;
  20. import java.io.IOException;
  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.util.Arrays;
  27. import java.util.regex.Matcher;
  28. import java.util.regex.Pattern;
  29. import javax.crypto.BadPaddingException;
  30. import javax.crypto.Cipher;
  31. import javax.crypto.IllegalBlockSizeException;
  32. import javax.crypto.NoSuchPaddingException;
  33. import javax.crypto.spec.IvParameterSpec;
  34. import javax.crypto.spec.SecretKeySpec;
  35. import org.junit.Test;
  36. import static org.junit.Assert.assertArrayEquals;
  37. import static org.junit.Assert.assertEquals;
  38. import static org.junit.Assert.assertTrue;
  39. /**
  40. * Tests the {@link PDFEncryptionJCE} class.
  41. */
  42. public class PDFEncryptionJCETestCase {
  43. private EncryptionTest test;
  44. private PDFEncryptionJCE encryptionObject;
  45. private static final class EncryptionTest {
  46. private int objectNumber = 1;
  47. private final PDFEncryptionParams encryptionParameters = new PDFEncryptionParams();
  48. private byte[] data;
  49. private byte[] encryptedData;
  50. private final EncryptionDictionaryTester encryptionDictionaryTester;
  51. EncryptionTest() {
  52. this(new EncryptionDictionaryTester());
  53. }
  54. EncryptionTest(EncryptionDictionaryTester encryptionDictionaryTester) {
  55. encryptionParameters.setUserPassword("TestUserPassword");
  56. encryptionParameters.setOwnerPassword("TestOwnerPassword");
  57. setData(0x00, 0xAA, 0xFF, 0x55, 0xCC, 0x33, 0xF0);
  58. this.encryptionDictionaryTester = encryptionDictionaryTester;
  59. this.encryptionDictionaryTester.setLength(
  60. encryptionParameters.getEncryptionLengthInBits());
  61. }
  62. int getObjectNumber() {
  63. return objectNumber;
  64. }
  65. EncryptionTest setObjectNumber(int objectNumber) {
  66. this.objectNumber = objectNumber;
  67. return this;
  68. }
  69. byte[] getData() {
  70. return data;
  71. }
  72. EncryptionTest setData(int... data) {
  73. /*
  74. * Use an array of int to avoid having to cast some elements to byte in the
  75. * method call.
  76. */
  77. this.data = convertIntArrayToByteArray(data);
  78. return this;
  79. }
  80. byte[] getEncryptedData() {
  81. return encryptedData;
  82. }
  83. EncryptionTest setEncryptedData(int... encryptedData) {
  84. this.encryptedData = convertIntArrayToByteArray(encryptedData);
  85. return this;
  86. }
  87. private byte[] convertIntArrayToByteArray(int[] intArray) {
  88. byte[] byteArray = new byte[intArray.length];
  89. for (int i = 0; i < intArray.length; i++) {
  90. byteArray[i] = (byte) intArray[i];
  91. }
  92. return byteArray;
  93. }
  94. PDFEncryptionParams getEncryptionParameters() {
  95. return encryptionParameters;
  96. }
  97. EncryptionTest setUserPassword(String userPassword) {
  98. encryptionParameters.setUserPassword(userPassword);
  99. return this;
  100. }
  101. EncryptionTest setOwnerPassword(String ownerPassword) {
  102. encryptionParameters.setOwnerPassword(ownerPassword);
  103. return this;
  104. }
  105. EncryptionTest setEncryptionLength(int encryptionLength) {
  106. encryptionParameters.setEncryptionLengthInBits(encryptionLength);
  107. encryptionDictionaryTester.setLength(encryptionLength);
  108. return this;
  109. }
  110. EncryptionTest disablePrint() {
  111. encryptionParameters.setAllowPrint(false);
  112. return this;
  113. }
  114. EncryptionTest disableEditContent() {
  115. encryptionParameters.setAllowEditContent(false);
  116. return this;
  117. }
  118. EncryptionTest disableCopyContent() {
  119. encryptionParameters.setAllowCopyContent(false);
  120. return this;
  121. }
  122. EncryptionTest disableEditAnnotations() {
  123. encryptionParameters.setAllowEditAnnotations(false);
  124. return this;
  125. }
  126. EncryptionTest disableFillInForms() {
  127. encryptionParameters.setAllowFillInForms(false);
  128. return this;
  129. }
  130. EncryptionTest disableAccessContent() {
  131. encryptionParameters.setAllowAccessContent(false);
  132. return this;
  133. }
  134. EncryptionTest disableAssembleDocument() {
  135. encryptionParameters.setAllowAssembleDocument(false);
  136. return this;
  137. }
  138. EncryptionTest disablePrintHq() {
  139. encryptionParameters.setAllowPrintHq(false);
  140. return this;
  141. }
  142. void testEncryptionDictionary(PDFEncryptionJCE encryptionObject) {
  143. encryptionDictionaryTester.test(encryptionObject);
  144. }
  145. }
  146. private static final class EncryptionDictionaryTester {
  147. private int version = 2;
  148. private int revision = 3;
  149. private int length = 128;
  150. private int permissions = -4;
  151. private String ownerEntry = "D9A98017F0500EF9B69738641C9B4CBA1229EDC3F2151BC6C9C4FB07B1CB315E";
  152. private String userEntry = "D3EF424BFEA2E434000E1A74941CC87300000000000000000000000000000000";
  153. EncryptionDictionaryTester setVersion(int version) {
  154. this.version = version;
  155. return this;
  156. }
  157. EncryptionDictionaryTester setRevision(int revision) {
  158. this.revision = revision;
  159. return this;
  160. }
  161. EncryptionDictionaryTester setLength(int length) {
  162. this.length = length;
  163. return this;
  164. }
  165. EncryptionDictionaryTester setPermissions(int permissions) {
  166. this.permissions = permissions;
  167. return this;
  168. }
  169. EncryptionDictionaryTester setOwnerEntry(String ownerEntry) {
  170. this.ownerEntry = ownerEntry;
  171. return this;
  172. }
  173. EncryptionDictionaryTester setUserEntry(String userEntry) {
  174. this.userEntry = userEntry;
  175. return this;
  176. }
  177. void test(PDFEncryptionJCE encryptionObject) {
  178. byte[] encryptionDictionary = encryptionObject.toPDF();
  179. RegexTestedCharSequence dictionary = new RegexTestedCharSequence(encryptionDictionary);
  180. final String whitespace = "\\s+";
  181. final String digits = "\\d+";
  182. final String hexDigits = "\\p{XDigit}+";
  183. dictionary.mustContain("/Filter" + whitespace + "/Standard\\b");
  184. dictionary.mustContain("/V" + whitespace + "(" + digits + ")")
  185. .withGroup1EqualTo(Integer.toString(version));
  186. dictionary.mustContain("/R" + whitespace + "(" + digits + ")")
  187. .withGroup1EqualTo(Integer.toString(revision));
  188. dictionary.mustContain("/Length" + whitespace + "(" + digits + ")")
  189. .withGroup1EqualTo(Integer.toString(length));
  190. dictionary.mustContain("/P" + whitespace + "(-?" + digits + ")")
  191. .withGroup1EqualTo(Integer.toString(permissions));
  192. dictionary.mustContain("/O" + whitespace + "<(" + hexDigits + ")>")
  193. .withGroup1EqualTo(ownerEntry);
  194. dictionary.mustContain("/U" + whitespace + "<(" + hexDigits + ")>")
  195. .withGroup1EqualTo(userEntry);
  196. }
  197. }
  198. private static final class RegexTestedCharSequence {
  199. private final String string;
  200. private Matcher matcher;
  201. RegexTestedCharSequence(byte[] bytes) {
  202. try {
  203. string = new String(bytes, "US-ASCII");
  204. } catch (UnsupportedEncodingException e) {
  205. throw new RuntimeException(e);
  206. }
  207. }
  208. RegexTestedCharSequence mustContain(String regex) {
  209. Pattern pattern = Pattern.compile(regex);
  210. matcher = pattern.matcher(string);
  211. assertTrue(matcher.find());
  212. return this;
  213. }
  214. RegexTestedCharSequence withGroup1EqualTo(String expected) {
  215. assertEquals(expected, matcher.group(1));
  216. return this;
  217. }
  218. }
  219. @Test
  220. public final void testMake() {
  221. PDFEncryption testEncryptionObj = createEncryptionObject(new PDFEncryptionParams());
  222. assertTrue(testEncryptionObj instanceof PDFEncryptionJCE);
  223. assertEquals(1, ((PDFEncryptionJCE) testEncryptionObj).getObjectNumber().getNumber());
  224. }
  225. @Test
  226. public void testBasic() throws IOException {
  227. test = new EncryptionTest();
  228. test.setData(0x00).setEncryptedData(0x24);
  229. runEncryptionTests();
  230. test.setData(0xAA).setEncryptedData(0x8E);
  231. runEncryptionTests();
  232. test.setData(0xFF).setEncryptedData(0xDB);
  233. runEncryptionTests();
  234. test = new EncryptionTest().setEncryptedData(0x24, 0x07, 0x85, 0xF7, 0x87, 0x31, 0x90);
  235. runEncryptionTests();
  236. }
  237. @Test
  238. public void test128bit() throws IOException {
  239. EncryptionDictionaryTester encryptionDictionaryTester = new EncryptionDictionaryTester()
  240. .setVersion(2)
  241. .setRevision(3)
  242. .setPermissions(-4)
  243. .setOwnerEntry("D9A98017F0500EF9B69738641C9B4CBA1229EDC3F2151BC6C9C4FB07B1CB315E")
  244. .setUserEntry("D3EF424BFEA2E434000E1A74941CC87300000000000000000000000000000000");
  245. test = new EncryptionTest(encryptionDictionaryTester)
  246. .setObjectNumber(2)
  247. .setEncryptionLength(128)
  248. .setEncryptedData(0xE3, 0xCB, 0xB2, 0x55, 0xD9, 0x26, 0x55);
  249. runEncryptionTests();
  250. }
  251. @Test
  252. public void testDisableRev2Permissions() throws IOException {
  253. EncryptionDictionaryTester encryptionDictionaryTester = new EncryptionDictionaryTester()
  254. .setVersion(1)
  255. .setRevision(2)
  256. .setLength(40)
  257. .setPermissions(-64)
  258. .setOwnerEntry("3EE8C4000CA44B2645EED029C9EA7D4FC63C6D9B89349E8FA5A40C7691AB96B5")
  259. .setUserEntry("3E65D0090746C4C37C5EF23C1BDB6323E00C24C4B2D744DD3BFB654CD58591A1");
  260. test = new EncryptionTest(encryptionDictionaryTester).setObjectNumber(3).setEncryptionLength(40)
  261. .disablePrint().disableEditContent().disableCopyContent().disableEditAnnotations()
  262. .setEncryptedData(0x66, 0xEE, 0xA7, 0x93, 0xC4, 0xB1, 0xB4);
  263. runEncryptionTests();
  264. }
  265. @Test
  266. public void testDisableRev3Permissions() throws IOException {
  267. EncryptionDictionaryTester encryptionDictionaryTester = new EncryptionDictionaryTester()
  268. .setVersion(2)
  269. .setRevision(3)
  270. .setLength(40)
  271. .setPermissions(-3844)
  272. .setOwnerEntry("8D4BCA4F4AB2BAB4E38F161D61F937EC50BE5EB30C2DC05EA409D252CD695E55")
  273. .setUserEntry("0F01171E22C7FB27B079C132BA4277DE00000000000000000000000000000000");
  274. test = new EncryptionTest(encryptionDictionaryTester)
  275. .setObjectNumber(4)
  276. .setEncryptionLength(40)
  277. .disableFillInForms()
  278. .disableAccessContent()
  279. .disableAssembleDocument()
  280. .disablePrintHq()
  281. .setEncryptedData(0x8E, 0x3C, 0xD2, 0x05, 0x50, 0x48, 0x82);
  282. runEncryptionTests();
  283. }
  284. @Test
  285. public void test128bitDisableSomePermissions() throws IOException {
  286. EncryptionDictionaryTester encryptionDictionaryTester = new EncryptionDictionaryTester()
  287. .setVersion(2)
  288. .setRevision(3)
  289. .setPermissions(-1304)
  290. .setOwnerEntry("D9A98017F0500EF9B69738641C9B4CBA1229EDC3F2151BC6C9C4FB07B1CB315E")
  291. .setUserEntry("62F0E4D8641D482E0F8E71A89270045A00000000000000000000000000000000");
  292. test = new EncryptionTest(encryptionDictionaryTester)
  293. .setObjectNumber(5)
  294. .setEncryptionLength(128)
  295. .disablePrint()
  296. .disableCopyContent()
  297. .disableFillInForms()
  298. .disableAssembleDocument()
  299. .setEncryptedData(0xF7, 0x85, 0x4F, 0xB0, 0x50, 0x5C, 0xDF);
  300. runEncryptionTests();
  301. }
  302. @Test
  303. public void testDifferentPasswords() throws IOException {
  304. EncryptionDictionaryTester encryptionDictionaryTester = new EncryptionDictionaryTester()
  305. .setRevision(2)
  306. .setVersion(1)
  307. .setLength(40)
  308. .setOwnerEntry("D11C233C65E9DC872E858ABBD8B62198771167ADCE7AB8DC7AE0A1A7E21A1E25")
  309. .setUserEntry("6F449167DB8DDF0D2DF4602DDBBA97ABF9A9101F632CC16AB0BE74EB9500B469");
  310. test = new EncryptionTest(encryptionDictionaryTester)
  311. .setObjectNumber(6)
  312. .setEncryptionLength(40)
  313. .setUserPassword("ADifferentUserPassword")
  314. .setOwnerPassword("ADifferentOwnerPassword")
  315. .setEncryptedData(0x27, 0xAC, 0xB1, 0x6C, 0x42, 0xE0, 0xA8);
  316. runEncryptionTests();
  317. }
  318. @Test
  319. public void testNoOwnerPassword() throws IOException {
  320. EncryptionDictionaryTester encryptionDictionaryTester = new EncryptionDictionaryTester()
  321. .setRevision(2)
  322. .setVersion(1)
  323. .setLength(40)
  324. .setOwnerEntry("5163AAF3EE74C76D7C223593A84C8702FEA8AA4493E4933FF5B5A5BBB20AE4BB")
  325. .setUserEntry("42DDF1C1BF3AB04786D5038E7B0A723AE614D944E1DE91A922FC54F5F2345E00");
  326. test = new EncryptionTest(encryptionDictionaryTester)
  327. .setObjectNumber(7)
  328. .setEncryptionLength(40)
  329. .setUserPassword("ADifferentUserPassword")
  330. .setOwnerPassword("")
  331. .setEncryptedData(0xEC, 0x2E, 0x5D, 0xC2, 0x7F, 0xAD, 0x58);
  332. runEncryptionTests();
  333. }
  334. @Test
  335. public void test128bitDisableSomePermissionsDifferentPasswords() throws IOException {
  336. EncryptionDictionaryTester encryptionDictionaryTester = new EncryptionDictionaryTester()
  337. .setVersion(2)
  338. .setRevision(3)
  339. .setPermissions(-2604)
  340. .setOwnerEntry("F83CA049FAA2F774F8541F25E746A92EE2A7F060C46C91C693E673BF18FF7B36")
  341. .setUserEntry("88A4C58F5385B5F08FACA0636D790EDF00000000000000000000000000000000");
  342. test = new EncryptionTest(encryptionDictionaryTester)
  343. .setObjectNumber(8)
  344. .setUserPassword("ADifferentUserPassword")
  345. .setOwnerPassword("ADifferentOwnerPassword")
  346. .setEncryptionLength(128)
  347. .disableEditContent()
  348. .disableEditAnnotations()
  349. .disableAccessContent()
  350. .disablePrintHq()
  351. .setEncryptedData(0x77, 0x54, 0x67, 0xA5, 0xCC, 0x73, 0xDE);
  352. runEncryptionTests();
  353. }
  354. @Test
  355. public void test128bitNoPermissionNoOwnerPassword() throws IOException {
  356. EncryptionDictionaryTester encryptionDictionaryTester = new EncryptionDictionaryTester()
  357. .setVersion(2)
  358. .setRevision(3)
  359. .setPermissions(-3904)
  360. .setOwnerEntry("3EEB3FA5594CBD935BFB2F83FB184DD41FBCD7C36A04F1FFD0899B0DFFCFF96B")
  361. .setUserEntry("D972B72DD2633F613B0DDB7511C719C500000000000000000000000000000000");
  362. test = new EncryptionTest(encryptionDictionaryTester)
  363. .setObjectNumber(9)
  364. .setUserPassword("ADifferentUserPassword")
  365. .setOwnerPassword("")
  366. .setEncryptionLength(128)
  367. .disablePrint()
  368. .disableEditContent()
  369. .disableCopyContent()
  370. .disableEditAnnotations()
  371. .disableFillInForms()
  372. .disableAccessContent()
  373. .disableAssembleDocument()
  374. .disablePrintHq()
  375. .setEncryptedData(0x0C, 0xAD, 0x49, 0xC7, 0xE5, 0x05, 0xB8);
  376. runEncryptionTests();
  377. }
  378. @Test
  379. public void testAES256() throws UnsupportedEncodingException, NoSuchAlgorithmException,
  380. NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException,
  381. IllegalBlockSizeException, BadPaddingException {
  382. MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
  383. String dataText = "Test data to encrypt.";
  384. byte[] data = dataText.getBytes("UTF-8");
  385. PDFEncryptionParams params = new PDFEncryptionParams();
  386. params.setEncryptionLengthInBits(256);
  387. params.setUserPassword("userpassword");
  388. params.setOwnerPassword("ownerpassword");
  389. PDFEncryptionJCE encryption = createEncryptionObject(params);
  390. PDFText text = new PDFText();
  391. text.setObjectNumber(1); // obj number not used with AES 256, can be anything
  392. String dictionary = new String(encryption.toPDF());
  393. byte[] encrypted = encryption.encrypt(data, text);
  394. byte[] u = parseHexStringEntries(dictionary, "U");
  395. byte[] o = parseHexStringEntries(dictionary, "O");
  396. byte[] ue = parseHexStringEntries(dictionary, "UE");
  397. byte[] oe = parseHexStringEntries(dictionary, "OE");
  398. byte[] perms = parseHexStringEntries(dictionary, "Perms");
  399. // check byte arrays lengths
  400. assertEquals(48, u.length);
  401. assertEquals(48, o.length);
  402. assertEquals(32, ue.length);
  403. assertEquals(32, oe.length);
  404. assertEquals(16, perms.length);
  405. // check user password is valid
  406. byte[] userValSalt = new byte[8];
  407. byte[] userKeySalt = new byte[8];
  408. System.arraycopy(u, 32, userValSalt, 0, 8);
  409. System.arraycopy(u, 40, userKeySalt, 0, 8);
  410. byte[] uPassBytes = params.getUserPassword().getBytes("UTF-8");
  411. byte[] testUPass = new byte[uPassBytes.length + 8];
  412. System.arraycopy(uPassBytes, 0, testUPass, 0, uPassBytes.length);
  413. System.arraycopy(userValSalt, 0, testUPass, uPassBytes.length, 8);
  414. sha256.reset();
  415. sha256.update(testUPass);
  416. byte[] actualUPass = sha256.digest();
  417. byte[] expectedUPass = new byte[32];
  418. System.arraycopy(u, 0, expectedUPass, 0, 32);
  419. assertArrayEquals(expectedUPass, actualUPass);
  420. // check owner password is valid
  421. byte[] ownerValSalt = new byte[8];
  422. byte[] ownerKeySalt = new byte[8];
  423. System.arraycopy(o, 32, ownerValSalt, 0, 8);
  424. System.arraycopy(o, 40, ownerKeySalt, 0, 8);
  425. byte[] oPassBytes = params.getOwnerPassword().getBytes("UTF-8");
  426. byte[] testOPass = new byte[oPassBytes.length + 8 + 48];
  427. System.arraycopy(oPassBytes, 0, testOPass, 0, oPassBytes.length);
  428. System.arraycopy(ownerValSalt, 0, testOPass, oPassBytes.length, 8);
  429. System.arraycopy(u, 0, testOPass, oPassBytes.length + 8, 48);
  430. sha256.reset();
  431. sha256.update(testOPass);
  432. byte[] actualOPass = sha256.digest();
  433. byte[] expectedOPass = new byte[32];
  434. System.arraycopy(o, 0, expectedOPass, 0, 32);
  435. assertArrayEquals(expectedOPass, actualOPass);
  436. // compute encryption key from ue
  437. byte[] ivZero = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
  438. IvParameterSpec ivspecZero = new IvParameterSpec(ivZero);
  439. Cipher cipherNoPadding = Cipher.getInstance("AES/CBC/NoPadding");
  440. byte[] tmpUKey = new byte[uPassBytes.length + 8];
  441. System.arraycopy(uPassBytes, 0, tmpUKey, 0, uPassBytes.length);
  442. System.arraycopy(userKeySalt, 0, tmpUKey, uPassBytes.length, 8);
  443. sha256.reset();
  444. sha256.update(tmpUKey);
  445. byte[] intUKey = sha256.digest();
  446. SecretKeySpec uSKeySpec = new SecretKeySpec(intUKey, "AES");
  447. cipherNoPadding.init(Cipher.DECRYPT_MODE, uSKeySpec, ivspecZero);
  448. byte[] uFileEncryptionKey = cipherNoPadding.doFinal(ue);
  449. // compute encryption key from oe
  450. byte[] tmpOKey = new byte[oPassBytes.length + 8 + 48];
  451. System.arraycopy(oPassBytes, 0, tmpOKey, 0, oPassBytes.length);
  452. System.arraycopy(ownerKeySalt, 0, tmpOKey, oPassBytes.length, 8);
  453. System.arraycopy(u, 0, tmpOKey, oPassBytes.length + 8, 48);
  454. sha256.reset();
  455. sha256.update(tmpOKey);
  456. byte[] intOKey = sha256.digest();
  457. SecretKeySpec oSKeySpec = new SecretKeySpec(intOKey, "AES");
  458. cipherNoPadding.init(Cipher.DECRYPT_MODE, oSKeySpec, ivspecZero);
  459. byte[] oFileEncryptionKey = cipherNoPadding.doFinal(oe);
  460. // check both keys are the same
  461. assertArrayEquals(uFileEncryptionKey, oFileEncryptionKey);
  462. byte[] fileEncryptionKey = new byte[uFileEncryptionKey.length];
  463. System.arraycopy(uFileEncryptionKey, 0, fileEncryptionKey, 0, uFileEncryptionKey.length);
  464. // decrypt perms
  465. SecretKeySpec sKeySpec = new SecretKeySpec(fileEncryptionKey, "AES");
  466. cipherNoPadding.init(Cipher.DECRYPT_MODE, sKeySpec, ivspecZero);
  467. byte[] decryptedPerms = cipherNoPadding.doFinal(perms);
  468. assertEquals('T', decryptedPerms[8]); // metadata encrypted by default
  469. assertEquals('a', decryptedPerms[9]);
  470. assertEquals('d', decryptedPerms[10]);
  471. assertEquals('b', decryptedPerms[11]);
  472. int expectedPermissions = -4; // default if nothing set
  473. int actualPermissions = decryptedPerms[3] << 24 | (decryptedPerms[2] & 0xFF) << 16
  474. | (decryptedPerms[1] & 0xFF) << 8 | (decryptedPerms[0] & 0xFF);
  475. assertEquals(expectedPermissions, actualPermissions);
  476. // decrypt data
  477. byte[] iv = new byte[16];
  478. System.arraycopy(encrypted, 0, iv, 0, 16);
  479. byte[] encryptedData = new byte[encrypted.length - 16];
  480. System.arraycopy(encrypted, 16, encryptedData, 0, encrypted.length - 16);
  481. IvParameterSpec ivspec = new IvParameterSpec(iv);
  482. Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
  483. cipher.init(Cipher.DECRYPT_MODE, sKeySpec, ivspec);
  484. byte[] decryptedData = cipher.doFinal(encryptedData);
  485. assertArrayEquals(data, decryptedData);
  486. }
  487. private byte[] parseHexStringEntries(String dictionary, String entry) throws UnsupportedEncodingException {
  488. String token = "/" + entry + " <";
  489. int start = dictionary.indexOf(token) + token.length();
  490. int end = dictionary.indexOf(">", start);
  491. String parsedEntry = dictionary.substring(start, end);
  492. int length = parsedEntry.length();
  493. byte[] data = new byte[length / 2];
  494. for (int i = 0; i < length; i += 2) {
  495. data[i / 2] = (byte) ((Character.digit(parsedEntry.charAt(i), 16) << 4) + Character.digit(
  496. parsedEntry.charAt(i + 1), 16));
  497. }
  498. return data;
  499. }
  500. /**
  501. * Creates an encryption object using a fixed file ID generator for test reproducibility.
  502. *
  503. * @param params the encryption parameters
  504. * @return PDFEncryptionJCE the encryption object
  505. */
  506. private PDFEncryptionJCE createEncryptionObject(PDFEncryptionParams params) {
  507. PDFDocument doc = new PDFDocument("Apache FOP") {
  508. @Override
  509. FileIDGenerator getFileIDGenerator() {
  510. return new FileIDGenerator() {
  511. private final byte[] fixedFileID = new byte[] {
  512. 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
  513. 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
  514. @Override
  515. byte[] getOriginalFileID() {
  516. return fixedFileID;
  517. }
  518. @Override
  519. byte[] getUpdatedFileID() {
  520. return fixedFileID;
  521. }
  522. };
  523. }
  524. };
  525. return (PDFEncryptionJCE) PDFEncryptionJCE.make(new PDFObjectNumber(1), params, doc);
  526. }
  527. private void runEncryptionTests() throws IOException {
  528. encryptionObject = createEncryptionObject(test.getEncryptionParameters());
  529. runEncryptTest();
  530. runFilterTest();
  531. runEncryptionDictionaryTest();
  532. }
  533. private void runEncryptTest() {
  534. PDFText text = new PDFText();
  535. text.setObjectNumber(test.getObjectNumber());
  536. byte[] byteResult = encryptionObject.encrypt(test.getData(), text);
  537. assertTrue(Arrays.equals(test.getEncryptedData(), byteResult));
  538. }
  539. private void runFilterTest() throws IOException {
  540. PDFStream stream = new PDFStream();
  541. stream.setDocument(encryptionObject.getDocumentSafely());
  542. stream.setObjectNumber(test.getObjectNumber());
  543. stream.setData(test.getData());
  544. encryptionObject.applyFilter(stream);
  545. StreamCache streamCache = stream.encodeStream();
  546. ByteArrayOutputStream testOutputStream = new ByteArrayOutputStream();
  547. streamCache.outputContents(testOutputStream);
  548. assertTrue(Arrays.equals(test.getEncryptedData(), testOutputStream.toByteArray()));
  549. }
  550. private void runEncryptionDictionaryTest() {
  551. test.testEncryptionDictionary(encryptionObject);
  552. }
  553. }