private static PDFDecrypter createStandardDecrypter(
            PDFObject encryptDict,
            PDFObject documentId,
            PDFPassword password,
            Integer keyLength,
            boolean encryptMetadata,
            StandardDecrypter.EncryptionAlgorithm encryptionAlgorithm)
            throws
            PDFAuthenticationFailureException,
            IOException,
            EncryptionUnsupportedByPlatformException,
            EncryptionUnsupportedByProductException {

        if (keyLength == null) {
            keyLength = DEFAULT_KEY_LENGTH;
        }

        // R describes the revision of the security handler
        final PDFObject rObj = encryptDict.getDictRef("R");
        if (rObj == null) {
            throw new PDFParseException(
                    "No R entry present in Encrypt dictionary");
        }

        final int revision = rObj.getIntValue();
        if (revision < 2 || revision > 4) {
            throw new EncryptionUnsupportedByPlatformException(
                    "Unsupported Standard security handler revision; R=" +
                            revision);
        }

        // O describes validation details for the owner key
        final PDFObject oObj = encryptDict.getDictRef("O");
        if (oObj == null) {
            throw new PDFParseException(
                    "No O entry present in Encrypt dictionary");
        }
        final byte[] o = oObj.getStream();
        if (o.length != 32) {
            throw new PDFParseException("Expected owner key O " +
                    "value of 32 bytes; found " + o.length);
        }

        // U describes validation details for the user key
        final PDFObject uObj = encryptDict.getDictRef("U");
        if (uObj == null) {
            throw new PDFParseException(
                    "No U entry present in Encrypt dictionary");
        }
        final byte[] u = uObj.getStream();
        if (u.length != 32) {
            throw new PDFParseException(
                    "Expected user key U value of 32 bytes; found " + o.length);
        }

        // P describes the permissions regarding document usage
        final PDFObject pObj = encryptDict.getDictRef("P");
        if (pObj == null) {
            throw new PDFParseException(
                    "Required P entry in Encrypt dictionary not found");
        }

        return new StandardDecrypter(
                encryptionAlgorithm, documentId, keyLength,
                revision, o, u, pObj.getIntValue(), encryptMetadata, password);
    }