Добавление LTV в подпись делает PDF недействительным с использованием C # - PullRequest
1 голос
/ 29 апреля 2019

После добавления LTV к цифровой подписи он показывает, что документ изменился.

После получения ссылки из этой очереди: После подписи сертификации LTV в PDF-файле отображается «Документ был изменен»

Я внес изменения в свой код. Он отлично работает со всем документом, но для этого документа: https://www.sendspace.com/file/3ulwn7 - показывает недопустимую подпись.

мы также используем службу подписи документов из глобального знака длятак же.

Ниже код для добавления LTV:

public void AddLtv(string src, string dest, IOcspClient ocsp, ICrlClient crl, ITSAClient tsa)
{
    using (PdfReader r = new PdfReader(src))
    {
        using (FileStream fos =new FileStream(dest,FileMode.CreateNew))
        {
            PdfStamper stp = new PdfStamper(r, fos, '\0', true);
            LtvVerification v = stp.LtvVerification;
            AcroFields fields = stp.AcroFields;
            List<String> names = fields.GetSignatureNames();
            String sigName = names[names.Count - 1];
            PdfPKCS7 pkcs7 = fields.VerifySignature(sigName);
            if (pkcs7.IsTsp)
            {
                v.AddVerification(sigName, ocsp, crl,
                        LtvVerification.CertificateOption.SIGNING_CERTIFICATE,
                        LtvVerification.Level.OCSP_CRL,
                        LtvVerification.CertificateInclusion.NO);
            }
            else
            {
                foreach (var name in names)
                {
                    v.AddVerification(name, ocsp, crl,
                            LtvVerification.CertificateOption.WHOLE_CHAIN,
                            LtvVerification.Level.OCSP_CRL,
                            LtvVerification.CertificateInclusion.NO);
                }
            }
            stp.Close();
        }
    }
}

Редактировать: Я думаю, что способ работы с PDF в коде вызывает проблемы при чтении / записи PDF.Каким-то образом я не смог получить валидатор для pdf, который мог бы идентифицировать проблему, о которой @mkl рассказал о перекрестных ссылках в pdf.Тем не менее, я делюсь своим кодом, как показано ниже, если есть какие-либо проблемы в работе PDF.Помощь будет оценена.

Старая подпись этого pdf становится недействительной, когда я добавляю новую.и если я добавлю LTV, то он будет недействительным даже для единственной подписи.

PDF без подписи: https://www.sendspace.com/file/n0ckem

PDF с подписью без LTV URL: https://www.sendspace.com/file/t1gwp9

PDF с подписью и одиночным знаком LTV: https://www.sendspace.com/file/ba8leq

Подписанный pdf с двумя знаками LTV: https://www.sendspace.com/file/6b53z1

Ниже код для создания пустого контейнера и добавления подписи:

