Цифровая подпись файла PDF за 2 шага с iText 7, результат - поврежденный файл

Я пытаюсь подписать PDF-файл цифровой подписью в два этапа: вычислить хэш, подписать хэш, а затем объединить подписанный хеш с выходным файлом. Я использую библиотеку iText 7.

Однако в результате файл поврежден и не может быть открыт.

Что не так?

Сначала я вычисляю хэш и подписываю входной файл пустым контейнером для подписи:

public static byte[] ComputeHash(string source, string temp, X509Certificate[] chains, string reason, string location, int qtySigns, int pageNumber)
{
    IList<ICrlClient> crlList = new List<ICrlClient>();
    crlList.Add(new CrlClientOnline(chains));

    using (var reader = new PdfReader(source))
    {
        using (var os = new FileStream(temp, FileMode.OpenOrCreate, FileAccess.Write))
        {
            var signer = new PdfSigner(reader, os, new StampingProperties().UseAppendMode());

            var signatureAppearance = signer.GetSignatureAppearance();
            signatureAppearance
                .SetReason(reason)
                .SetLocation(location)
                .SetPageNumber(pageNumber)
                .SetRenderingMode(PdfSignatureAppearance.RenderingMode.NAME_AND_DESCRIPTION)
                .SetCertificate(chains[0])
                .SetPageRect(new iText.Kernel.Geom.Rectangle(36, 20, 144, 53));

            var container = new EmptySignatureContainer(PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached);
            signer.SignExternalContainer(container, 8192);

            byte[] hash = container.Hash;

            return hash;
        }
    }
}

Вот контейнер:

public class EmptySignatureContainer : IExternalSignatureContainer
{
    private PdfDictionary _sigDic;
    public byte[] Hash;

    public byte[] Sign(Stream data)
    {
        string hashAlgorithm = "SHA256";

        try
        {
            this.Hash = DigestAlgorithms.Digest(data, hashAlgorithm);
        }
        catch (IOException e)
        {
            throw new GeneralSecurityException("EmptySignatureContainer signing exception", e);
        }

        return new byte[0];
    }

    public void ModifySigningDictionary(PdfDictionary signDic)
    {
        signDic.PutAll(_sigDic);
    }

    public EmptySignatureContainer(PdfName filter, PdfName subFilter)
    {
        _sigDic = new PdfDictionary();
        _sigDic.Put(PdfName.Filter, filter);
        _sigDic.Put(PdfName.SubFilter, subFilter);
    }
}

После этого я получаю аутентифицированные байты атрибута и вычисляю его хэш:

var hash = ComputeHash(inputFile, tempFile, chain, "sign-reason", "",  1, 1);
PdfPKCS7 sgn = new PdfPKCS7(null, chain, "SHA256", false);
var authenAttrBytes = sgn.GetAuthenticatedAttributeBytes(hash, PdfSigner.CryptoStandard.CMS, null, null);

var computedHash = SHA256Managed.Create().ComputeHash(authenAttrBytes);`

Затем вычисленный хэш будет отправлен на сервер hsm для подписи. Затем подписанный хэш будет добавлен обратно во временный файл в виде следующего кода:

Sign(tempFile, outputFile, chain, authenAttrBytes, signedHash);

А вот и метод Sign:

public static void Sign(string tempFile, string targetFile, X509Certificate[] chains, byte[] hash, byte[] signedHash)
{
    using (PdfReader reader = new PdfReader(tempFile))
    {
        using (FileStream outStream = System.IO.File.OpenWrite(targetFile))
        {
            var signedContainer = new SignedSignatureContainer(hash, signedHash, chains);
            var signer = new PdfSigner(reader, outStream, new StampingProperties());
            signer.SignExternalContainer(signedContainer, 8192);
        }
    }
}

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

public  class SignedSignatureContainer : IExternalSignatureContainer
{
    public byte[] Hash { get; set; }
    public byte[] SignedHash { get; set; }
    public X509Certificate[] CertChains { get; set; }

    public SignedSignatureContainer(byte[] hash, byte[] signedHash, X509Certificate[] certCertChains)
    {
        this.Hash = hash;
        this.SignedHash = signedHash;
        this.CertChains = certCertChains;
    }

    public byte[] Sign(Stream data)
    {
        var sgn = new PdfPKCS7(null,  CertChains, "SHA256", false);
        sgn.SetExternalDigest(this.SignedHash, null, "RSA");
        return sgn.GetEncodedPKCS7(this.Hash, CryptoStandard.CMS, null, null, null );
    }

    public void ModifySigningDictionary(PdfDictionary signDic)
    {
        signDic.Put(PdfName.Filter, PdfName.Adobe_PPKLite);
        signDic.Put(PdfName.SubFilter, PdfName.Adbe_pkcs7_detached);
    }
}
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
61
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В вашем методе Sign вы используете метод PdfSignerSignExternalContainer:

var signedContainer = new SignedSignatureContainer(hash, signedHash, chains);
var signer = new PdfSigner(reader, outStream, new StampingProperties());
signer.SignExternalContainer(signedContainer, 8192);

Но это не заполняет изначально подготовленную подпись! Вместо этого создается другое поле подписи и заполняется подписью исходного.

Вместо этого используйте статический PdfSigner метод SignDeferred:

/// <summary>Signs a PDF where space was already reserved.</summary>
/// <param name = "document">the original PDF</param>
/// <param name = "fieldName">the field to sign. It must be the last field</param>
/// <param name = "outs">the output PDF</param>
/// <param name = "externalSignatureContainer">
/// the signature container doing the actual signing. Only the
/// method ExternalSignatureContainer.sign is used
/// </param>
public static void SignDeferred(PdfDocument document, String fieldName, Stream outs,
     IExternalSignatureContainer externalSignatureContainer)

Другие вопросы по теме