Search code examples
itextdigital-signaturesignaturesmartcardecdsa

Sign PDF with new Belgian id card (eid middleware and itext)


I try to sign some pdf's with a Belgian id card. To acheive that, I'm using the belgium eid middleware to sign the data and itext7 to stamp the pdf with the signature.

I use a PdfSigner (itext) and I have implement a IExternalSignature to call the eid middleware to sign the message.

All work well for the Belgian id card 1.7 with encryption RSA and hash SHA256.

But when I try to sign with the new Belgian id card 1.8 with encryption ECDSA and hash SHA384, the signature can't be validated by adobe reader (or other reader). "The document has been altered or corrupted".

It seems to be a mismatch somewhere in the hashing or ...

I search for some days but I have no more idea to fix that ...

Someone have an idea about what is going wrong?

Thanks in advance for your help.

Here some additional informations.

The external signature class:

    internal sealed class BeIDSignature : IExternalSignature
    {
        public string GetEncryptionAlgorithm()
        {
            return eidWrapper.Instance.GetEncryptionAlgorithm().ToString();
        }

        public string GetHashAlgorithm()
        {
            switch (EidWrapper.Instance.GetEncryptionAlgorithm())
            {
                case EidWrapper.EncryptionAmgorithm.RSA:
                    return DigestAlgorithms.SHA256;
                case EidWrapper.EncryptionAmgorithm.ECDSA:
                    return DigestAlgorithms.SHA384;
                default:
                    return null;
            }
        }
    
        public byte[] Sign(byte[] message)
        {
            return EidWrapper.Instance.SignData(message);
        }
    }

GetEncryptionAlgorithm will return RSA or ECDSA depending of the chip. The sign method will use the eid-mw packege to generate the signature.

A little piece of code of the sign method of the EidWrapper:

    if (key.KeyType.KeyType == CKK.EC)
    {
        session.SignInit(new Mechanism(CKM.ECDSA_SHA384), key);
        return session.Sign(data);
    }
    else if (key.KeyType.KeyType == CKK.RSA)
    {
        session.SignInit(new Mechanism(CKM.SHA256_RSA_PKCS), key);
        return session.Sign(data);
    }

You can find here a zip with 3 pdf files:

  • The original file
  • One signed directly with adobe (siganture is ok)
  • One signed with eid-mw and itext (signature is NOT ok). But remember that is working for RSA/SHA256 siganture.

https://easyupload.io/yzscsu

Thanks again for your time.


Solution

  • This answer sums up the comments to the question.

    iText and ECDSA signatures

    First of all one has to realize that currently (i.e. for iText 7.2.2) ECDSA is not supported by all parts of the iText signing API.

    This limitation is mostly due to the iText PdfPKCS7 class used to create and validate CMS signature containers embedded in PDFs. When it is used to create a signature container, the identifier it stores in the signatureAlgorithm field does not take the used hash algorithm into account. E.g. it uses the same value for RSA (with PKCS1 v1.5 padding) signatures, no matter if it's actually SHA1withRSA or SHA256withRSA or whichever combination.

    For RSA this is ok because there indeed is an identifier that can be used for all these cases. For DSA it is somewhat ok because in many contexts DSA is limited to use with SHA1 only.

    For ECDSA this is not ok, there only are identifiers taking the hash algorithm into account. iText uses the EC public key identifier in all these cases which is simply wrong. The reason why hardly anyone noticed this bug is that Adobe Acrobat validation apparently ignores the contents of this signatureAlgorithm field: You can even write the RSA identifier into this field of an ECDSA signature and the validation succeeds without an indication of a problem.

    To create proper ECDSA signatures, therefore, one currently should not use the PdfPKCS7 class. As all the PdfSigner.signDetached methods internally use the PdfPKCS7 class, this in turn means that one must not use them but instead PdfSigner.signExternalContainer. As a consequence, one must not use an IExternalSignature implementation to retrieve one's signature value but instead an IExternalSignatureContainer implementation in which one builds the CMS signature container differently, for example using BouncyCastle classes.

    In the case at hand the BeIDSignature implementation of IExternalSignature, therefore, must be replaced accordingly.

    For further details please read the section Which Interface to Use of the iText knowledge base article Digital Signing with iText 7.

    ECDSA signature formats

    There are two major formats in which an ECDSA signature value can be stored, either as a TLV (DER) encoded sequence of two integers or (plain encoding) as the concatenation of fixed length representations of those two integers.

    Depending on the used format one has to use specific algorithm identifiers for ECDSA and PLAIN-ECDSA respectively. If one needs a specific identifier, one can convert the signature value from one format to the other.

    In the case at hand the Belgian ID card returns the ECDSA signature value in plain format. To use the more common non-PLAIN ECDSA identifiers, one has to convert that value to the DER format. This can be done using this method:

    byte[] PlainToDer(byte[] plain)
    {
        int valueLength = plain.Length / 2;
        BigInteger r = new BigInteger(1, plain, 0, valueLength);
        BigInteger s = new BigInteger(1, plain, valueLength, valueLength);
        return new DerSequence(new DerInteger(r), new DerInteger(s)).GetEncoded(Asn1Encodable.Der);
    }
    

    (X509Certificate2Signature helper method from the code examples of Digital Signing with iText 7)