简体   繁体   中英

C# Itext7 signed pdf signature is invalid in Foxit PDF Reqader but valid in Acrobat reader

I cant find why Foxit PDF Reader show me my signature of signed file is invalid. First I insert blank signature container to pdf

public class BlankSignatureContainer2: IExternalSignatureContainer
{
    private readonly PdfName filter;
    private readonly PdfName subFilter;
    private byte[] docBytesHash;
    private byte[] docDigest;
    private const String HASH_ALGORITHM = DigestAlgorithms.SHA256;
    private X509Certificate[] chain;

    public BlankSignatureContainer2(PdfName filter, PdfName subFilter, X509Certificate[] chain)
    {
        this.filter = filter;
        this.subFilter = subFilter;
        this.chain = chain;
    }

    public virtual byte[] GetDocBytesHash()
    {
        return docBytesHash;
    }

    public virtual byte[] GetDocBytesDigest()
    {
        return docDigest;
    }

    public virtual byte[] Sign(Stream docBytes)
    {
        PdfPKCS7 sgn = new PdfPKCS7(null, chain, HASH_ALGORITHM, false);
        docDigest = DigestAlgorithms.Digest(docBytes, DigestAlgorithms.GetMessageDigest(HASH_ALGORITHM));
        docBytesHash = sgn.GetAuthenticatedAttributeBytes(docDigest, PdfSigner.CryptoStandard.CADES, null, null);
        using (SHA256 sha256 = SHA256.Create())
        {
            docBytesHash = sha256.ComputeHash(docBytesHash);
        }

        return new byte[0];
    }

    public virtual void ModifySigningDictionary(PdfDictionary signDic)
    {
        signDic.Put(PdfName.Filter, filter);
        signDic.Put(PdfName.SubFilter, subFilter);
    }
}

docBytesHash I sent to external webservice to sign with qualified signature.

using (MemoryStream stream = new MemoryStream())
using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(value.pdfFile))) //pdf file to sign
using (PdfReader pdfReader = new PdfReader(ms))
{
    //Get signers and CA certificates
    X509Certificate[] chain = await _signPdf.GetMidChain(certificate.Cert);

    // creating empty signature container, getting file digest and hash
    var blankSignature = new BlankSignatureContainer2(PdfName.Adobe_PPKLite, PdfName.ETSI_CAdES_DETACHED, chain);
    var stamper = new PdfSigner(pdfReader, stream, new StampingProperties().UseAppendMode());
    var appearance = stamper.GetSignatureAppearance();

    //Method to add signature appearance    
    var sigAppearance = new SigAppearance("stamp1.svg", chain[0].SubjectDN.GetValueList(X509Name.CN).Cast<string>().FirstOrDefault() ?? "", DateTime.Now, "Sign", stamper.GetDocument());

    appearance.SetPageRect(new iText.Kernel.Geom.Rectangle(sigAppearance.GetPosition()));
    stamper.SetFieldName(new GetSignatureToSign(stamper.GetDocument()).GetSignatureName());
    new PdfCanvas(appearance.GetLayer2(), stamper.GetDocument()).AddXObjectFittedIntoRectangle(sigAppearance.GetXObject(), sigAppearance.GetRectangle());
    stamper.SignExternalContainer(blankSignature, 8192*2+2);

    //Call external webservice to sign blankSignature.GetDocBytesHash()
    var msrp = await _clientMID.SignAsync(new MidSignReqModel() {
        NationalIdentityNumber = value.personalCode,
        PhoneNumber = value.phone,
        Language = value.language,
        DisplayText = value.ShortMessage(),
        DisplayTextFormat = "UCS-2",
        Hash = Convert.ToBase64String(blankSignature.GetDocBytesHash()),
        HashType = "SHA256"
    });

Next I had sign pdf using result from webservice. Here is external signature container:

public class ExternalSignatureContainer2: IExternalSignatureContainer
{
    private X509Certificate[] chain;
    private byte[] signature;
    private string hashAlgorithm;

    public ExternalSignatureContainer2(X509Certificate[] chain, byte[] signature, string hashAlgorithm)
    {
        this.chain = chain;
        this.signature = signature;
        this.hashAlgorithm = hashAlgorithm;
    }

    public void ModifySigningDictionary(PdfDictionary signDic)
    {
        //throw new NotImplementedException();
    }

    public byte[] Sign(Stream data)
    {
        //create TimeStamp Client
        ITSAClient tsa = new TSAClientBouncyCastle(@"http://demo.sk.ee/tsa/");

        string hashAlgorithm = "SHA256";
        PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, false);
        byte[] digest = DigestAlgorithms.Digest(data, DigestAlgorithms.GetMessageDigest(hashAlgorithm));
        sgn.SetExternalDigest(signature, null, "ECDSA");
        return sgn.GetEncodedPKCS7(digest, PdfSigner.CryptoStandard.CADES, tsa, null, null);
    }
}

