public byte[] encryptionKeyAlgorithm(String password, int keyLength) {

        if (encryptionDictionary.getRevisionNumber() < 5) {
            // Step 1:  pad the password
            byte[] paddedPassword = padPassword(password);

            // Step 2: initialize the MD5 hash function
            MessageDigest md5 = null;
            try {
                md5 = MessageDigest.getInstance("MD5");
            } catch (NoSuchAlgorithmException ex) {
                logger.log(Level.FINE, "NoSuchAlgorithmException.", ex);
            }
            // and pass in padded password from step 1
            md5.update(paddedPassword);

            // Step 3: Pass the value of the encryption dictionary's 0 entry
            byte[] bigO = Utils.convertByteCharSequenceToByteArray(
                    encryptionDictionary.getBigO());
            md5.update(bigO);

            // Step 4: treat P as an unsigned 4-byte integer
            for (int i = 0, p = encryptionDictionary.getPermissions(); i < 4; i++,
                    p >>= 8) {
                md5.update((byte) (p & 0xFF));
            }

            // Step 5: Pass in the first element of the file's file identifies array
            String firstFileID =
                    ((StringObject) encryptionDictionary.getFileID().elementAt(0)).getLiteralString();
            byte[] fileID = Utils.convertByteCharSequenceToByteArray(firstFileID);
            paddedPassword = md5.digest(fileID);

            // Step 6: If document metadata is not being encrypted, pass 4 bytes with
            // the value of 0xFFFFFFFF to the MD5 hash, Only used when R=3 and
            // encrypting.

            // Step 7: Finish Hash.

            // Step 8: Do the following 50 times: take the output from the previous
            // MD5 hash and pass it as ainput into a new MD5 hash;
            // only for R >= 3
            if (encryptionDictionary.getRevisionNumber() >= 3) {
                for (int i = 0; i < 50; i++) {
                    paddedPassword = md5.digest(paddedPassword);
                }
            }

            // Step 9: Set the encryption key to the first n bytes of the output from
            // the MD5 hash
            byte[] out = null;
            int n = 5;
            // n = 5 when R = 2
            if (encryptionDictionary.getRevisionNumber() == 2) {
                out = new byte[n];
            } else if (encryptionDictionary.getRevisionNumber() >= 3) {
                n = keyLength / 8;
                out = new byte[n];
            }
            if (n > paddedPassword.length) {
                n = paddedPassword.length;
            }

            // truncate out to the appropriate value
            System.arraycopy(paddedPassword,
                    0,
                    out,
                    0,
                    n);
            // assign instance
            encryptionKey = out;

            return out;
        }
        // algorithm 3.2a for Revision 5
        else if (encryptionDictionary.getRevisionNumber() == 5) {
            try {
                byte[] passwordBytes = Utils.convertByteCharSequenceToByteArray(password);
                if (passwordBytes == null) {
                    passwordBytes = new byte[0];
                }

                byte[] ownerPassword = Utils.convertByteCharSequenceToByteArray(
                        encryptionDictionary.getBigO());
                byte[] userPassword = Utils.convertByteCharSequenceToByteArray(
                        encryptionDictionary.getBigU());
                // To understand the algorithm below, it is necessary to treat
                // the O and U strings in the Encrypt dictionary as made up of
                // three sections. The first 32 bytes are a hash value .
                // The next 8 bytes are called the Validation Salt.
                // The final 8 bytes are called the Key Salt.
                MessageDigest md = MessageDigest.getInstance("SHA-256");
                // 3.) computing the SHA-256 hash of the UTF-8 password, 127
                // bytes if it is longer than 127 bytes.
                md.update(passwordBytes, 0, Math.min(passwordBytes.length, 127));
                // concatenated with the 8 bytes of owner Validation Salt,
                md.update(ownerPassword, 32, 8);
                // concatenated with the 48-byte U string
                md.update(userPassword, 0, 48);
                // calculate the 32 bit result
                byte[] hash = md.digest();
                // Check if the 32-byte result matches the first 32 bytes of the
                // O string, this is the owner password.
                boolean isOwnerPassword = byteCompare(hash, ownerPassword, 32);
                encryptionDictionary.setAuthenticatedOwnerPassword(isOwnerPassword);
                if (isOwnerPassword) {
                    // calculate an intermediate owner key
                    md.update(passwordBytes, 0, Math.min(passwordBytes.length, 127));
                    // concatenate  8 bytes of owner key salt
                    md.update(ownerPassword, 32, 8);
                    // concatenated with the 48-byte u string
                    md.update(userPassword, 0, 48);
                    hash = md.digest();
                    // the 32 byte hash result is the key to decrypt the 32byte
                    // oe string using AES-256 in CBC mode with no padding and an
                    // initialization vector of zero.
                    // the 32byte result is the file encryption key.
                    byte[] oePassword = Utils.convertByteCharSequenceToByteArray(
                            encryptionDictionary.getBigOE());
                    encryptionKey = AES256CBC(hash, oePassword);
                }
                // 4.)test the password against the user password.
                else {
                    // concatenate password
                    md.update(passwordBytes, 0, Math.min(passwordBytes.length, 127));
                    // concatenated with the 8 bytes of user Validation Salt
                    md.update(userPassword, 32, 8);
                    hash = md.digest();
                    // test first 32 bytes against the user string.
                    boolean isUserPassword = byteCompare(hash, userPassword, 32);
                    encryptionDictionary.setAuthenticatedUserPassword(isUserPassword);
                    if (isUserPassword) {
                        // calculate an intermediate owner key
                        md.update(passwordBytes, 0, Math.min(passwordBytes.length, 127));
                        // concatenate  8 bytes of owner key salt
                        md.update(userPassword, 40, 8);
                        hash = md.digest();
                        // the 32 byte hash result is the key to decrypt the 32byte
                        // ue string using AES-256 in CBC mode with no padding and an
                        // initialization vector of zero.
                        // the 32byte result is the file encryption key.
                        byte[] uePassword = Utils.convertByteCharSequenceToByteArray(
                                encryptionDictionary.getBigUE());
                        encryptionKey = AES256CBC(hash, uePassword);
                    } else {
                        logger.warning("User password is incorrect. ");
                    }
                }
                // 5.)Decrypt the 16-byte Perms string using AES-256 in ECB mode
                // with an initialization vector of zero and the file encryption
                // key as the key.
                byte[] perms = Utils.convertByteCharSequenceToByteArray(
                        encryptionDictionary.getPerms());
                byte[] decryptedPerms = AES256CBC(encryptionKey, perms);

                // Verify that bytes 9-11 of the result are the characters 'a', 'd', 'b'.
                if (decryptedPerms[9] != (byte) 'a' ||
                        decryptedPerms[10] != (byte) 'd' ||
                        decryptedPerms[11] != (byte) 'b') {
                    logger.warning("User password is incorrect.");
                    return null;
                }
                // Bytes 0-3 of the decrypted Perms entry, treated as a
                // little-endian integer, are the user permissions. They should
                // match the value in the P key.
                int permissions = (decryptedPerms[0] & 0xff) |
                        ((decryptedPerms[1] & 0xff) << 8) |
                        ((decryptedPerms[2] & 0xff) << 16) |
                        ((decryptedPerms[2] & 0xff) << 24);
                int pPermissions = encryptionDictionary.getPermissions();
                if (pPermissions != permissions) {
                    logger.warning("Perms and P do not match");
                }
                return encryptionKey;
            } catch (NoSuchAlgorithmException e) {
                logger.warning("Error computing the the 3.2a Encryption key.");
            }
        } else {
            logger.warning("Adobe standard Encryption R = 6 is not supported.");
        }
        return null;
    }