Digitální podepisování PDF souborů v C#

Jan Holan       30.05.2011       Bezpečnost       20660 zobrazení

V tomto článku vyrobíme program v C#, který otevře již exitující PDF dokument, doplní do něj digitální podpis a soubor zase uloží. Vlastní program bude docela jednoduchý, ale tím si ukážeme jak přistupovat k PDF souborům z .NET kódu a také jak si vygenerovat testovací podpisový certifikát.

Prvním problémem tedy je, jak v naší aplikaci jednoduše pracovat s dokumenty ve formátu PDF. K tomu využijeme C# knihovnu iTextSharp.dll. Je to plně managed .NET assembly, kterou přidáme jako referenci do našeho projektu. Jedná se o portaci Javovské knihovny iText, portace probíhala tak různě, nejprve pro .NET 1.1 do J# (iText.NET) a teprve později jako knihovna plně v C# (iTextSharp). Proto je trochu problém s tím správnou verzi knihovny najít a také je problém s dokumentací, která je většinou pouze pro původní Javovskou nebo J# verzi. Knihovnu je možné i se zdrojovými soubory stáhnout z sourceforge.net zde (v době psaní článku ve verzi 5.1.0.0). Stránky celého iText projektu jsou zde.

Aby jsme se s knihovnou nejprve trochu seznámili, tak si napišme jednoduchý prográmek, a jak to bývá zvykem, třeba Hello World resp. v tomto případě program co vytvoří PDF s textem Hello World.

using iTextSharp.text;
using iTextSharp.text.pdf;

class Program
{
    static void Main(string[] args)
    {
        //Create document-object
        var document = new Document();

        //Create a writer that listens to the document and directs a PDF-stream to a file
        PdfWriter.GetInstance(document, new System.IO.FileStream(@"Document.pdf", System.IO.FileMode.Create));

        //Open the document
        document.Open();

        //Add a paragraph to the document
        document.Add(new Paragraph("Hello World"));

        //Close the document
        document.Close();
    }
}

A nyní již program pro vlastní podepsání PDF dokumentu:

using System;
using iTextSharp.text;
using iTextSharp.text.pdf;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Pkcs;

namespace SignPDF
{
    class Program
    {
        static void Main(string[] args)
        {
            string pass = "tHuch5wr";

            string alias = null;
            ICipherParameters key;
            Org.BouncyCastle.X509.X509Certificate[] chain;
            using (var keyfs = new System.IO.FileStream(@"..\..\TestCert\TestCert.pfx", System.IO.FileMode.Open))
            {
                var ks = new Pkcs12Store(keyfs, pass.ToCharArray());

                foreach (string al in ks.Aliases)
                {
                    if (ks.IsKeyEntry(al) && ks.GetKey(al).Key.IsPrivate)
                    {
                        alias = al;
                        break;
                    }
                }

                key = ks.GetKey(alias).Key;

                var certificateChainEntry = ks.GetCertificateChain(alias);
                chain = new Org.BouncyCastle.X509.X509Certificate[certificateChainEntry.Length];
                for (int k = 0; k < certificateChainEntry.Length; ++k)
                {
                    chain[k] = certificateChainEntry[k].Certificate;
                }
            }

            var pdrReader = new PdfReader(@"Document.pdf");
            var fsOut = new System.IO.FileStream(@"Signed.pdf", System.IO.FileMode.Create);

            var stp = PdfStamper.CreateSignature(pdrReader, fsOut, '\0');

            var sap = stp.SignatureAppearance;
            sap.SetCrypto(key, chain, null, PdfSignatureAppearance.WINCER_SIGNED);
            //sap.Reason = "Reason";
            //sap.Location = "Location";

            //Remove next line to have an invisible signature
            sap.SetVisibleSignature(new iTextSharp.text.Rectangle(415, 100, 585, 40), 1, null);

            stp.Close();
        }
    }
}

Kód nejprve musí načíst data certifikátu pro podpis, pozor tedy, že certifikát musí obsahovat i privátní klíč. Z toho důvodu ho máme uložený v souboru pfx (Personal Information Exchange) ve formátu PKCS #12. Pro načtení tohoto souboru třídou Pkcs12Store je nutné, ještě dodat heslo, se kterým byl soubor vytvořen. Vlastní certifikát je zde třídě předán přímo jako FileStream, možností je ale více, např. je možné jeho načtení provést přímo z úložiště certifikátů systému Windows.

Z certifikátu se pak získá privátní klíč, který je uložen do proměnné key typu ICipherParameters, a dále cesta certifikátů - certificate chain  typu X509Certificate[]. Obě tyto proměnné jsou použity v metodě SetCrypto objektu SignatureAppearance, která klíč přiřadí PDF dokumentu. Dále metoda SetVisibleSignature umožňuje, aby byl podpis v PDF dokumentu přímo vizuálně zobrazen, jako první parametr se zde uvádí umístění na dokumentu. V tomto příkladu je podpis umístěn na pozici napravo dolu.