private async Task<string> SignPdf(string ocspResponse, string cert, string unsignedPdf, DocumentShapeModel annotations, string caCertraw, int pageHeight, UserProfileModel user)
{
    var trustedSignedpdf = Path.Combine(_env.WebRootPath, "TempPath", annotations.userId, annotations.Id.ToString(), "trustedSignedpdf.pdf");
    if (!Directory.Exists(Path.GetDirectoryName(trustedSignedpdf)))
    {
        Directory.CreateDirectory(trustedSignedpdf);
    }

    var tempPdf = Path.Combine(_env.WebRootPath, "TempPath", annotations.userId, annotations.Id.ToString());
    if (!Directory.Exists(tempPdf))
    {
        Directory.CreateDirectory(tempPdf);
    }
    tempPdf = Path.Combine(tempPdf, "tempSignedpdfglobal.pdf");
    string finalsignedPdf = trustedSignedpdf;
    var ocsp = new OcspClientBouncyCastle();
    byte[] oc2 = Convert.FromBase64String(oc1);
    OcspResp ocspResp = new OcspResp(oc2);
    BasicOcspResp basicResp = (BasicOcspResp)ocspResp.GetResponseObject();
    byte[] oc = basicResp.GetEncoded();

    bool check = false;
    string hexencodedDigest = null;
    PdfPKCS7 sgn = null;
    byte[] hash = null;
    Org.BouncyCastle.X509.X509Certificate[] chain = new Org.BouncyCastle.X509.X509Certificate[2];
    var cer = new Org.BouncyCastle.X509.X509CertificateParser()
        .ReadCertificate((new X509Certificate2(cert)).GetRawCertData());
    chain[0] = cer;
    var caCert = new Org.BouncyCastle.X509.X509CertificateParser()
        .ReadCertificate((new X509Certificate2(caCertraw)).GetRawCertData());
    chain[1] = caCert;
    while (!check)
    {
        PdfReader.unethicalreading = true;
        //create empty signature
        using (PdfReader reader = new PdfReader(unsignedPdf))
        {
            using (FileStream os = File.OpenWrite(tempPdf))
            {
                PdfStamper pdfStamper = PdfStamper.CreateSignature(reader, os, '\0', null, true);
                PdfSignatureAppearance signatureAppearance = pdfStamper.SignatureAppearance;

                // Sets Signature Appearance

                signatureAppearance.Certificate = chain[0];
                signatureAppearance.CertificationLevel = PdfSignatureAppearance.NOT_CERTIFIED;
                signatureAppearance.Reason = "E Signed by " + user.FirstName + " " + user.LastName + " (" + user.Email + ").";

                signatureAppearance.Acro6Layers = false;
                signatureAppearance.Layer4Text = PdfSignatureAppearance.questionMark;

                float shapeH = annotations.IsResponsive == true ? annotations.h : ((annotations.h * 72 / 150) / (float)Convert.ToDouble(annotations.ratio)); 
                float shapeX = annotations.IsResponsive == true ? annotations.x : ((annotations.x * 72 / 150) / (float)Convert.ToDouble(annotations.ratio)); 
                float shapeY = annotations.IsResponsive == true ? annotations.y : ((annotations.y * 72 / 150) / (float)Convert.ToDouble(annotations.ratio)); 
                float shapeW = annotations.IsResponsive == true ? annotations.w : ((annotations.w * 72 / 150) / (float)Convert.ToDouble(annotations.ratio)); 

                double yaxis = (float)Convert.ToDouble(pageHeight) - (shapeH + shapeY);

                // Sets Layer2 text and acro6layers

                signatureAppearance.Layer2Text = " "; //Left blank so that it do not overwrite Esignature.
                signatureAppearance.SetVisibleSignature(new iTextSharp.text.Rectangle((int)(shapeX), (int)yaxis, (int)(shapeX) + (int)shapeW, (int)yaxis + (int)shapeH), annotations.p, annotations.Id.ToString());

                IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);

                MakeSignature.SignExternalContainer(signatureAppearance, external, 8192);

                Stream data = signatureAppearance.GetRangeStream();
                string hashAlgorithm = "SHA256";

                sgn = new PdfPKCS7(null, chain, hashAlgorithm, false);
                hash = DigestAlgorithms.Digest(data, hashAlgorithm);
                byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, oc, null, CryptoStandard.CADES);

                //create sha256 message digest
                using (SHA256.Create())
                {
                    sh = SHA256.Create().ComputeHash(sh);
                }

                //create hex encoded sha256 message digest
                hexencodedDigest = new BigInteger(1, sh).ToString(16);
                hexencodedDigest = hexencodedDigest.ToUpper();
                if (hexencodedDigest.Length == 64)
                {
                    check = true;
                }
            }
        }
    }

    var identityGetResult = await IdentityGet(_appConfiguration.TrustedSignSettings.Url + "/identity", AccessToken, IdentityJson, hexencodedDigest);

    //decode hex
    byte[] dsg = FromHex(identityGetResult);

    //include signature on PDF
    sgn.SetExternalDigest(dsg, null, "RSA");

    //create TimeStamp Client
    ITSAClient tsc = new DssClient(AccessToken, _env, _appConfiguration.TrustedSignSettings.Url);

    //byte[] ocspResponse = ocsp.GetEncoded(chain[0],chain[chain.Length -1], CertificateUtil.GetCRLURL(chain[0]));
    //Collection<byte[]> crlBytes = CertificateUtil.fetchCrlBytes(x509certificate, chain);

    byte[] encodedpkcs7 = sgn.GetEncodedPKCS7(hash, tsc, oc, null, CryptoStandard.CADES);
    //adds PKCS7 format Signature on empty signature container
    CreateSignature(tempPdf, finalsignedPdf, annotations.Id.ToString(), encodedpkcs7);

    var finaltrustedSignedpdf = Path.Combine(_env.WebRootPath, "TempPath", annotations.userId, annotations.Id.ToString());
    if (!Directory.Exists(finaltrustedSignedpdf))
    {
        Directory.CreateDirectory(finaltrustedSignedpdf);
    }
    finaltrustedSignedpdf = Path.Combine(finaltrustedSignedpdf, "FinaltrustedSignedpdf.pdf");

    //adds LTV to signed document
    AddLtv(finalsignedPdf, finaltrustedSignedpdf, ocsp, new CrlClientOnline(), tsc);
    return finaltrustedSignedpdf;
}

Для создания подписи

