* @return byte array representing the U value for the encryption dictionary
     */
    public byte[] calculateUserPassword(String userPassword) {

        // Step 1: Create an encryption key based on the user password String,
        // as described in Algorithm 3.2
        byte[] encryptionKey = encryptionKeyAlgorithm(
                userPassword,
                encryptionDictionary.getKeyLength());

        // Algorithm 3.4 steps, 2 - 3
        if (encryptionDictionary.getRevisionNumber() == 2) {
            // Step 2: Encrypt the 32-byte padding string show in step 1, using
            // an RC4 encryption function with the encryption key from the
            // preceding step

            // 32-byte padding string
            byte[] paddedUserPassword = PADDING.clone();
            // encrypt the data
            byte[] finalData = null;
            try {
                // Use above as key for the RC4 encryption function.
                SecretKeySpec key = new SecretKeySpec(encryptionKey, "RC4");
                Cipher rc4 = Cipher.getInstance("RC4");
                rc4.init(Cipher.ENCRYPT_MODE, key);

                // finally encrypt the padding string
                finalData = rc4.doFinal(paddedUserPassword);

            } catch (NoSuchAlgorithmException ex) {
                logger.log(Level.FINE, "NoSuchAlgorithmException.", ex);
            } catch (IllegalBlockSizeException ex) {
                logger.log(Level.FINE, "IllegalBlockSizeException.", ex);
            } catch (BadPaddingException ex) {
                logger.log(Level.FINE, "BadPaddingException.", ex);
            } catch (NoSuchPaddingException ex) {
                logger.log(Level.FINE, "NoSuchPaddingException.", ex);
            } catch (InvalidKeyException ex) {
                logger.log(Level.FINE, "InvalidKeyException.", ex);
            }
            // Step 3: return the result of step 2 as the value of the U entry
            return finalData;
        }
        // algorithm 3.5 steps, 2 - 6
        else if (encryptionDictionary.getRevisionNumber() >= 3 &&
                encryptionDictionary.getRevisionNumber() < 5) {
            // Step 2: Initialize the MD5 hash function and pass the 32-byte
            // padding string shown in step 1 of Algorithm 3.2 as input to
            // this function
            byte[] paddedUserPassword = PADDING.clone();
            MessageDigest md5 = null;
            try {
                md5 = MessageDigest.getInstance("MD5");
            } catch (NoSuchAlgorithmException e) {
                logger.log(Level.FINE, "MD5 digester could not be found", e);
            }
            // and pass in padded password 32-byte padding string
            md5.update(paddedUserPassword);

            // Step 3: Pass the first element of the files identify array to the
            // hash function and finish the hash.
            String firstFileID = ((StringObject) encryptionDictionary.getFileID().elementAt(0)).getLiteralString();
            byte[] fileID = Utils.convertByteCharSequenceToByteArray(firstFileID);
            byte[] encryptData = md5.digest(fileID);

            // Step 4: Encrypt the 16 byte result of the hash, using an RC4
            // encryption function with the encryption key from step 1
            //System.out.println("R=3 " + encryptData.length);

            // The final data should be 16 bytes long
            // currently no checking for this.

            try {
                // Use above as key for the RC4 encryption function.
                SecretKeySpec key = new SecretKeySpec(encryptionKey, "RC4");
                Cipher rc4 = Cipher.getInstance("RC4");
                rc4.init(Cipher.ENCRYPT_MODE, key);

                // finally encrypt the padding string
                encryptData = rc4.update(encryptData);

                // Step 5: Do the following 19 times: Take the output from the previous
                // invocation of the RC4 function and pass it as input to a new
                // invocation of the function; use an encryption key generated by taking
                // each byte of the encryption key in step 4 and performing an XOR
                // operation between that byte and the single-byte value of the
                // iteration counter

                // key to be made on each interaction
                byte[] indexedKey = new byte[encryptionKey.length];
                // start the 19? interactions
                for (int i = 1; i <= 19; i++) {

                    // build new key for each i xor on each byte
                    for (int j = 0; j < encryptionKey.length; j++) {
                        indexedKey[j] = (byte) (encryptionKey[j] ^ (byte) i);
                    }
                    // create new key and init rc4
                    key = new SecretKeySpec(indexedKey, "RC4");
                    rc4.init(Cipher.ENCRYPT_MODE, key);
                    // encrypt the old data with the new key
                    encryptData = rc4.update(encryptData);
                }

            } catch (NoSuchAlgorithmException ex) {
                logger.log(Level.FINE, "NoSuchAlgorithmException.", ex);
            } catch (NoSuchPaddingException ex) {
                logger.log(Level.FINE, "NoSuchPaddingException.", ex);
            } catch (InvalidKeyException ex) {
                logger.log(Level.FINE, "InvalidKeyException.", ex);
            }
            // Step 6: Append 16 bytes of arbitrary padding to the output from
            // the final invocation of the RC4 function and return the 32-byte
            // result as the value of the U entry.
            byte[] finalData = new byte[32];
            System.arraycopy(encryptData, 0, finalData, 0, 16);
            System.arraycopy(PADDING, 0, finalData, 16, 16);

            return finalData;
        }else {
            return null;
        }
    }