Po spuštění příkladu je PDF dokument Document.pdf podepsán a uložen do souboru Signed.pdf.

Signature

Kód používá zmíněný soubor certifikátu TestCert.pfx, pro vývoj nebo testování je vhodné si vygenerovat zkušební self-signed certifikát. To se dá provést následujícími příkazy:

"c:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\makecert.exe" -r -pe -n "CN=IMP spol. s r.o." -b 01/01/2010 -e 01/01/2099 -ss DevelopCertStore -sv TestCert.pvk TestCert.cer
"c:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\pvk2pfx.exe" -pvk TestCert.pvk -spc TestCert.cer -pfx TestCert.pfx -po tHuch5wr

Jedná se nejprve o volání utility makecert (Certificate Creation Tool ), která vygeneruje privátní klíč (do souboru TestCert.pvk) a certifikát TestCert.cer. Tyto dva soubory jsou pak druhou utilitou pvk2pfx (PVK/SPC to PFX file converter) převedeny do výsledného PFX souboru, při jeho volání je zde parametrem “–po” určeno heslo, kterým je soubor pfx zabezpečen (toto heslo je pak uvedeno v našem kódu, který soubor načítá). Z uvedené cesty je vidět, že obě utility se nacházejí součástí Windows SDK (pro Windows 7 dostupného zde).

Pozn.: Ve volání makecert.exe je použit parametr “–pe”, tento parametr určuje, že privátní klíč je možné vyexportovat. Pozor ale na to, že některé starší verze této utility tento parametr nepodporovali, je tedy nutné použít verzi z některého novějšího SDK.

Aby byl náš podpis pří použití vygenerovaného self-signed certifikátu v PDF souboru validní, je nutné náš soubor TestCert.cer naimportovat do Trusted identit Adobe Readeru. Zde je nutné v dialogu Import Contact Settings zaškrtnout volbu Use this certificate as s trusted root.

ImportContactSettings

Kompletní příklad včetně vygenerovaného certifikátu je možné stáhnout zde.

Zdroje:
http://itextpdf.sourceforge.net/howtosign.html (Návod How to sign a PDF using iText and iTextSharp)
http://sourceforge.net/projects/itextsharp (Stránky projektu iTextSharp)
http://www.ujihara.jp/iTextdotNET/en/examples.html (iText .NET příklady pro C# )
http://itextpdf.com/itext.php (Stránky iText)

 

hodnocení článku

0 bodů / 1 hlasů       Hodnotit mohou jen registrované uživatelé.

 

Nový příspěvek

 

RE: Digitální podepisování PDF souborů v C#

Zdeněk: Nevím, spíš bych postupoval tak, že bych při podepsání PDF rovnou zakázal provádět zmeny v dokumentu.

To lze provést metodou SetEncryption na objektu PdfStamper, jako to dělám v druhé části článku. Například tedy:

[quote]stp.SetEncryption(null, Guid.NewGuid().ToByteArray(), PdfWriter.ALLOW_PRINTING | PdfWriter.ALLOW_COPY | PdfWriter.ALLOW_SCREENREADERS, PdfWriter.ENCRYPTION_AES_128);[/quote]

nahlásit spamnahlásit spam 0 odpovědětodpovědět

RE: Digitální podepisování PDF souborů v C#

Dobrý den,

lze nějak ověřit, že dokument pdf nebyl upravován (po podpisu)?

Díky,

Zdeněk.

Zdeněk

nahlásit spamnahlásit spam 0 odpovědětodpovědět

RE: Digitální podepisování PDF souborů v C#

Máte pravdu, výchozí font, který se použije na zobrazení podpisu je v jiným kódování a nezobrazuje znaky ěčřů.

Chvilku mi to dalo, ale přišel jsme jak nato jak font a jeho kodování změnit, před volání sap.SetVisibleSignature zkuste doplnit následující:

BaseFont helvetica = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1250, BaseFont.EMBEDDED);

Font font = new Font(helvetica, 12, iTextSharp.text.Font.NORMAL);

sap.Layer2Font = font;

kde CP1250 je určení encodingu pro font (pro můj tetovací certifikát jsem použil Windows 1250).

nahlásit spamnahlásit spam 0 odpovědětodpovědět

RE: Digitální podepisování PDF souborů v C#

Dobrý den,

funguje zobrazení podpisu korektně v případě, že název vlastníka podpisovacího certifikátu obsahuje písmeno "č"? V mém případě není "č" v podpisu zobrazeno. Skusil jsem nastavit font podpisu pomocí setLayer2Font(..), ale bez úspěchu.

Díky,

Jara

Jara

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback