⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 standardsecurity.cs

📁 PDF文件格式解析库源代码
💻 CS
字号:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Security.Cryptography;
using QiHe.CodeLib;

namespace AnotherPDFLib
{
    /// <summary>
    /// Standard Security Handler, the built-in password-based security handler.
    /// </summary>
    public class StandardSecurity
    {
        /// <summary>
        /// compute the encryption key if password is valid
        /// </summary>
        /// <param name="password">either user password or owner password</param>
        /// <param name="usercheck"></param>
        /// <param name="ownercheck"></param>
        /// <param name="permission"></param>
        /// <param name="fileID"></param>
        /// <param name="encryptMetadata"></param>
        /// <param name="revision"></param>
        /// <param name="keyLength"></param>
        /// <returns></returns>
        public static byte[] ComputeKey(
            string password,
            byte[] usercheck,
            byte[] ownercheck,
            uint permission,
            byte[] fileID,
            bool encryptMetadata,
            int revision,
            int keyLength)
        {
            byte[] passwordBytes = PadPassword(password);

            byte[] key = ComputeKey(passwordBytes, ownercheck, permission, fileID, encryptMetadata, revision, keyLength);

            if (!CheckPassword(usercheck, key, fileID, revision))
            {
                //guess the password is owner password
                passwordBytes = ComputeUserPasswordFromOwnerPassword(ownercheck, password, keyLength, revision);
                key = ComputeKey(passwordBytes, ownercheck, permission, fileID, encryptMetadata, revision, keyLength);
                if (!CheckPassword(usercheck, key, fileID, revision))
                {
                    throw new Exception("password is invalid.");
                }
            }
            return key;
        }

        public static byte[] CreateKey(
            string password,
            byte[] ownercheck,
            uint permission,
            byte[] fileID,
            bool encryptMetadata,
            int revision,
            int keyLength)
        {
            byte[] passwordBytes = PadPassword(password);

            byte[] key = ComputeKey(passwordBytes, ownercheck, permission, fileID, encryptMetadata, revision, keyLength);

            return key;
        }


        static byte[] PaddingBytes = new byte[32] {
	0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08, 
	0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A};


        /// <summary>
        /// Algorithm 3.2 Computing an encryption key
        /// </summary>
        private static byte[] ComputeKey(byte[] passwordBytes, byte[] ownercheckBytes, uint permission, byte[] fileID, bool encryptMetadata, int revision, int keyLength)
        {
            MemoryStream data = new MemoryStream();
            BinaryWriter writer = new BinaryWriter(data);

            //Pad or truncate the password string to exactly 32 bytes.
            writer.Write(passwordBytes);

            //Pass the value of the encryption dictionary's O entry to the MD5 hash function.
            writer.Write(ownercheckBytes);

            //Treat the value of the P entry as an unsigned 4-byte integer and
            //pass these bytes to the MD5 hash function, low-order byte first.
            byte[] permissionBytes = BitConverter.GetBytes(permission);
            writer.Write(permissionBytes);

            //Pass the first element of the file’s file identifier array.
            writer.Write(fileID);

            //If document metadata is not being encrypted, pass 4 bytes with the value 0xFFFFFFFF
            if (!encryptMetadata)
            {
                writer.Write(0xFFFFFFFF);
            }
            writer.Flush();
            data.Position = 0;

            //Finish the hash.
            System.Security.Cryptography.MD5 md5 = new MD5CryptoServiceProvider();
            byte[] hash = md5.ComputeHash(data);

            //(Revision 3 or greater) Do the following 50 times:
            if (revision >= 3)
            {
                for (int i = 0; i < 50; i++)
                {
                    hash = md5.ComputeHash(hash, 0, keyLength);
                }
            }

            //Set the encryption key to the first n bytes of the output from the final MD5 hash
            return CreateKey(hash, keyLength);
        }

        private static byte[] CreateKey(byte[] hash, int keyLength)
        {
            if (keyLength == hash.Length)
            {
                return hash;
            }
            else
            {
                byte[] key = new byte[keyLength];
                Array.Copy(hash, key, keyLength);
                return key;
            }
        }

        private static byte[] PadPassword(string password)
        {
            byte[] passwordBytes = new byte[32];
            byte[] chars = Encoding.ASCII.GetBytes(password);
            int pwdlen = chars.Length;
            if (pwdlen >= 32)
            {
                Array.Copy(chars, passwordBytes, 32);
            }
            else
            {
                Array.Copy(chars, passwordBytes, pwdlen);
                Array.Copy(PaddingBytes, 0, passwordBytes, pwdlen, 32 - pwdlen);
            }
            return passwordBytes;
        }