public void CreateSignature(string src, string dest, string fieldname, byte[] sig)
{
    using (PdfReader reader = new PdfReader(src))
    {
        using (FileStream os = File.OpenWrite(dest))
        {
            IExternalSignatureContainer external = new MyExternalSignatureContainer(sig);
            MakeSignature.SignDeferred(reader, fieldname, os, external);
        }
    }
}

1 Ответ

1 голос
/ 01 мая 2019

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

Вы должны попросить источник этого документа предоставить версию без этой ошибки

Подробности

Таблица перекрестных ссылок первой, неподписанной редакции документа выглядит следующим образом:

xref
0 55
0000000000 65535 f
0000000018 00000 n
0000000164 00000 n
0000000216 00000 n
0000000554 00000 n
0000003363 00000 n
0000003529 00000 n
0000003764 00000 n
0000003815 00000 n
0000003866 00000 n
0000004038 00000 n
0000004279 00000 n
0000004439 00000 n
0000004662 00000 n
0000004792 00000 n
0000004818 00000 n
0000004991 00000 n
0000005061 00000 n
0000005297 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000005466 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000006188 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000006236 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
54 18
0000006284 00000 n
0000006350 00000 n
0000006648 00000 n
0000171077 00000 n
0000171435 00000 n
0000171726 00000 n
0000171973 00000 n
0000323100 00000 n
0000323123 00000 n
0000324290 00000 n
0000324333 00000 n
0000324715 00000 n
0000326153 00000 n
0000328056 00000 n
0000328093 00000 n
0000328132 00000 n
0000328214 00000 n
0000328377 00000 n

Как видите, он состоит из двух подразделов: первый для 55 объектов, начинающихся с 0, второй из 18 объектов, начинающихся с 54.

Это недопустимо по двум причинам:

  • Прежде всего (как уже было объяснено в двух ответах, упомянутых выше) таблица перекрестных ссылок начальной редакции PDF должна состоять только из одного раздела!

    Для файла, который никогда не обновлялся постепенно, раздел перекрестных ссылок должен содержать только один подраздел, чья нумерация объектов начинается с 0.

    (ISO 32000-1 и ISO 32000-2, в обоих случаях раздел 7.5.4 «Таблица перекрестных ссылок»)

  • Кроме того, эта таблица перекрестных ссылок имеет две записи для одного и того же объекта, причем последняя запись первого подраздела и первая запись второго подраздела относятся к объекту 54. Это также запрещено:

    У данного номера объекта не должно быть записи в более чем одном подразделе в пределах одного раздела.

    (там же)

В зависимости от деталей соответствующего кода это может или не может привести к произвольным проблемам при обработке PDF с некоторым процессором PDF, например, Adobe Acrobat Reader.

Ваше редактирование

При редактировании вы поделились несколькими файлами. В частности вы поделились

Согласно вашему коду вы применяете все изменения в режиме добавления. Таким образом, каждый из трех последних файлов должен состоять из первого файла VeriFinger_SDK_Brochure_2017-12-27.pdf и некоторых добавленных данных. Но это не тот случай, на самом деле все три последних файла * на 1091 * короче , чем первый. Таким образом, я должен предположить, что этот первый файл сначала каким-то образом обрабатывается, а затем подписывается.

Теперь, глядя на таблицу перекрестных ссылок "исходного файла" VeriFinger_SDK_Brochure_2017-12-27.pdf (просто откройте его в средстве просмотра текста и прокрутите до конца), мы видим, что он в одном куске всего один подраздел. Он содержит ряд записей, помеченных как free , но без пробелов.

Глядя на таблицы перекрестных ссылок первых ревизий последних трех файлов, мы видим, что каждый из них разбит на несколько подразделов. По-видимому, из таблицы вычеркнуты серии записей, помеченных как free , в результате чего в таблице появилось много подразделов. Возможно, это было задумано как попытка оптимизации, но в результате получился поврежденный файл PDF.

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

Процессор PDF, который повреждает PDF

При сравнении информации документа вашего исходного файла и первоначальных версий в трех других файлах процессор PDF, обрабатывающий файл до его подписи, выглядит как Aspose.PDF для .NET 19.1 , поскольку Producer значение изменено на это.

И действительно, это кажется известной проблемой Aspose, см., Например, преобразование PDF / A-1 создает недопустимую таблицу XRef ветка на форуме бесплатной поддержки Aspose, начатая в августе 2016 года.

Он был подан в формате PDFNET-41272 и помечен как исправленный в Aspose.Pdf для .NET 17.2.0 в феврале 2017 года, но, как сообщалось,Сама ветка форума в том же месяце, она вообще не была исправлена.

Очевидно, что Aspose еще не исправил эту ошибку и все еще работает над ней.

...