Открытие защищенного паролем pdf файла с iTextSharp

Я делаю приложение, которое должно отображать PDF файлы с паролем. Это мой код:

protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        try
        {
            string filePath = Request.QueryString["filePath"];
            if (filePath.ToUpper().EndsWith("PDF"))
            {
                copyPDF(filePath);
            }
        }
        catch
        {
            string message = "<script language='Javascript'>alert('File Not Found! Call Records Department for verification. ')</script>";
            ScriptManager.RegisterStartupScript(Page, this.GetType(), message, message, false);
        }
    }
}
public void copyPDF(string filePath)
{
    iTextSharp.text.pdf.RandomAccessFileOrArray ra = new iTextSharp.text.pdf.RandomAccessFileOrArray(Server.MapPath(ResolveUrl(filePath)));
    if (ra != null)
    {
        System.IO.MemoryStream ms = new System.IO.MemoryStream();
        byte[] password = System.Text.ASCIIEncoding.ASCII.GetBytes("Secretinfo");
        iTextSharp.text.pdf.PdfReader thepdfReader = new iTextSharp.text.pdf.PdfReader(ra, password);
        int pages = thepdfReader.NumberOfPages;
        iTextSharp.text.Document pdfDoc = new iTextSharp.text.Document();
        iTextSharp.text.pdf.PdfCopy pdfCopy = new iTextSharp.text.pdf.PdfCopy(pdfDoc, ms);

        pdfDoc.Open();
        int i = 0;
        while (i < pages)
        {
            pdfCopy.AddPage(pdfCopy.GetImportedPage(thepdfReader, i + 1));
            i += 1;
        }
        pdfDoc.Close();
        Byte[] byteInfo = ms.ToArray();
        Response.Clear();
        Response.ContentType = "application/pdf";
        Response.AddHeader("content-length", byteInfo.Length.ToString());
        Response.BinaryWrite(byteInfo);
        Response.Flush();
        Response.End();
    }
}

В моем коде нет проблем с открытием pdf файлов без пароля, но он не может открыть PDF файлы с паролем, даже если пароль предоставляется. Вместо этого приложение выполняет catch. Что, похоже, не так с моим кодом?

ИЗМЕНИТЬ: Я удалил Catch, чтобы увидеть созданное исключение.

Сведения об исключении: System.ArgumentException: PdfReader не открыт с паролем владельца

В нем говорится, что источником ошибки является строка 51.

Line 49:    while (i < pages)
Line 50:    {
Line 51:         pdfCopy.AddPage(pdfCopy.GetImportedPage(thepdfReader, i + 1));
Line 52:         i += 1;
Line 53:    }

Ответы

Ответ 1

Для определенных операций с зашифрованными документами iText (Sharp) требует, чтобы документ не просто был открыт с помощью пароля пользователя, а вместо него с паролем владельца. Это соответствует определению этих паролей в спецификации PDF:

Допускаются ли дополнительные операции в расшифрованном документе, зависит от того, какой пароль (если есть) был предоставлен при открытии документа и при любых ограничениях доступа, которые были указаны при создании документа:

  • Открытие документа с правильным паролем владельца должно позволить полному (владельцу) доступу к документу. Этот неограниченный доступ включает в себя возможность изменения паролей документов и прав доступа.
  • Открытие документа с правильным паролем пользователя (или открытие документа с паролем по умолчанию) должно позволить выполнять дополнительные операции в соответствии с правами доступа пользователя, указанными в словаре шифрования документов.

(раздел 7.6.3.1 в ISO 32000-1)

iText (Sharp) в настоящее время не проверяет подробные разрешения доступа пользователей, указанные в словаре шифрования документов, но вместо этого всегда требует пароль владельца для операций, требующих определенных разрешений, и одним из них является копирование целых страниц из документа.

Было сказано, что разработчики iText (Sharp) очень хорошо знают (из-за многих таких вопросов)

  • что пользователи iText (Sharp) могут иметь право выполнять такие операции, даже без пароля владельца, из-за указанных выше прав доступа пользователей, указанных в словаре шифрования документов,
  • что есть много файлов PDF, к которым их соответствующие владельцы применяли пароль владельца (чтобы предотвратить неправильное использование другими), а затем забыли его (или используя случайно сгенерированный, который никогда не знал его для начала) и
  • что iText (Sharp) (будучи открытым исходным кодом) может быть легко исправлен любым, кто не соблюдает различия между паролем пользователя и владельца.

Чтобы пользователи могли делать то, на что они имеют право, и чтобы предотвратить распространение исправленных копий библиотеки, iText (Sharp) содержит переопределение для этого теста в классе PdfReader:

/**
 * The iText developers are not responsible if you decide to change the
 * value of this static parameter.
 * @since 5.0.2
 */
public static bool unethicalreading = false;

Таким образом, установив

PdfReader.unethicalreading = true;

вы глобально переопределите этот механизм проверки разрешений.

Пожалуйста, уважайте права авторов PDF и используйте только это переопределение, если вы действительно имеете право выполнять рассматриваемые операции.

Ответ 2

Я применил это обходное решение, и оно работает:

private void fixIssue(PdfReader pdfReader) throws Exception {
        Field f = pdfReader.getClass().getDeclaredField("ownerPasswordUsed");
        f.setAccessible(true);
        f.setBoolean(pdfReader, true);

}