and this is signing code:

public async Task<byte[]> Sign2(string file, byte[] signature, X509Certificate[] chain, string hashAlgorithm)
{
    using (var tempStream = new MemoryStream(File.ReadAllBytes(Path.Combine(Directory.GetCurrentDirectory(), @"drafts", file))))
    using (var destination = new MemoryStream())
    {
        var tempReader = new PdfReader(tempStream);
        var gsContainer = new ExternalSignatureContainer2(chain, signature, hashAlgorithm);
        var signer = new PdfSigner(tempReader, destination, new StampingProperties().UseAppendMode());
        signer.SetCertificationLevel(PdfSigner.CERTIFIED_FORM_FILLING);
        PdfSigner.SignDeferred(signer.GetDocument(), new GetSignatureToSign(signer.GetDocument()).GetSignatureName(), destination, gsContainer);

        using (MemoryStream LTV = new MemoryStream())
        using (var newSource = new MemoryStream(destination.ToArray()))
        {
            PdfDocument pdfDoc = new PdfDocument(new PdfReader(newSource), new PdfWriter(LTV), new StampingProperties().UseAppendMode());

            LtvVerification v = new LtvVerification(pdfDoc);
            SignatureUtil signatureUtil = new SignatureUtil(pdfDoc);

            IList<string> names = signatureUtil.GetSignatureNames();
            String sigName = names[(names.Count - 1)];

            PdfPKCS7 pkcs7 = signatureUtil.ReadSignatureData(sigName);

            if (pkcs7.IsTsp())
            {
                v.AddVerification(sigName, new OcspClientBouncyCastle(null), new CrlClientOnline(), LtvVerification.CertificateOption.WHOLE_CHAIN,
                    LtvVerification.Level.OCSP_CRL, LtvVerification.CertificateInclusion.YES);
            }
            else
            {
                foreach (String name in names)
                {
                    v.AddVerification(name, new OcspClientBouncyCastle(null), new CrlClientOnline(), LtvVerification.CertificateOption.WHOLE_CHAIN,
                        LtvVerification.Level.OCSP_CRL, LtvVerification.CertificateInclusion.YES);
                }
            }

            v.Merge();
            pdfDoc.Close();
            return await Task.FromResult( LTV.ToArray());
        }
    }
}

and there is result:

爱看阅读器

福昕PDF阅读器

Can't find what I am doing wrong.

Your question is related to this question , like in that case there are problems with the ECDSA signature format. For some more details, therefore, read the answer there.

As explained in response to said question , there are two major formats to encode an ECDSA signature value,

  • as a TLV SEQUENCE of two INTEGER values and
  • as the concatenation of two integers with a fixed length.

Adobe Reader in my experience supports both formats while Foxit apparently only supports the former one.

The signature value you embedded uses the latter format. Thus, Foxit does not accept it.

To make your signature work with Foxit (and with iText's own validation), you should switch to the former format. Check whether your web service can return the signature in the TLV format instead. Alternatively you have to transform from plain format to TLV format in your code.


Actually here one can read that for digital signatures Foxit claims a tight integration with the underlying operating system. While this indeed may have the advantage of enabling any certificates recognized by the operating system to be utilized by Foxit, as explained there, it has the disadvantage that there may be issues with signatures generated by signing software which is not OS dependent, or even with signatures generated or successfully validated by Foxit on other operating systems...


As an aside: Just like in the case of said question , there is another issue in your signature containers: In the signature algorithm field it has the OID for ECDSA public keys, not an OID of an actual signing algorithm. While both Adobe Reader and Foxit accept this, other, less lax validators will reject this.

Using this wrong OID is a known issue in current iText versions. For the time being you could use BouncyCastle or.Net security API classes instead of iText's PdfPKCS7 to create a CMS signature container.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM