001/*
002 * $Id: PdfEncryption.java 4789 2011-03-28 20:40:57Z psoares33 $
003 *
004 * This file is part of the iText (R) project.
005 * Copyright (c) 1998-2011 1T3XT BVBA
006 * Authors: Bruno Lowagie, Paulo Soares, et al.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU Affero General Public License version 3
010 * as published by the Free Software Foundation with the addition of the
011 * following permission added to Section 15 as permitted in Section 7(a):
012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT,
013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
014 *
015 * This program is distributed in the hope that it will be useful, but
016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
017 * or FITNESS FOR A PARTICULAR PURPOSE.
018 * See the GNU Affero General Public License for more details.
019 * You should have received a copy of the GNU Affero General Public License
020 * along with this program; if not, see http://www.gnu.org/licenses or write to
021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
022 * Boston, MA, 02110-1301 USA, or download the license from the following URL:
023 * http://itextpdf.com/terms-of-use/
024 *
025 * The interactive user interfaces in modified source and object code versions
026 * of this program must display Appropriate Legal Notices, as required under
027 * Section 5 of the GNU Affero General Public License.
028 *
029 * In accordance with Section 7(b) of the GNU Affero General Public License,
030 * a covered work must retain the producer line in every PDF that is created
031 * or manipulated using iText.
032 *
033 * You can be released from the requirements of the license by purchasing
034 * a commercial license. Buying such a license is mandatory as soon as you
035 * develop commercial activities involving the iText software without
036 * disclosing the source code of your own applications.
037 * These activities include: offering paid services to customers as an ASP,
038 * serving PDFs on the fly in a web application, shipping iText with a closed
039 * source product.
040 *
041 * For more information, please contact iText Software Corp. at this
042 * address: sales@itextpdf.com
043 */
044package com.itextpdf.text.pdf;
045
046import com.itextpdf.text.pdf.crypto.ARCFOUREncryption;
047import com.itextpdf.text.error_messages.MessageLocalization;
048
049import java.io.IOException;
050import java.io.OutputStream;
051import java.io.ByteArrayOutputStream;
052import java.security.MessageDigest;
053import java.security.cert.Certificate;
054
055import com.itextpdf.text.ExceptionConverter;
056import com.itextpdf.text.exceptions.BadPasswordException;
057import com.itextpdf.text.pdf.crypto.AESCipherCBCnoPad;
058import com.itextpdf.text.pdf.crypto.IVGenerator;
059import java.security.DigestException;
060
061/**
062 * 
063 * @author Paulo Soares
064 * @author Kazuya Ujihara
065 */
066public class PdfEncryption {
067
068        public static final int STANDARD_ENCRYPTION_40 = 2;
069
070        public static final int STANDARD_ENCRYPTION_128 = 3;
071
072        public static final int AES_128 = 4;
073
074        public static final int AES_256 = 5;
075
076        private static final byte[] pad = { (byte) 0x28, (byte) 0xBF, (byte) 0x4E,
077                        (byte) 0x5E, (byte) 0x4E, (byte) 0x75, (byte) 0x8A, (byte) 0x41,
078                        (byte) 0x64, (byte) 0x00, (byte) 0x4E, (byte) 0x56, (byte) 0xFF,
079                        (byte) 0xFA, (byte) 0x01, (byte) 0x08, (byte) 0x2E, (byte) 0x2E,
080                        (byte) 0x00, (byte) 0xB6, (byte) 0xD0, (byte) 0x68, (byte) 0x3E,
081                        (byte) 0x80, (byte) 0x2F, (byte) 0x0C, (byte) 0xA9, (byte) 0xFE,
082                        (byte) 0x64, (byte) 0x53, (byte) 0x69, (byte) 0x7A };
083
084        private static final byte[] salt = { (byte) 0x73, (byte) 0x41, (byte) 0x6c,
085                        (byte) 0x54 };
086
087        private static final byte[] metadataPad = { (byte) 255, (byte) 255,
088                        (byte) 255, (byte) 255 };
089
090        /** The encryption key for a particular object/generation */
091        byte key[];
092
093        /** The encryption key length for a particular object/generation */
094        int keySize;
095
096        /** The global encryption key */
097        byte mkey[] = new byte[0];
098
099        /** Work area to prepare the object/generation bytes */
100        byte extra[] = new byte[5];
101
102        /** The message digest algorithm MD5 */
103        MessageDigest md5;
104
105        /** The encryption key for the owner */
106        byte ownerKey[] = new byte[32];
107
108        /** The encryption key for the user */
109        byte userKey[] = new byte[32];
110
111    byte[] oeKey;
112    byte[] ueKey;
113    byte[] perms;
114
115        /** The public key security handler for certificate encryption */
116        protected PdfPublicKeySecurityHandler publicKeyHandler = null;
117
118        int permissions;
119
120        byte documentID[];
121
122        static long seq = System.currentTimeMillis();
123
124        private int revision;
125
126        private ARCFOUREncryption arcfour = new ARCFOUREncryption();
127
128        /** The generic key length. It may be 40 or 128. */
129        private int keyLength;
130
131        private boolean encryptMetadata;
132        
133        /**
134         * Indicates if the encryption is only necessary for embedded files.
135         * @since 2.1.3
136         */
137        private boolean embeddedFilesOnly;
138
139        private int cryptoMode;
140
141        public PdfEncryption() {
142                try {
143                        md5 = MessageDigest.getInstance("MD5");
144                } catch (Exception e) {
145                        throw new ExceptionConverter(e);
146                }
147                publicKeyHandler = new PdfPublicKeySecurityHandler();
148        }
149
150        public PdfEncryption(PdfEncryption enc) {
151                this();
152        if (enc.key != null)
153            key = (byte[]) enc.key.clone();
154        keySize = enc.keySize;
155                mkey = (byte[]) enc.mkey.clone();
156                ownerKey = (byte[]) enc.ownerKey.clone();
157                userKey = (byte[]) enc.userKey.clone();
158                permissions = enc.permissions;
159                if (enc.documentID != null)
160                        documentID = (byte[]) enc.documentID.clone();
161                revision = enc.revision;
162                keyLength = enc.keyLength;
163                encryptMetadata = enc.encryptMetadata;
164                embeddedFilesOnly = enc.embeddedFilesOnly;
165                publicKeyHandler = enc.publicKeyHandler;
166        }
167
168        public void setCryptoMode(int mode, int kl) {
169                cryptoMode = mode;
170                encryptMetadata = (mode & PdfWriter.DO_NOT_ENCRYPT_METADATA) != PdfWriter.DO_NOT_ENCRYPT_METADATA;
171                embeddedFilesOnly = (mode & PdfWriter.EMBEDDED_FILES_ONLY) == PdfWriter.EMBEDDED_FILES_ONLY;
172                mode &= PdfWriter.ENCRYPTION_MASK;
173                switch (mode) {
174                case PdfWriter.STANDARD_ENCRYPTION_40:
175                        encryptMetadata = true;
176                        embeddedFilesOnly = false;
177                        keyLength = 40;
178                        revision = STANDARD_ENCRYPTION_40;
179                        break;
180                case PdfWriter.STANDARD_ENCRYPTION_128:
181                        embeddedFilesOnly = false;
182                        if (kl > 0)
183                                keyLength = kl;
184                        else
185                                keyLength = 128;
186                        revision = STANDARD_ENCRYPTION_128;
187                        break;
188                case PdfWriter.ENCRYPTION_AES_128:
189                        keyLength = 128;
190                        revision = AES_128;
191                        break;
192                case PdfWriter.ENCRYPTION_AES_256:
193                        keyLength = 256;
194            keySize = 32;
195                        revision = AES_256;
196                        break;
197                default:
198                        throw new IllegalArgumentException(MessageLocalization.getComposedMessage("no.valid.encryption.mode"));
199                }
200        }
201
202        public int getCryptoMode() {
203                return cryptoMode;
204        }
205
206        public boolean isMetadataEncrypted() {
207                return encryptMetadata;
208        }
209
210    public int getPermissions() {
211        return permissions;
212    }
213
214        /**
215         * Indicates if only the embedded files have to be encrypted.
216         * @return      if true only the embedded files will be encrypted
217         * @since       2.1.3
218         */
219        public boolean isEmbeddedFilesOnly() {
220                return embeddedFilesOnly;
221        }
222
223        /**
224         */
225        private byte[] padPassword(byte userPassword[]) {
226                byte userPad[] = new byte[32];
227                if (userPassword == null) {
228                        System.arraycopy(pad, 0, userPad, 0, 32);
229                } else {
230                        System.arraycopy(userPassword, 0, userPad, 0, Math.min(
231                                        userPassword.length, 32));
232                        if (userPassword.length < 32)
233                                System.arraycopy(pad, 0, userPad, userPassword.length,
234                                                32 - userPassword.length);
235                }
236
237                return userPad;
238        }
239
240        /**
241         */
242        private byte[] computeOwnerKey(byte userPad[], byte ownerPad[]) {
243        byte ownerKey[] = new byte[32];
244        byte digest[] = md5.digest(ownerPad);
245        if (revision == STANDARD_ENCRYPTION_128 || revision == AES_128) {
246            byte mkey[] = new byte[keyLength / 8];
247            // only use for the input as many bit as the key consists of
248            for (int k = 0; k < 50; ++k) {
249                md5.update(digest, 0, mkey.length);
250                System.arraycopy(md5.digest(), 0, digest, 0, mkey.length);
251            }
252            System.arraycopy(userPad, 0, ownerKey, 0, 32);
253            for (int i = 0; i < 20; ++i) {
254                for (int j = 0; j < mkey.length; ++j)
255                    mkey[j] = (byte) (digest[j] ^ i);
256                arcfour.prepareARCFOURKey(mkey);
257                arcfour.encryptARCFOUR(ownerKey);
258            }
259        } else {
260            arcfour.prepareARCFOURKey(digest, 0, 5);
261            arcfour.encryptARCFOUR(userPad, ownerKey);
262        }
263        return ownerKey;
264        }
265
266        /**
267         * 
268         * ownerKey, documentID must be setup
269         */
270        private void setupGlobalEncryptionKey(byte[] documentID, byte userPad[],
271                        byte ownerKey[], int permissions) {
272                this.documentID = documentID;
273                this.ownerKey = ownerKey;
274                this.permissions = permissions;
275                // use variable keylength
276                mkey = new byte[keyLength / 8];
277
278                // fixed by ujihara in order to follow PDF reference
279                md5.reset();
280                md5.update(userPad);
281                md5.update(ownerKey);
282
283                byte ext[] = new byte[4];
284                ext[0] = (byte) permissions;
285                ext[1] = (byte) (permissions >> 8);
286                ext[2] = (byte) (permissions >> 16);
287                ext[3] = (byte) (permissions >> 24);
288                md5.update(ext, 0, 4);
289                if (documentID != null)
290                        md5.update(documentID);
291                if (!encryptMetadata)
292                        md5.update(metadataPad);
293
294                byte digest[] = new byte[mkey.length];
295                System.arraycopy(md5.digest(), 0, digest, 0, mkey.length);
296
297                // only use the really needed bits as input for the hash
298                if (revision == STANDARD_ENCRYPTION_128 || revision == AES_128) {
299                        for (int k = 0; k < 50; ++k)
300                                System.arraycopy(md5.digest(digest), 0, digest, 0, mkey.length);
301                }
302
303                System.arraycopy(digest, 0, mkey, 0, mkey.length);
304        }
305
306        /**
307         * 
308         * mkey must be setup
309         */
310        // use the revision to choose the setup method
311        private void setupUserKey() {
312                if (revision == STANDARD_ENCRYPTION_128 || revision == AES_128) {
313                        md5.update(pad);
314                        byte digest[] = md5.digest(documentID);
315                        System.arraycopy(digest, 0, userKey, 0, 16);
316                        for (int k = 16; k < 32; ++k)
317                                userKey[k] = 0;
318                        for (int i = 0; i < 20; ++i) {
319                                for (int j = 0; j < mkey.length; ++j)
320                                        digest[j] = (byte) (mkey[j] ^ i);
321                                arcfour.prepareARCFOURKey(digest, 0, mkey.length);
322                                arcfour.encryptARCFOUR(userKey, 0, 16);
323                        }
324                } else {
325                        arcfour.prepareARCFOURKey(mkey);
326                        arcfour.encryptARCFOUR(pad, userKey);
327                }
328        }
329
330        // gets keylength and revision and uses revision to choose the initial values
331        // for permissions
332        public void setupAllKeys(byte userPassword[], byte ownerPassword[],
333                        int permissions) {
334                if (ownerPassword == null || ownerPassword.length == 0)
335                        ownerPassword = md5.digest(createDocumentId());
336                permissions |= (revision == STANDARD_ENCRYPTION_128 || revision == AES_128 || revision == AES_256) ? 0xfffff0c0
337                                : 0xffffffc0;
338                permissions &= 0xfffffffc;
339        this.permissions = permissions;
340        if (revision == AES_256) {
341            try {
342                if (userPassword == null)
343                    userPassword = new byte[0];
344                documentID = createDocumentId();
345                byte[] uvs = IVGenerator.getIV(8);
346                byte[] uks = IVGenerator.getIV(8);
347                key = IVGenerator.getIV(32);
348                // Algorithm 3.8.1
349                MessageDigest md = MessageDigest.getInstance("SHA-256");
350                md.update(userPassword, 0, Math.min(userPassword.length, 127));
351                md.update(uvs);
352                userKey = new byte[48];
353                md.digest(userKey, 0, 32);
354                System.arraycopy(uvs, 0, userKey, 32, 8);
355                System.arraycopy(uks, 0, userKey, 40, 8);
356                // Algorithm 3.8.2
357                md.update(userPassword, 0, Math.min(userPassword.length, 127));
358                md.update(uks);
359                AESCipherCBCnoPad ac = new AESCipherCBCnoPad(true, md.digest());
360                ueKey = ac.processBlock(key, 0, key.length);
361                // Algorithm 3.9.1
362                byte[] ovs = IVGenerator.getIV(8);
363                byte[] oks = IVGenerator.getIV(8);
364                md.update(ownerPassword, 0, Math.min(ownerPassword.length, 127));
365                md.update(ovs);
366                md.update(userKey);
367                ownerKey = new byte[48];
368                md.digest(ownerKey, 0, 32);
369                System.arraycopy(ovs, 0, ownerKey, 32, 8);
370                System.arraycopy(oks, 0, ownerKey, 40, 8);
371                // Algorithm 3.9.2
372                md.update(ownerPassword, 0, Math.min(ownerPassword.length, 127));
373                md.update(oks);
374                md.update(userKey);
375                ac = new AESCipherCBCnoPad(true, md.digest());
376                oeKey = ac.processBlock(key, 0, key.length);
377                // Algorithm 3.10
378                byte[] permsp = IVGenerator.getIV(16);
379                permsp[0] = (byte)permissions;
380                permsp[1] = (byte)(permissions >> 8);
381                permsp[2] = (byte)(permissions >> 16);
382                permsp[3] = (byte)(permissions >> 24);
383                permsp[4] = (byte)(255);
384                permsp[5] = (byte)(255);
385                permsp[6] = (byte)(255);
386                permsp[7] = (byte)(255);
387                permsp[8] = encryptMetadata ? (byte)'T' : (byte)'F';
388                permsp[9] = (byte)'a';
389                permsp[10] = (byte)'d';
390                permsp[11] = (byte)'b';
391                ac = new AESCipherCBCnoPad(true, key);
392                perms = ac.processBlock(permsp, 0, permsp.length);
393            }
394            catch (Exception ex) {
395                throw new ExceptionConverter(ex);
396            }
397        }
398        else {
399            // PDF reference 3.5.2 Standard Security Handler, Algorithm 3.3-1
400            // If there is no owner password, use the user password instead.
401            byte userPad[] = padPassword(userPassword);
402            byte ownerPad[] = padPassword(ownerPassword);
403
404            this.ownerKey = computeOwnerKey(userPad, ownerPad);
405            documentID = createDocumentId();
406            setupByUserPad(this.documentID, userPad, this.ownerKey, permissions);
407        }
408        }
409
410    private static final int VALIDATION_SALT_OFFSET = 32;
411    private static final int KEY_SALT_OFFSET = 40;
412    private static final int SALT_LENGHT = 8;
413    private static final int OU_LENGHT = 48;
414
415    public boolean readKey(PdfDictionary enc, byte[] password) throws BadPasswordException {
416        try {
417            if (password == null)
418                password = new byte[0];
419            byte[] oValue = com.itextpdf.text.DocWriter.getISOBytes(enc.get(PdfName.O).toString());
420            byte[] uValue = com.itextpdf.text.DocWriter.getISOBytes(enc.get(PdfName.U).toString());
421            byte[] oeValue = com.itextpdf.text.DocWriter.getISOBytes(enc.get(PdfName.OE).toString());
422            byte[] ueValue = com.itextpdf.text.DocWriter.getISOBytes(enc.get(PdfName.UE).toString());
423            byte[] perms = com.itextpdf.text.DocWriter.getISOBytes(enc.get(PdfName.PERMS).toString());
424            boolean isUserPass = false;
425            MessageDigest md = MessageDigest.getInstance("SHA-256");
426            md.update(password, 0, Math.min(password.length, 127));
427            md.update(oValue, VALIDATION_SALT_OFFSET, SALT_LENGHT);
428            md.update(uValue, 0, OU_LENGHT);
429            byte[] hash = md.digest();
430            boolean isOwnerPass = compareArray(hash, oValue, 32);
431            if (isOwnerPass) {
432                md.update(password, 0, Math.min(password.length, 127));
433                md.update(oValue, KEY_SALT_OFFSET, SALT_LENGHT);
434                md.update(uValue, 0, OU_LENGHT);
435                hash = md.digest();
436                AESCipherCBCnoPad ac = new AESCipherCBCnoPad(false, hash);
437                key = ac.processBlock(oeValue, 0, oeValue.length);
438            }
439            else {
440                md.update(password, 0, Math.min(password.length, 127));
441                md.update(uValue, VALIDATION_SALT_OFFSET, SALT_LENGHT);
442                hash = md.digest();
443                isUserPass = compareArray(hash, uValue, 32);
444                if (!isUserPass)
445                    throw new BadPasswordException(MessageLocalization.getComposedMessage("bad.user.password"));
446                md.update(password, 0, Math.min(password.length, 127));
447                md.update(uValue, KEY_SALT_OFFSET, SALT_LENGHT);
448                hash = md.digest();
449                AESCipherCBCnoPad ac = new AESCipherCBCnoPad(false, hash);
450                key = ac.processBlock(ueValue, 0, ueValue.length);
451            }
452            AESCipherCBCnoPad ac = new AESCipherCBCnoPad(false, key);
453            byte[] decPerms = ac.processBlock(perms, 0, perms.length);
454            if (decPerms[9] != (byte)'a' || decPerms[10] != (byte)'d' || decPerms[11] != (byte)'b')
455                throw new BadPasswordException(MessageLocalization.getComposedMessage("bad.user.password"));
456            permissions = (decPerms[0] & 0xff) | ((decPerms[1] & 0xff) << 8)
457                    | ((decPerms[2] & 0xff) << 16) | ((decPerms[2] & 0xff) << 24);
458            encryptMetadata = decPerms[8] == (byte)'T';
459            return isOwnerPass;
460        }
461        catch (BadPasswordException ex) {
462            throw ex;
463        }
464        catch (Exception ex) {
465            throw new ExceptionConverter(ex);
466        }
467    }
468
469    private static boolean compareArray(byte[] a, byte[] b, int len) {
470        for (int k = 0; k < len; ++k) {
471            if (a[k] != b[k]) {
472                return false;
473            }
474        }
475        return true;
476    }
477
478        public static byte[] createDocumentId() {
479                MessageDigest md5;
480                try {
481                        md5 = MessageDigest.getInstance("MD5");
482                } catch (Exception e) {
483                        throw new ExceptionConverter(e);
484                }
485                long time = System.currentTimeMillis();
486                long mem = Runtime.getRuntime().freeMemory();
487                String s = time + "+" + mem + "+" + (seq++);
488                return md5.digest(s.getBytes());
489        }
490
491        /**
492         */
493        public void setupByUserPassword(byte[] documentID, byte userPassword[],
494                        byte ownerKey[], int permissions) {
495                setupByUserPad(documentID, padPassword(userPassword), ownerKey,
496                                permissions);
497        }
498
499        /**
500         */
501        private void setupByUserPad(byte[] documentID, byte userPad[],
502                        byte ownerKey[], int permissions) {
503                setupGlobalEncryptionKey(documentID, userPad, ownerKey, permissions);
504                setupUserKey();
505        }
506
507        /**
508         */
509        public void setupByOwnerPassword(byte[] documentID, byte ownerPassword[],
510                        byte userKey[], byte ownerKey[], int permissions) {
511                setupByOwnerPad(documentID, padPassword(ownerPassword), userKey,
512                                ownerKey, permissions);
513        }
514
515        private void setupByOwnerPad(byte[] documentID, byte ownerPad[],
516                        byte userKey[], byte ownerKey[], int permissions) {
517                byte userPad[] = computeOwnerKey(ownerKey, ownerPad); // userPad will
518                                                                                                                                // be set in
519                                                                                                                                // this.ownerKey
520                setupGlobalEncryptionKey(documentID, userPad, ownerKey, permissions); // step
521                                                                                                                                                                // 3
522                setupUserKey();
523        }
524
525        public void setupByEncryptionKey(byte[] key, int keylength) {
526                mkey = new byte[keylength / 8];
527                System.arraycopy(key, 0, mkey, 0, mkey.length);
528        }
529
530        public void setHashKey(int number, int generation) {
531        if (revision == AES_256)
532            return;
533                md5.reset(); // added by ujihara
534                extra[0] = (byte) number;
535                extra[1] = (byte) (number >> 8);
536                extra[2] = (byte) (number >> 16);
537                extra[3] = (byte) generation;
538                extra[4] = (byte) (generation >> 8);
539                md5.update(mkey);
540                md5.update(extra);
541                if (revision == AES_128)
542                        md5.update(salt);
543                key = md5.digest();
544                keySize = mkey.length + 5;
545                if (keySize > 16)
546                        keySize = 16;
547        }
548
549        public static PdfObject createInfoId(byte id[]) {
550                ByteBuffer buf = new ByteBuffer(90);
551                buf.append('[').append('<');
552                for (int k = 0; k < 16; ++k)
553                        buf.appendHex(id[k]);
554                buf.append('>').append('<');
555                id = createDocumentId();
556                for (int k = 0; k < 16; ++k)
557                        buf.appendHex(id[k]);
558                buf.append('>').append(']');
559                return new PdfLiteral(buf.toByteArray());
560        }
561
562        public PdfDictionary getEncryptionDictionary() {
563                PdfDictionary dic = new PdfDictionary();
564
565                if (publicKeyHandler.getRecipientsSize() > 0) {
566                        PdfArray recipients = null;
567
568                        dic.put(PdfName.FILTER, PdfName.PUBSEC);
569                        dic.put(PdfName.R, new PdfNumber(revision));
570
571                        try {
572                                recipients = publicKeyHandler.getEncodedRecipients();
573                        } catch (Exception f) {
574                                throw new ExceptionConverter(f);
575                        }
576
577                        if (revision == STANDARD_ENCRYPTION_40) {
578                                dic.put(PdfName.V, new PdfNumber(1));
579                                dic.put(PdfName.SUBFILTER, PdfName.ADBE_PKCS7_S4);
580                                dic.put(PdfName.RECIPIENTS, recipients);
581                        } else if (revision == STANDARD_ENCRYPTION_128 && encryptMetadata) {
582                                dic.put(PdfName.V, new PdfNumber(2));
583                                dic.put(PdfName.LENGTH, new PdfNumber(128));
584                                dic.put(PdfName.SUBFILTER, PdfName.ADBE_PKCS7_S4);
585                                dic.put(PdfName.RECIPIENTS, recipients);
586                        } else {
587                                dic.put(PdfName.R, new PdfNumber(AES_128));
588                                dic.put(PdfName.V, new PdfNumber(4));
589                                dic.put(PdfName.SUBFILTER, PdfName.ADBE_PKCS7_S5);
590
591                                PdfDictionary stdcf = new PdfDictionary();
592                                stdcf.put(PdfName.RECIPIENTS, recipients);
593                                if (!encryptMetadata)
594                                        stdcf.put(PdfName.ENCRYPTMETADATA, PdfBoolean.PDFFALSE);
595
596                                if (revision == AES_128)
597                                        stdcf.put(PdfName.CFM, PdfName.AESV2);
598                                else
599                                        stdcf.put(PdfName.CFM, PdfName.V2);
600                                PdfDictionary cf = new PdfDictionary();
601                                cf.put(PdfName.DEFAULTCRYPTFILTER, stdcf);
602                                dic.put(PdfName.CF, cf);
603                if (embeddedFilesOnly) {
604                                        dic.put(PdfName.EFF, PdfName.DEFAULTCRYPTFILTER);
605                                        dic.put(PdfName.STRF, PdfName.IDENTITY);
606                                        dic.put(PdfName.STMF, PdfName.IDENTITY);
607                                }
608                                else {
609                                        dic.put(PdfName.STRF, PdfName.DEFAULTCRYPTFILTER);
610                                        dic.put(PdfName.STMF, PdfName.DEFAULTCRYPTFILTER);
611                                }
612                        }
613
614                        MessageDigest md = null;
615                        byte[] encodedRecipient = null;
616
617                        try {
618                                md = MessageDigest.getInstance("SHA-1");
619                                md.update(publicKeyHandler.getSeed());
620                                for (int i = 0; i < publicKeyHandler.getRecipientsSize(); i++) {
621                                        encodedRecipient = publicKeyHandler.getEncodedRecipient(i);
622                                        md.update(encodedRecipient);
623                                }
624                                if (!encryptMetadata)
625                                        md.update(new byte[] { (byte) 255, (byte) 255, (byte) 255,
626                                                        (byte) 255 });
627                        } catch (Exception f) {
628                                throw new ExceptionConverter(f);
629                        }
630
631                        byte[] mdResult = md.digest();
632
633                        setupByEncryptionKey(mdResult, keyLength);
634                } else {
635                        dic.put(PdfName.FILTER, PdfName.STANDARD);
636                        dic.put(PdfName.O, new PdfLiteral(PdfContentByte
637                                        .escapeString(ownerKey)));
638                        dic.put(PdfName.U, new PdfLiteral(PdfContentByte
639                                        .escapeString(userKey)));
640                        dic.put(PdfName.P, new PdfNumber(permissions));
641                        dic.put(PdfName.R, new PdfNumber(revision));
642
643                        if (revision == STANDARD_ENCRYPTION_40) {
644                                dic.put(PdfName.V, new PdfNumber(1));
645                        } else if (revision == STANDARD_ENCRYPTION_128 && encryptMetadata) {
646                                dic.put(PdfName.V, new PdfNumber(2));
647                                dic.put(PdfName.LENGTH, new PdfNumber(128));
648
649                        }
650            else if (revision == AES_256) {
651                                if (!encryptMetadata)
652                                        dic.put(PdfName.ENCRYPTMETADATA, PdfBoolean.PDFFALSE);
653                dic.put(PdfName.OE, new PdfLiteral(PdfContentByte
654                        .escapeString(oeKey)));
655                dic.put(PdfName.UE, new PdfLiteral(PdfContentByte
656                        .escapeString(ueKey)));
657                dic.put(PdfName.PERMS, new PdfLiteral(PdfContentByte
658                        .escapeString(perms)));
659                                dic.put(PdfName.V, new PdfNumber(revision));
660                                dic.put(PdfName.LENGTH, new PdfNumber(256));
661                                PdfDictionary stdcf = new PdfDictionary();
662                                stdcf.put(PdfName.LENGTH, new PdfNumber(32));
663                                if (embeddedFilesOnly) {
664                                        stdcf.put(PdfName.AUTHEVENT, PdfName.EFOPEN);
665                                        dic.put(PdfName.EFF, PdfName.STDCF);
666                                        dic.put(PdfName.STRF, PdfName.IDENTITY);
667                                        dic.put(PdfName.STMF, PdfName.IDENTITY);
668                                }
669                                else {
670                                        stdcf.put(PdfName.AUTHEVENT, PdfName.DOCOPEN);
671                                        dic.put(PdfName.STRF, PdfName.STDCF);
672                                        dic.put(PdfName.STMF, PdfName.STDCF);
673                                }
674                                stdcf.put(PdfName.CFM, PdfName.AESV3);
675                                PdfDictionary cf = new PdfDictionary();
676                                cf.put(PdfName.STDCF, stdcf);
677                                dic.put(PdfName.CF, cf);
678            }
679            else {
680                                if (!encryptMetadata)
681                                        dic.put(PdfName.ENCRYPTMETADATA, PdfBoolean.PDFFALSE);
682                                dic.put(PdfName.R, new PdfNumber(AES_128));
683                                dic.put(PdfName.V, new PdfNumber(4));
684                                dic.put(PdfName.LENGTH, new PdfNumber(128));
685                                PdfDictionary stdcf = new PdfDictionary();
686                                stdcf.put(PdfName.LENGTH, new PdfNumber(16));
687                                if (embeddedFilesOnly) {
688                                        stdcf.put(PdfName.AUTHEVENT, PdfName.EFOPEN);
689                                        dic.put(PdfName.EFF, PdfName.STDCF);
690                                        dic.put(PdfName.STRF, PdfName.IDENTITY);
691                                        dic.put(PdfName.STMF, PdfName.IDENTITY);
692                                }
693                                else {
694                                        stdcf.put(PdfName.AUTHEVENT, PdfName.DOCOPEN);
695                                        dic.put(PdfName.STRF, PdfName.STDCF);
696                                        dic.put(PdfName.STMF, PdfName.STDCF);
697                                }
698                                if (revision == AES_128)
699                                        stdcf.put(PdfName.CFM, PdfName.AESV2);
700                                else
701                                        stdcf.put(PdfName.CFM, PdfName.V2);
702                                PdfDictionary cf = new PdfDictionary();
703                                cf.put(PdfName.STDCF, stdcf);
704                                dic.put(PdfName.CF, cf);
705                        }
706                }
707
708                return dic;
709        }
710
711        public PdfObject getFileID() {
712                return createInfoId(documentID);
713        }
714
715        public OutputStreamEncryption getEncryptionStream(OutputStream os) {
716                return new OutputStreamEncryption(os, key, 0, keySize, revision);
717        }
718
719        public int calculateStreamSize(int n) {
720                if (revision == AES_128 || revision == AES_256)
721                        return (n & 0x7ffffff0) + 32;
722                else
723                        return n;
724        }
725
726        public byte[] encryptByteArray(byte[] b) {
727                try {
728                        ByteArrayOutputStream ba = new ByteArrayOutputStream();
729                        OutputStreamEncryption os2 = getEncryptionStream(ba);
730                        os2.write(b);
731                        os2.finish();
732                        return ba.toByteArray();
733                } catch (IOException ex) {
734                        throw new ExceptionConverter(ex);
735                }
736        }
737
738        public StandardDecryption getDecryptor() {
739                return new StandardDecryption(key, 0, keySize, revision);
740        }
741
742        public byte[] decryptByteArray(byte[] b) {
743                try {
744                        ByteArrayOutputStream ba = new ByteArrayOutputStream();
745                        StandardDecryption dec = getDecryptor();
746                        byte[] b2 = dec.update(b, 0, b.length);
747                        if (b2 != null)
748                                ba.write(b2);
749                        b2 = dec.finish();
750                        if (b2 != null)
751                                ba.write(b2);
752                        return ba.toByteArray();
753                } catch (IOException ex) {
754                        throw new ExceptionConverter(ex);
755                }
756        }
757
758        public void addRecipient(Certificate cert, int permission) {
759                documentID = createDocumentId();
760                publicKeyHandler.addRecipient(new PdfPublicKeyRecipient(cert,
761                                permission));
762        }
763
764        public byte[] computeUserPassword(byte[] ownerPassword) {
765                byte[] userPad = computeOwnerKey(ownerKey, padPassword(ownerPassword));
766                for (int i = 0; i < userPad.length; i++) {
767                        boolean match = true;
768                        for (int j = 0; j < userPad.length - i; j++) {
769                                if (userPad[i + j] != pad[j]) {
770                                        match = false;
771                                        break;
772                }
773                        }
774                        if (!match) continue;
775                        byte[] userPassword = new byte[i];
776                        System.arraycopy(userPad, 0, userPassword, 0, i);
777                        return userPassword;
778                }
779                return userPad;
780        }
781}