Цифровая подпись смарт-карты C # PKCS7 - документ был изменен или поврежден с момента его подписания - PullRequest
0 голосов
/ 25 июня 2018

Я пытаюсь подписать pdf-файл с помощью своей смарт-карты (USB-токена), но при открытии подписанного pdf-файла в Adobe возникает ошибка "Document has been altered or corrupted since it was signed".Ошибка не настолько описательна, и я не уверен, где искать, потому что код кажется мне хорошим, но, очевидно, это не так.

Код, который я использую:

var signer = smartCardManager.getSigner("myTokenPassword");
var toBeSignedHash = GetHashOfPdf(File.ReadAllBytes(@"xxx\pdf.pdf"), cert.asX509Certificate2().RawData, "dsa", null, false);
var signature = signer.sign(toBeSignedHash);
var signedPdf = EmbedSignature(cert.getBytes(), signature);
File.WriteAllBytes(@"xxx\signedpdf.pdf", signedPdf);

public byte[] GetHashOfPdf(byte[] unsignedFile, byte[] userCertificate, string signatureFieldName, List<float> location, bool append)
{
    byte[] result = null;

    var chain = new List<Org.BouncyCastle.X509.X509Certificate>
    {
        Org.BouncyCastle.Security.DotNetUtilities.FromX509Certificate(new X509Certificate2(userCertificate))
    };
    Org.BouncyCastle.X509.X509Certificate certificate = chain.ElementAt(0);
    using (PdfReader reader = new PdfReader(unsignedFile))
    {
        using (var os = new MemoryStream())
        {
            PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0', null, append);
            PdfSignatureAppearance appearance = stamper.SignatureAppearance;
            appearance.SetVisibleSignature(new iTextSharp.text.Rectangle(0,0,0,0), 1, signatureFieldName);
            appearance.Certificate = certificate;
            IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
            MakeSignature.SignExternalContainer(appearance, external, 8192);
            Stream data = appearance.GetRangeStream();
            byte[] hash = DigestAlgorithms.Digest(data, "SHA256");
            var signatureContainer = new PdfPKCS7(null, chain, "SHA256", false);
            byte[] signatureHash = signatureContainer.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
            result = DigestAlgorithms.Digest(new MemoryStream(signatureHash), "SHA256");
            this.hash = hash;
            this.os = os.ToArray();
            File.WriteAllBytes(@"xxx\temp.pdf", this.os);
        }
    }

    return result;
}

public byte[] EmbedSignature(byte[] publicCert, byte[] sign)
{
    var chain = new List<Org.BouncyCastle.X509.X509Certificate>
    {
        Org.BouncyCastle.Security.DotNetUtilities.FromX509Certificate(new X509Certificate2(publicCert))
    };
    var signatureContainer = new PdfPKCS7(null, chain, "SHA256", false);
    using (var reader = new PdfReader(this.os))
    {
        using (var os2 = new MemoryStream())
        {
            signatureContainer.SetExternalDigest(sign, null, "RSA");
            byte[] encodedSignature = signatureContainer.GetEncodedPKCS7(this.hash, null, null, null, CryptoStandard.CMS);
            IExternalSignatureContainer external = new MyExternalSignatureContainer(encodedSignature);
            MakeSignature.SignDeferred(reader, "dsa", os2, external);
            return os2.ToArray();
        }
    }
}

PDF-файл, который я пытаюсь подписать, this .

. Temp pdf-файл, который создается после добавления полей подписи, this .

ПодписаноPDF-файл это .

Base64 формат подписанного хэша: klh6CGp7DUzayt62Eusiqjr1BFCcTZT4XdgnMBq7QeY=

Base64 формат подписи: Uam/J6W0YX99rVP4M9mL9Lg9l6YzC2yiR4OtJ18AH1PtBVaNPteT3oPS7SUc+6ak2LfijgJ6j1RgdLamodDPKl/0E90kbBenry+/g1Ttd1bpO8lqTn1PWJU2TxeGHwyRyaFBOUga2AxpErIHrwxfuKCBcodB7wvAqRjso0jovnyP/4DluyOPm97QWh4na0S+rtUWOdqVmKGOuGJ3sBXuk019ewpvFcqWBX4Mvz7IKV56wcxQVQuJLCiyXsMXoazwyDCvdteaDz05K25IVwgEEjwLrppnc/7Ue9a9KVadFFzXWXfia7ndmUCgyd70r/Z+Oviu9MIAZL8GuTpkD7fJeA==

1 Ответ

0 голосов
/ 27 июня 2018

Я использую шестнадцатеричное кодирование байтовых массивов здесь.Ваш хэш в кодировке base64

klh6CGp7DUzayt62Eusiqjr1BFCcTZT4XdgnMBq7QeY=

в шестнадцатеричной кодировке равен

92587A086A7B0D4CDACADEB612EB22AA3AF504509C4D94F85DD827301ABB41E6

Короче

Ваш код хеширует подписанныйАтрибуты дважды.Просто не хэшируйте байты, возвращаемые signatureContainer.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS) в GetHashOfPdf, а вместо этого используйте сами байты атрибута в качестве возвращаемого значения.

Подробно

Анализ подписи в вашем примере PDF этополучается, что

  • действительно хэш подписанных атрибутов равен

    92587A086A7B0D4CDACADEB612EB22AA3AF504509C4D94F85DD827301ABB41E6
    
  • , но хеш в зашифрованном RSA DigestInfo объектеподпись

    1DC7CAA50D88243327A9D928D5FB4F1A61CBEFF9E947D393DDA705BD61B67F25
    
  • , которая оказывается хэшем вышеупомянутого хэша подписанных атрибутов.

Таким образом, ваш

var signature = signer.sign(toBeSignedHash);

вызов, кажется, снова хэширует значение toBeSignedHash.

Самое простое решение - заменить

byte[] signatureHash = signatureContainer.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
result = DigestAlgorithms.Digest(new MemoryStream(signatureHash), "SHA256");

на

result = signatureContainer.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);

в GetHashOfPdf только для хеширования signer.sign.

Анализ таких проблем

В комментарии вы спросили

как вы это выясниливсе это:)?

Ну, это не первый вопрос с настраиваемым процессом подписывания iText, приводящим к ошибкам илипо крайней мере нежелательные профили.

В ходе анализа этих вопросов первым шагом обычно является извлечение встроенного контейнера подписи и проверка его в средстве просмотра ASN.1.

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

Если были некоторые переменные данные (например,атрибут времени подписи), кандидатом для причины проблемы было бы то, что вы создали дважды подписанные атрибуты, один раз явно в GetHashOfPdf, один раз неявно в EmbedSignature, с различными значениями для переменных данных.Но, как упоминалось выше, это был не тот случай.

Следующим шагом здесь была проверка задействованных хэшей.Проверка хеша документа проста: вычисляется хэш диапазона байтов со знаком и сравнивается со значением атрибута со знаком MessageDigest, ср. ExtractHash test testSotnSignedpdf (на Java).

Результат для вашего PDF оказался нормальным.

Следующим шагом была более тщательная проверка контейнера подписи.В этом контексте я однажды начал писать некоторые проверки, но не очень далеко, ср. SignatureAnalyzer класс.Я немного расширил его для проверки хэша подписанных атрибутов, используя алгоритм подписи, который вы использовали, старый RSASSA-PKCS1-v1_5 : в отличие от многих других алгоритмов подписи, этот позволяеттривиально извлечь подписанный хеш.

Здесь результат для вашего PDF оказался не в порядке, хэш подписанных атрибутов отличался от подписанного хеша.

Здесь есть две часто встречающиеся причины несоответствия:

  • либо подписанные атрибуты подписаны с неправильной кодировкой (это должна быть обычная кодировка DER, а не какая-то произвольная)Кодировка BER и, в частности, не кодировка с неявным тегом, который имеет значение, хранящееся в сигнатуре - даже крупные игроки иногда делают это неправильно, например, Docusign, ср. DSS-1343 )

  • или хэш каким-то образом трансформировался во время подписи (например, хэш был закодирован или снова хеширован в base64).

Как оказалось,последний случай был здесь, хеш был снова хеширован.

...