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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  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 static org.junit.Assert.assertEquals;
  20. import static org.junit.Assert.assertTrue;
  21. import java.io.ByteArrayOutputStream;
  22. import java.io.IOException;
  23. import java.io.UnsupportedEncodingException;
  24. import java.util.Arrays;
  25. import java.util.regex.Matcher;
  26. import java.util.regex.Pattern;
  27. import org.junit.Test;
  28. /**
  29. * Tests the {@link PDFEncryptionJCE} class.
  30. */
  31. public class PDFEncryptionJCETestCase {
  32. private EncryptionTest test;
  33. private PDFEncryptionJCE encryptionObject;
  34. private static final class EncryptionTest {
  35. private int objectNumber = 1;
  36. private final PDFEncryptionParams encryptionParameters = new PDFEncryptionParams();
  37. private byte[] data;
  38. private byte[] encryptedData;
  39. private final EncryptionDictionaryTester encryptionDictionaryTester;
  40. EncryptionTest() {
  41. this(new EncryptionDictionaryTester());
  42. }
  43. EncryptionTest(EncryptionDictionaryTester encryptionDictionaryTester) {
  44. encryptionParameters.setUserPassword("TestUserPassword");
  45. encryptionParameters.setOwnerPassword("TestOwnerPassword");
  46. setData(0x00, 0xAA, 0xFF, 0x55, 0xCC, 0x33, 0xF0);
  47. this.encryptionDictionaryTester = encryptionDictionaryTester;
  48. this.encryptionDictionaryTester.setLength(
  49. encryptionParameters.getEncryptionLengthInBits());
  50. }
  51. int getObjectNumber() {
  52. return objectNumber;
  53. }
  54. EncryptionTest setObjectNumber(int objectNumber) {
  55. this.objectNumber = objectNumber;
  56. return this;
  57. }
  58. byte[] getData() {
  59. return data;
  60. }
  61. EncryptionTest setData(int... data) {
  62. /*
  63. * Use an array of int to avoid having to cast some elements to byte in the
  64. * method call.
  65. */
  66. this.data = convertIntArrayToByteArray(data);
  67. return this;
  68. }
  69. byte[] getEncryptedData() {
  70. return encryptedData;
  71. }
  72. EncryptionTest setEncryptedData(int... encryptedData) {
  73. this.encryptedData = convertIntArrayToByteArray(encryptedData);
  74. return this;
  75. }
  76. private byte[] convertIntArrayToByteArray(int[] intArray) {
  77. byte[] byteArray = new byte[intArray.length];
  78. for (int i = 0; i < intArray.length; i++) {
  79. byteArray[i] = (byte) intArray[i];
  80. }
  81. return byteArray;
  82. }
  83. PDFEncryptionParams getEncryptionParameters() {
  84. return encryptionParameters;
  85. }
  86. EncryptionTest setUserPassword(String userPassword) {
  87. encryptionParameters.setUserPassword(userPassword);
  88. return this;
  89. }
  90. EncryptionTest setOwnerPassword(String ownerPassword) {
  91. encryptionParameters.setOwnerPassword(ownerPassword);
  92. return this;
  93. }
  94. EncryptionTest setEncryptionLength(int encryptionLength) {
  95. encryptionParameters.setEncryptionLengthInBits(encryptionLength);
  96. encryptionDictionaryTester.setLength(encryptionLength);
  97. return this;
  98. }
  99. EncryptionTest disablePrint() {
  100. encryptionParameters.setAllowPrint(false);
  101. return this;
  102. }
  103. EncryptionTest disableEditContent() {
  104. encryptionParameters.setAllowEditContent(false);
  105. return this;
  106. }
  107. EncryptionTest disableCopyContent() {
  108. encryptionParameters.setAllowCopyContent(false);
  109. return this;
  110. }
  111. EncryptionTest disableEditAnnotations() {
  112. encryptionParameters.setAllowEditAnnotations(false);
  113. return this;
  114. }
  115. EncryptionTest disableFillInForms() {
  116. encryptionParameters.setAllowFillInForms(false);
  117. return this;
  118. }
  119. EncryptionTest disableAccessContent() {
  120. encryptionParameters.setAllowAccessContent(false);
  121. return this;
  122. }
  123. EncryptionTest disableAssembleDocument() {
  124. encryptionParameters.setAllowAssembleDocument(false);
  125. return this;
  126. }
  127. EncryptionTest disablePrintHq() {
  128. encryptionParameters.setAllowPrintHq(false);
  129. return this;
  130. }
  131. void testEncryptionDictionary(PDFEncryptionJCE encryptionObject) {
  132. encryptionDictionaryTester.test(encryptionObject);
  133. }
  134. }
  135. private static final class EncryptionDictionaryTester {
  136. private int version = 1;
  137. private int revision = 2;
  138. private int length = 40;
  139. private int permissions = -4;
  140. private String ownerEntry
  141. = "3EE8C4000CA44B2645EED029C9EA7D4FC63C6D9B89349E8FA5A40C7691AB96B5";
  142. private String userEntry
  143. = "D1810D9E6E488BA5D2DDCBB3F974F7472D0D5389F554DB55574A787DC5C59884";
  144. EncryptionDictionaryTester setVersion(int version) {
  145. this.version = version;
  146. return this;
  147. }
  148. EncryptionDictionaryTester setRevision(int revision) {
  149. this.revision = revision;
  150. return this;
  151. }
  152. EncryptionDictionaryTester setLength(int length) {
  153. this.length = length;
  154. return this;
  155. }
  156. EncryptionDictionaryTester setPermissions(int permissions) {
  157. this.permissions = permissions;
  158. return this;
  159. }
  160. EncryptionDictionaryTester setOwnerEntry(String ownerEntry) {
  161. this.ownerEntry = ownerEntry;
  162. return this;
  163. }
  164. EncryptionDictionaryTester setUserEntry(String userEntry) {
  165. this.userEntry = userEntry;
  166. return this;
  167. }
  168. void test(PDFEncryptionJCE encryptionObject) {
  169. byte[] encryptionDictionary = encryptionObject.toPDF();
  170. RegexTestedCharSequence dictionary = new RegexTestedCharSequence(encryptionDictionary);
  171. final String whitespace = "\\s+";
  172. final String digits = "\\d+";
  173. final String hexDigits = "\\p{XDigit}+";
  174. dictionary.mustContain("/Filter" + whitespace + "/Standard\\b");
  175. dictionary.mustContain("/V" + whitespace + "(" + digits + ")")
  176. .withGroup1EqualTo(Integer.toString(version));
  177. dictionary.mustContain("/R" + whitespace + "(" + digits + ")")
  178. .withGroup1EqualTo(Integer.toString(revision));
  179. dictionary.mustContain("/Length" + whitespace + "(" + digits + ")")
  180. .withGroup1EqualTo(Integer.toString(length));
  181. dictionary.mustContain("/P" + whitespace + "(-?" + digits + ")")
  182. .withGroup1EqualTo(Integer.toString(permissions));
  183. dictionary.mustContain("/O" + whitespace + "<(" + hexDigits + ")>")
  184. .withGroup1EqualTo(ownerEntry);
  185. dictionary.mustContain("/U" + whitespace + "<(" + hexDigits + ")>")
  186. .withGroup1EqualTo(userEntry);
  187. }
  188. }
  189. private static final class RegexTestedCharSequence {
  190. private final String string;
  191. private Matcher matcher;
  192. RegexTestedCharSequence(byte[] bytes) {
  193. try {
  194. string = new String(bytes, "US-ASCII");
  195. } catch (UnsupportedEncodingException e) {
  196. throw new RuntimeException(e);
  197. }
  198. }
  199. RegexTestedCharSequence mustContain(String regex) {
  200. Pattern pattern = Pattern.compile(regex);
  201. matcher = pattern.matcher(string);
  202. assertTrue(matcher.find());
  203. return this;
  204. }
  205. RegexTestedCharSequence withGroup1EqualTo(String expected) {
  206. assertEquals(expected, matcher.group(1));
  207. return this;
  208. }
  209. }
  210. @Test
  211. public final void testMake() {
  212. PDFEncryption testEncryptionObj = createEncryptionObject(new PDFEncryptionParams());
  213. assertTrue(testEncryptionObj instanceof PDFEncryptionJCE);
  214. assertEquals(1, ((PDFEncryptionJCE) testEncryptionObj).getObjectNumber());
  215. }
  216. @Test
  217. public void testBasic() throws IOException {
  218. test = new EncryptionTest();
  219. test.setData(0x00).setEncryptedData(0x56);
  220. runEncryptionTests();
  221. test.setData(0xAA).setEncryptedData(0xFC);
  222. runEncryptionTests();
  223. test.setData(0xFF).setEncryptedData(0xA9);
  224. runEncryptionTests();
  225. test = new EncryptionTest().setEncryptedData(0x56, 0x0C, 0xFC, 0xA5, 0xAB, 0x61, 0x73);
  226. runEncryptionTests();
  227. }
  228. @Test
  229. public void test128bit() throws IOException {
  230. EncryptionDictionaryTester encryptionDictionaryTester = new EncryptionDictionaryTester()
  231. .setVersion(2)
  232. .setRevision(3)
  233. .setPermissions(-4)
  234. .setOwnerEntry("D9A98017F0500EF9B69738641C9B4CBA1229EDC3F2151BC6C9C4FB07B1CB315E")
  235. .setUserEntry("D3EF424BFEA2E434000E1A74941CC87300000000000000000000000000000000");
  236. test = new EncryptionTest(encryptionDictionaryTester)
  237. .setObjectNumber(2)
  238. .setEncryptionLength(128)
  239. .setEncryptedData(0xE3, 0xCB, 0xB2, 0x55, 0xD9, 0x26, 0x55);
  240. runEncryptionTests();
  241. }
  242. @Test
  243. public void testDisableRev2Permissions() throws IOException {
  244. EncryptionDictionaryTester encryptionDictionaryTester = new EncryptionDictionaryTester()
  245. .setPermissions(-64)
  246. .setUserEntry("3E65D0090746C4C37C5EF23C1BDB6323E00C24C4B2D744DD3BFB654CD58591A1");
  247. test = new EncryptionTest(encryptionDictionaryTester)
  248. .setObjectNumber(3)
  249. .disablePrint()
  250. .disableEditContent()
  251. .disableCopyContent()
  252. .disableEditAnnotations()
  253. .setEncryptedData(0x66, 0xEE, 0xA7, 0x93, 0xC4, 0xB1, 0xB4);
  254. runEncryptionTests();
  255. }
  256. @Test
  257. public void testDisableRev3Permissions() throws IOException {
  258. EncryptionDictionaryTester encryptionDictionaryTester = new EncryptionDictionaryTester()
  259. .setVersion(2)
  260. .setRevision(3)
  261. .setPermissions(-3844)
  262. .setOwnerEntry("8D4BCA4F4AB2BAB4E38F161D61F937EC50BE5EB30C2DC05EA409D252CD695E55")
  263. .setUserEntry("0F01171E22C7FB27B079C132BA4277DE00000000000000000000000000000000");
  264. test = new EncryptionTest(encryptionDictionaryTester)
  265. .setObjectNumber(4)
  266. .disableFillInForms()
  267. .disableAccessContent()
  268. .disableAssembleDocument()
  269. .disablePrintHq()
  270. .setEncryptedData(0x8E, 0x3C, 0xD2, 0x05, 0x50, 0x48, 0x82);
  271. runEncryptionTests();
  272. }
  273. @Test
  274. public void test128bitDisableSomePermissions() throws IOException {
  275. EncryptionDictionaryTester encryptionDictionaryTester = new EncryptionDictionaryTester()
  276. .setVersion(2)
  277. .setRevision(3)
  278. .setPermissions(-1304)
  279. .setOwnerEntry("D9A98017F0500EF9B69738641C9B4CBA1229EDC3F2151BC6C9C4FB07B1CB315E")
  280. .setUserEntry("62F0E4D8641D482E0F8E71A89270045A00000000000000000000000000000000");
  281. test = new EncryptionTest(encryptionDictionaryTester)
  282. .setObjectNumber(5)
  283. .setEncryptionLength(128)
  284. .disablePrint()
  285. .disableCopyContent()
  286. .disableFillInForms()
  287. .disableAssembleDocument()
  288. .setEncryptedData(0xF7, 0x85, 0x4F, 0xB0, 0x50, 0x5C, 0xDF);
  289. runEncryptionTests();
  290. }
  291. @Test
  292. public void testDifferentPasswords() throws IOException {
  293. EncryptionDictionaryTester encryptionDictionaryTester = new EncryptionDictionaryTester()
  294. .setOwnerEntry("D11C233C65E9DC872E858ABBD8B62198771167ADCE7AB8DC7AE0A1A7E21A1E25")
  295. .setUserEntry("6F449167DB8DDF0D2DF4602DDBBA97ABF9A9101F632CC16AB0BE74EB9500B469");
  296. test = new EncryptionTest(encryptionDictionaryTester)
  297. .setObjectNumber(6)
  298. .setUserPassword("ADifferentUserPassword")
  299. .setOwnerPassword("ADifferentOwnerPassword")
  300. .setEncryptedData(0x27, 0xAC, 0xB1, 0x6C, 0x42, 0xE0, 0xA8);
  301. runEncryptionTests();
  302. }
  303. @Test
  304. public void testNoOwnerPassword() throws IOException {
  305. EncryptionDictionaryTester encryptionDictionaryTester = new EncryptionDictionaryTester()
  306. .setOwnerEntry("5163AAF3EE74C76D7C223593A84C8702FEA8AA4493E4933FF5B5A5BBB20AE4BB")
  307. .setUserEntry("42DDF1C1BF3AB04786D5038E7B0A723AE614D944E1DE91A922FC54F5F2345E00");
  308. test = new EncryptionTest(encryptionDictionaryTester)
  309. .setObjectNumber(7)
  310. .setUserPassword("ADifferentUserPassword")
  311. .setOwnerPassword("")
  312. .setEncryptedData(0xEC, 0x2E, 0x5D, 0xC2, 0x7F, 0xAD, 0x58);
  313. runEncryptionTests();
  314. }
  315. @Test
  316. public void test128bitDisableSomePermissionsDifferentPasswords() throws IOException {
  317. EncryptionDictionaryTester encryptionDictionaryTester = new EncryptionDictionaryTester()
  318. .setVersion(2)
  319. .setRevision(3)
  320. .setPermissions(-2604)
  321. .setOwnerEntry("F83CA049FAA2F774F8541F25E746A92EE2A7F060C46C91C693E673BF18FF7B36")
  322. .setUserEntry("88A4C58F5385B5F08FACA0636D790EDF00000000000000000000000000000000");
  323. test = new EncryptionTest(encryptionDictionaryTester)
  324. .setObjectNumber(8)
  325. .setUserPassword("ADifferentUserPassword")
  326. .setOwnerPassword("ADifferentOwnerPassword")
  327. .setEncryptionLength(128)
  328. .disableEditContent()
  329. .disableEditAnnotations()
  330. .disableAccessContent()
  331. .disablePrintHq()
  332. .setEncryptedData(0x77, 0x54, 0x67, 0xA5, 0xCC, 0x73, 0xDE);
  333. runEncryptionTests();
  334. }
  335. @Test
  336. public void test128bitNoPermissionNoOwnerPassword() throws IOException {
  337. EncryptionDictionaryTester encryptionDictionaryTester = new EncryptionDictionaryTester()
  338. .setVersion(2)
  339. .setRevision(3)
  340. .setPermissions(-3904)
  341. .setOwnerEntry("3EEB3FA5594CBD935BFB2F83FB184DD41FBCD7C36A04F1FFD0899B0DFFCFF96B")
  342. .setUserEntry("D972B72DD2633F613B0DDB7511C719C500000000000000000000000000000000");
  343. test = new EncryptionTest(encryptionDictionaryTester)
  344. .setObjectNumber(9)
  345. .setUserPassword("ADifferentUserPassword")
  346. .setOwnerPassword("")
  347. .setEncryptionLength(128)
  348. .disablePrint()
  349. .disableEditContent()
  350. .disableCopyContent()
  351. .disableEditAnnotations()
  352. .disableFillInForms()
  353. .disableAccessContent()
  354. .disableAssembleDocument()
  355. .disablePrintHq()
  356. .setEncryptedData(0x0C, 0xAD, 0x49, 0xC7, 0xE5, 0x05, 0xB8);
  357. runEncryptionTests();
  358. }
  359. /**
  360. * Creates an encryption object using a fixed file ID generator for test reproducibility.
  361. *
  362. * @param params the encryption parameters
  363. * @return PDFEncryptionJCE the encryption object
  364. */
  365. private PDFEncryptionJCE createEncryptionObject(PDFEncryptionParams params) {
  366. PDFDocument doc = new PDFDocument("Apache FOP") {
  367. @Override
  368. FileIDGenerator getFileIDGenerator() {
  369. return new FileIDGenerator() {
  370. private final byte[] fixedFileID = new byte[] {
  371. 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
  372. 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
  373. @Override
  374. byte[] getOriginalFileID() {
  375. return fixedFileID;
  376. }
  377. @Override
  378. byte[] getUpdatedFileID() {
  379. return fixedFileID;
  380. }
  381. };
  382. }
  383. };
  384. return (PDFEncryptionJCE) PDFEncryptionJCE.make(1, params, doc);
  385. }
  386. private void runEncryptionTests() throws IOException {
  387. encryptionObject = createEncryptionObject(test.getEncryptionParameters());
  388. runEncryptTest();
  389. runFilterTest();
  390. runEncryptionDictionaryTest();
  391. }
  392. private void runEncryptTest() {
  393. PDFText text = new PDFText();
  394. text.setObjectNumber(test.getObjectNumber());
  395. byte[] byteResult = encryptionObject.encrypt(test.getData(), text);
  396. assertTrue(Arrays.equals(test.getEncryptedData(), byteResult));
  397. }
  398. private void runFilterTest() throws IOException {
  399. PDFStream stream = new PDFStream();
  400. stream.setDocument(encryptionObject.getDocumentSafely());
  401. stream.setObjectNumber(test.getObjectNumber());
  402. stream.setData(test.getData());
  403. encryptionObject.applyFilter(stream);
  404. StreamCache streamCache = stream.encodeStream();
  405. ByteArrayOutputStream testOutputStream = new ByteArrayOutputStream();
  406. streamCache.outputContents(testOutputStream);
  407. assertTrue(Arrays.equals(test.getEncryptedData(), testOutputStream.toByteArray()));
  408. }
  409. private void runEncryptionDictionaryTest() {
  410. test.testEncryptionDictionary(encryptionObject);
  411. }
  412. }