Search code examples
c#pdfitextdigital-signaturesmartcard

Signing a PDF with an external signature using a smartcard using iTextSharp 5 gives formatting errors C#


I'm trying to implement a PDF signature with a smartcard using iText 5.5.11. The smart card itself is a Mongolian national ID card which itself seems to be a type of Gemalto. I don't have the dll so I've implemented the iExternalSignature with APDU commands. The signed PDF says it's signed by Unknown and error encountered while BER decoding. There's a certificate file already extracted from the smart card and on the computer. SmartCardHandler is a handler for sending and receiving APDU commands.

Here's my SignPdf implementation.

        private static void SignPdf(string pdfPath, string signedPdfPath, SmartCardHandler handler, string certificatePath)
        {
            using (PdfReader reader = new PdfReader(pdfPath))
            using (FileStream os = new FileStream(signedPdfPath, FileMode.Create))
            {
                X509Certificate2 cert = new X509Certificate2(certificatePath);
                Org.BouncyCastle.X509.X509Certificate bouncyCert = ConvertToBouncyCastle(cert);
                List<Org.BouncyCastle.X509.X509Certificate> chain = new List<Org.BouncyCastle.X509.X509Certificate> { bouncyCert };

                PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0');
                PdfSignatureAppearance appearance = stamper.SignatureAppearance;
                appearance.Reason = "reason";
                appearance.Location = "location";
                appearance.SetVisibleSignature(new iTextSharp.text.Rectangle(0, 0, 200, 100), 1, "sig");
                appearance.Certificate = chain[0];
                
                IExternalSignature external = new SmartCardSignature(handler);

                MakeSignature.SignDetached(appearance, external, chain, null, null, null, 0, CryptoStandard.CMS);
            }
        }

and here is my external signature implementation

class SmartCardSignature : IExternalSignature
{
    private readonly SmartCardHandler _handler;

    public SmartCardSignature(SmartCardHandler handler)
    {
        _handler = handler;
    }

    public byte[] Sign(byte[] message)
    {
        IDigest messageDigest = DigestUtilities.GetDigest(GetHashAlgorithm());
        byte[] hash = DigestAlgorithms.Digest(messageDigest, message);

        byte[] sendHash = new byte[] { 0x00, 0x2A, 0x90, 0xA0, 0x22, 0x90, 0x20, (byte)hash.Length }.Concat(hash).ToArray();
        _handler.SendApduCommand(sendHash);
        byte[] getHash = new byte[] { 0x00, 0xC0, 0x00, 0x00, 0x20 };
        _handler.SendApduCommand(getHash);
        byte[] signApdu = new byte[] { 0x00, 0x2A, 0x9E, 0x9A, 0x00 };
        byte[] signatureWith9000 = _handler.SendApduCommand(signApdu);;

        if (signatureWith9000.Length < 2 || signatureWith9000[^2] != 0x90 || signatureWith9000[^1] != 0x00)
        {
            throw new InvalidOperationException("Failed to sign the message using the smart card.");
        }

        byte[] signature = signatureWith9000.Take(signatureWith9000.Length - 2).ToArray();
        return signature;
    }

    public string GetHashAlgorithm() => "SHA-256";

    public string GetEncryptionAlgorithm() => "RSA";
}

here's a test pdf before signing

and here's an example cms signed pdf

and here's an example cades (not sure) signed pdf

here's the apdu trace, pin code is 1111

00 A4 04 00 0C A0 00 00 00 18 40 00 00 01 63 42 00 
90 00 
00 20 00 81 08 31 31 31 31 30 30 30 30 
90 00 
00 22 41 B6 06 84 01 02 80 01 42 
90 00 
00 2A 90 A0 22 90 20 20 0B 2D C2 F7 37 64 BE 40 F9 F3 4C 33 F9 BA F5 4E 49 06 4D 63 C1 B7 CA 51 DD 19 46 08 21 C7 AC 90 
61 20 
00 C0 00 00 20 
20 0B 2D C2 F7 37 64 BE 40 F9 F3 4C 33 F9 BA F5 4E 49 06 4D 63 C1 B7 CA 51 DD 19 46 08 21 C7 AC 90 00 
00 2A 9E 9A 00 
00 36 F6 C6 9F 0D 40 E7 BC 1E 6C 0D 5F 50 00 DF 3C 26 7F D0 2F F3 95 4C C7 FD E6 18 E5 3E 94 82 4A D1 46 02 65 30 E7 1D 29 9D B7 6F 55 9E B8 49 11 63 16 8E C0 CB E8 44 0B 71 31 9F FB D5 2B 6E 70 DE 30 F7 7F C4 E8 57 EC 39 2A BB 4E 02 AD 92 70 5B DE 23 EB 99 AA FA E1 5F 9E 25 39 0A 39 C2 4F 41 87 82 98 6F 26 2E 5A 8E A2 E5 12 8C 70 41 00 9E 46 4D 9B 18 5D 52 7E 77 9B 16 F3 2A BC 2A 11 FB 53 76 81 8F B7 0C 5F BA 2F A0 A9 9A F2 75 45 78 D0 69 83 3D 05 78 32 68 99 8F 9F B9 14 26 6E 05 06 58 13 D4 7E 98 A2 51 02 F4 81 05 0A F0 DB 0E 8B BB DA C8 6C 56 E7 28 80 A3 DC 12 13 62 E0 CB AA 7B 44 0A E4 44 32 3F 33 8F 31 30 44 E2 EB 00 6F BF F0 42 03 4A AD 15 80 76 97 2E B5 14 B6 CA D0 37 3D E0 67 4A AB A0 96 66 59 85 65 F8 11 C6 B4 64 03 0F 65 78 C8 3B B4 00 58 6D 35 F3 90 00

As I understand it I first create a new PDF file with a predefined contents section which contains the certificate and the signature. It takes the digest from the rest of the input PDF and sends it to the smart card to get it hashed. Then that hash along with the appearance and certificate chain is used to do MakeSignature.SignDetached(). I've looked at following post1 and post2 but they do not seem to apply to my case.


Solution

  • I finally figured out why it wasn't working, it was because in my apdu commands I was adding an extra 0x20 which was unnecessary. Turning

    byte[] sendHash = new byte[] { 0x00, 0x2A, 0x90, 0xA0, 0x22, 0x90, 0x20, (byte)hash.Length }.Concat(hash).ToArray();
    

    into

    byte[] sendHash = new byte[] { 0x00, 0x2A, 0x90, 0xA0, 0x22, 0x90,(byte)hash.Length }.Concat(hash).ToArray();
    

    fixed the problem.