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.
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.
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)