        /// <summary>
        /// Algorithm 3.3 Computing the encryption dictionary's O (owner password) value
        /// </summary>
        /// <param name="userpassword"></param>
        /// <param name="ownerpassword"></param>
        /// <param name="key"></param>
        /// <param name="fileID"></param>
        /// <param name="revision"></param>
        /// <returns></returns>
        public static byte[] CreateOwnerCheck(
            string userpassword,
            string ownerpassword,
            int keyLength,
            int revision)
        {
            byte[] key = ComputeOwnerKey(userpassword, ownerpassword, keyLength, revision);

            byte[] ownercheck = RC4.EncryptDecrypt(PadPassword(userpassword), key);

            //(Revision 3 or greater) Do the following 19 times:
            if (revision >= 3)
            {
                for (int i = 1; i <= 19; i++)
                {
                    byte[] newkey = XOR(key, i);
                    ownercheck = RC4.EncryptDecrypt(ownercheck, newkey);
                }
            }
            return ownercheck;
        }

        private static byte[] ComputeOwnerKey(string userpassword, string ownerpassword, int keyLength, int revision)
        {
            byte[] passwordBytes = ownerpassword == null ?
                PadPassword(userpassword) : PadPassword(ownerpassword);
            System.Security.Cryptography.MD5 md5 = new MD5CryptoServiceProvider();
            byte[] hash = md5.ComputeHash(passwordBytes);

            //(Revision 3 or greater) Do the following 50 times:
            if (revision >= 3)
            {
                for (int i = 0; i < 50; i++)
                {
                    hash = md5.ComputeHash(hash);
                }
            }

            //4. Create an RC4 encryption key
            return CreateKey(hash, keyLength);
        }

        /// <summary>
        /// Computing the encryption dictionary's U (user password) value
        /// Algorithm 3.4 (Revision 2)
        /// Algorithm 3.5 (Revision 3 or greater)
        /// </summary>
        /// <param name="key"></param>
        /// <param name="fileID"></param>
        /// <param name="revision"></param>
        /// <returns></returns>
        public static byte[] CreateUserCheck(byte[] key, byte[] fileID, int revision)
        {
            if (revision == 2)
            {
                return RC4.EncryptDecrypt(PaddingBytes, key);
            }
            else
            {
                byte[] data = new byte[PaddingBytes.Length + fileID.Length];
                PaddingBytes.CopyTo(data, 0);
                fileID.CopyTo(data, PaddingBytes.Length);
                System.Security.Cryptography.MD5 md5 = new MD5CryptoServiceProvider();
                byte[] hash = md5.ComputeHash(data);
                hash = RC4.EncryptDecrypt(hash, key);

                for (int i = 1; i <= 19; i++)
                {
                    byte[] newkey = XOR(key, i);
                    hash = RC4.EncryptDecrypt(hash, newkey);
                }

                byte[] usercheck = new byte[32];
                hash.CopyTo(usercheck, 0);
                SecretKeys.GenerateKey(16).CopyTo(usercheck, 16);
                return usercheck;
            }
        }

        private static byte[] XOR(byte[] key, int n)
        {
            byte[] newkey = new byte[key.Length];
            for (int i = 0; i < key.Length; i++)
            {
                newkey[i] = (byte)(key[i] ^ n);
            }
            return newkey;
        }

        /// <summary>
        /// Algorithm 3.6 Authenticating the user password
        /// </summary>
        /// <param name="usercheck"></param>
        /// <param name="key"></param>
        /// <param name="fileID"></param>
        /// <param name="revision"></param>
        /// <returns></returns>
        private static bool CheckPassword(byte[] usercheck, byte[] key, byte[] fileID, int revision)
        {
            byte[] usercheckComputed = CreateUserCheck(key, fileID, revision);
            if (revision == 2)
            {
                return Algorithm.ArrayEqual(usercheck, usercheckComputed, 32);
            }
            else
            {
                return Algorithm.ArrayEqual(usercheck, usercheckComputed, 16);
            }
        }

        /// <summary>
        /// Algorithm 3.7 Authenticating the owner password
        /// </summary>
        /// <returns></returns>
        private static byte[] ComputeUserPasswordFromOwnerPassword(
            byte[] ownercheck,
            string ownerpassword,
            int keyLength,
            int revision)
        {
            byte[] key = ComputeOwnerKey("", ownerpassword, keyLength, revision);
            byte[] userpassword = ownercheck;
            if (revision == 2)
            {
                userpassword = RC4.EncryptDecrypt(ownercheck, key);
            }
            else
            {
                for (int i = 19; i >= 0; i--)
                {
                    byte[] newkey = XOR(key, i);
                    userpassword = RC4.EncryptDecrypt(userpassword, newkey);
                }
            }
            return userpassword;
        }
    }
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -