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