/**
         * Prepares everything to decrypt the document.
         *
         * @param encryption encryption dictionary, can be retrieved via
         * {@link Document#getEncryption()}
         * @param documentIDArray document id which is returned via
         * {@link org.apache.pdfbox.cos.COSDocument#getDocumentID()} (not used by
         * this handler)
         * @param decryptionMaterial Information used to decrypt the document.
         *
         * @throws IOException If there is an error accessing data. If verbose mode
         * is enabled, the exception message will provide more details why the
         * match wasn't successful.
         */
        public override void PrepareForDecryption(PdfEncryption encryption, PdfArray documentIDArray, DecryptionMaterial decryptionMaterial)
        {
            if (!(decryptionMaterial is PublicKeyDecryptionMaterial))
            {
                throw new IOException(
                          "Provided decryption material is not compatible with the document");
            }

            SetDecryptMetadata(encryption.IsEncryptMetaData);
            if (encryption.Length != 0)
            {
                this.keyLength = encryption.Length;
            }

            PublicKeyDecryptionMaterial material = (PublicKeyDecryptionMaterial)decryptionMaterial;

            try
            {
                bool foundRecipient = false;
                //Org.BouncyCastle.X509.Extension.
                X509Certificate      certificate  = material.Certificate;
                X509CertificateEntry materialCert = null;
                if (certificate != null)
                {
                    materialCert = new X509CertificateEntry(certificate);
                }

                // the decrypted content of the enveloped data that match
                // the certificate in the decryption material provided
                byte[] envelopedData = null;

                // the bytes of each recipient in the recipients array
                PdfArray array = (PdfArray)encryption.BaseDataObject.Resolve(PdfName.Recipients);
                if (array == null)
                {
                    PdfCryptFilterDictionary defaultCryptFilterDictionary = encryption.DefaultCryptFilterDictionary;
                    array = (PdfArray)defaultCryptFilterDictionary.BaseDataObject.Resolve(PdfName.Recipients);
                }
                byte[][] recipientFieldsBytes = new byte[array.Count][];
                //TODO encryption.getRecipientsLength() and getRecipientStringAt() should be deprecated

                int           recipientFieldsLength = 0;
                StringBuilder extraInfo             = new StringBuilder();
                for (int i = 0; i < array.Count; i++)
                {
                    PdfString recipientFieldString = (PdfString)array.Resolve(i);
                    byte[]    recipientBytes       = recipientFieldString.GetBuffer();

                    CmsEnvelopedData data   = new CmsEnvelopedData(recipientBytes);
                    var recipCertificatesIt = data.GetRecipientInfos().GetRecipients();
                    int j = 0;
                    foreach (RecipientInformation ri in recipCertificatesIt)
                    {
                        // Impl: if a matching certificate was previously found it is an error,
                        // here we just don't care about it
                        RecipientID rid = ri.RecipientID;
                        if (!foundRecipient && rid.Match(materialCert))
                        {
                            foundRecipient = true;
                            var privateKey = material.PrivateKey;
                            // might need to call setContentProvider() if we use PKI token, see
                            // http://bouncy-castle.1462172.n4.nabble.com/CMSException-exception-unwrapping-key-key-invalid-unknown-key-type-passed-to-RSA-td4658109.html
                            //DotNetUtilities.GetKeyPair(ri.AlgorithmIdentifier)
                            envelopedData = ri.GetContent(privateKey.Key);
                            break;
                        }
                        j++;
                        if (certificate != null)
                        {
                            extraInfo.Append('\n');
                            extraInfo.Append(j);
                            extraInfo.Append(": ");
                            if (ri is KeyTransRecipientInformation)
                            {
                                appendCertInfo(extraInfo, (KeyTransRecipientInformation)ri, certificate, materialCert);
                            }
                        }
                    }
                    recipientFieldsBytes[i] = recipientBytes;
                    recipientFieldsLength  += recipientBytes.Length;
                }
                if (!foundRecipient || envelopedData == null)
                {
                    throw new IOException("The certificate matches none of " + array.Count
                                          + " recipient entries" + extraInfo.ToString());
                }
                if (envelopedData.Length != 24)
                {
                    throw new IOException("The enveloped data does not contain 24 bytes");
                }
                // now envelopedData contains:
                // - the 20 bytes seed
                // - the 4 bytes of permission for the current user

                byte[] accessBytes = new byte[4];
                Array.Copy(envelopedData, 20, accessBytes, 0, 4);

                AccessPermission currentAccessPermission = new AccessPermission(accessBytes);
                currentAccessPermission.IsReadOnly = true;
                CurrentAccessPermission            = currentAccessPermission;

                // what we will put in the SHA1 = the seed + each byte contained in the recipients array
                byte[] sha1Input = new byte[recipientFieldsLength + 20];

                // put the seed in the sha1 input
                Array.Copy(envelopedData, 0, sha1Input, 0, 20);

                // put each bytes of the recipients array in the sha1 input
                int sha1InputOffset = 20;
                foreach (byte[] recipientFieldsByte in recipientFieldsBytes)
                {
                    Array.Copy(recipientFieldsByte, 0, sha1Input, sha1InputOffset, recipientFieldsByte.Length);
                    sha1InputOffset += recipientFieldsByte.Length;
                }

                byte[] mdResult;
                if (encryption.Version == 4 || encryption.Version == 5)
                {
                    mdResult = SHA256.Create().Digest(sha1Input);

                    // detect whether AES encryption is used. This assumes that the encryption algo is
                    // stored in the PDCryptFilterDictionary
                    // However, crypt filters are used only when V is 4 or 5.
                    PdfCryptFilterDictionary defaultCryptFilterDictionary = encryption.DefaultCryptFilterDictionary;
                    if (defaultCryptFilterDictionary != null)
                    {
                        PdfName cryptFilterMethod = defaultCryptFilterDictionary.CryptFilterMethod;
                        IsAES = PdfName.AESV2.Equals(cryptFilterMethod) || PdfName.AESV3.Equals(cryptFilterMethod);
                    }
                }
                else
                {
                    mdResult = SHA1.Create().Digest(sha1Input);
                }

                // we have the encryption key ...
                encryptionKey = new byte[this.keyLength / 8];
                Array.Copy(mdResult, 0, encryptionKey, 0, this.keyLength / 8);
            }
            catch (Exception e)
            {
                throw new IOException("", e);
            }
        }
Exemple #2
0
        /**
         * Prepares everything to decrypt the document.
         *
         * Only if decryption of single objects is needed this should be called.
         *
         * @param encryption  encryption dictionary
         * @param documentIDArray  document id
         * @param decryptionMaterial Information used to decrypt the document.
         *
         * @throws InvalidPasswordException If the password is incorrect.
         * @ If there is an error accessing data.
         */

        public override void PrepareForDecryption(PdfEncryption encryption, PdfArray documentIDArray, DecryptionMaterial decryptionMaterial)
        {
            if (!(decryptionMaterial is StandardDecryptionMaterial))
            {
                throw new IOException("Decryption material is not compatible with the document");
            }

            // This is only used with security version 4 and 5.
            if (encryption.Version >= 4)
            {
                SetStreamFilterName(encryption.StreamFilterName);
                SetStringFilterName(encryption.StreamFilterName);
            }
            SetDecryptMetadata(encryption.IsEncryptMetaData);
            StandardDecryptionMaterial material = (StandardDecryptionMaterial)decryptionMaterial;

            string password = material.Password ?? string.Empty;

            int dicPermissions = encryption.Permissions;
            int dicRevision    = encryption.Revision;
            int dicLength      = encryption.Version == 1 ? 5 : encryption.Length / 8;

            byte[] documentIDBytes = GetDocumentIDBytes(documentIDArray);

            // we need to know whether the meta data was encrypted for password calculation
            bool encryptMetadata = encryption.IsEncryptMetaData;

            byte[] userKey  = encryption.UserKey;
            byte[] ownerKey = encryption.OwnerKey;
            byte[] ue       = null, oe = null;

            var passwordCharset = Charset.ISO88591;

            if (dicRevision == 6 || dicRevision == 5)
            {
                passwordCharset = Charset.UTF8;
                ue = encryption.UserEncryptionKey;
                oe = encryption.OwnerEncryptionKey;
            }

            if (dicRevision == 6)
            {
                password = SaslPrep.SaslPrepQuery(password); // PDFBOX-4155
            }

            AccessPermission currentAccessPermission;

            if (IsOwnerPassword(passwordCharset.GetBytes(password), userKey, ownerKey,
                                dicPermissions, documentIDBytes, dicRevision,
                                dicLength, encryptMetadata))
            {
                currentAccessPermission = AccessPermission.getOwnerAccessPermission();
                CurrentAccessPermission = currentAccessPermission;

                byte[] computedPassword;
                if (dicRevision == 6 || dicRevision == 5)
                {
                    computedPassword = passwordCharset.GetBytes(password);
                }
                else
                {
                    computedPassword = GetUserPassword(passwordCharset.GetBytes(password),
                                                       ownerKey, dicRevision, dicLength);
                }

                encryptionKey =
                    ComputeEncryptedKey(
                        computedPassword,
                        ownerKey, userKey, oe, ue,
                        dicPermissions,
                        documentIDBytes,
                        dicRevision,
                        dicLength,
                        encryptMetadata, true);
            }
            else if (IsUserPassword(passwordCharset.GetBytes(password), userKey, ownerKey,
                                    dicPermissions, documentIDBytes, dicRevision,
                                    dicLength, encryptMetadata))
            {
                currentAccessPermission            = new AccessPermission(dicPermissions);
                currentAccessPermission.IsReadOnly = true;
                CurrentAccessPermission            = currentAccessPermission;

                encryptionKey = ComputeEncryptedKey(
                    passwordCharset.GetBytes(password),
                    ownerKey, userKey, oe, ue,
                    dicPermissions,
                    documentIDBytes,
                    dicRevision,
                    dicLength,
                    encryptMetadata, false);
            }
            else
            {
                throw new InvalidPasswordException("Cannot decrypt PDF, the password is incorrect");
            }

            if (dicRevision == 6 || dicRevision == 5)

            {
                ValidatePerms(encryption, dicPermissions, encryptMetadata);
            }

            if (encryption.Version == 4 || encryption.Version == 5)
            {
                // detect whether AES encryption is used. This assumes that the encryption algo is
                // stored in the PDCryptFilterDictionary
                // However, crypt filters are used only when V is 4 or 5.
                var stdCryptFilterDictionary = encryption.StdCryptFilterDictionary;

                if (stdCryptFilterDictionary != null)
                {
                    PdfName cryptFilterMethod = stdCryptFilterDictionary.CryptFilterMethod;
                    IsAES = PdfName.AESV2.Equals(cryptFilterMethod) ||
                            PdfName.AESV3.Equals(cryptFilterMethod);
                }
            }
        }