Ve webové ASP.NET aplikaci jsem potřebovat pracovat s certifikátem (konkrétně po podepisování generovaného PDF souboru viz. tento článek). Aby byla aplikace dostatečně flexibilní, rozhodl jsme se, že certifikát budu načítat z Windows uložiště certifikátů (Windows Certificate Store) a konkrétní certifikát bude nastaven konfigurací v souboru Web.config.
Při konfiguraci X.509 certifikátu jsem chtěl použít nějaký “standardní” způsob, obdobu zápisu jako je například při konfiguraci klientského nebo serverového certifikátu v technologii WCF, nebo konfigurace certifikátu pro STS v technologii Windows Identity Foundation (WIF). Tam konfigurace vypadá nějak takto:
<microsoft.identityModel>
<service>
<serviceCertificate>
<certificateReference x509FindType="FindByThumbprint" findValue="862002e527e1ea0e3465d59cc4c49af0d093ad0c" storeLocation="LocalMachine" storeName="My" />
</serviceCertificate>
...
</service>
</microsoft.identityModel>
Uvedený konfigurační element certificateReference je typu CertificateReferenceElement, který umožňuje určit certificate store (vlastnostmi StoreLocation a StoreName) a parametry pro vyhledání konkrétního certifikátu v něm (vlastnosti X509FindType a FindValue). A právě tento typ jsem se rozhodl použít.
Pro můj scénář jsem implementoval třídu konfigurační sekce SignCertificateConfigurationSection pro nastavení podpisového certifikátu a třídu SignCertificateConfiguration, pomoci které konfiguraci načteme. Obě třídy si můžete prohlédnou zde.
Třída SignCertificateConfigurationSection používá ve vlastnosti SignCertificate pro vyhledání platného certifikátu pomocnou metodu GetValidCertificate (*) implementovanou ve třídě CertificateUtil, ta je k dispozici zde(**).
Odpovídající konfigurace v .config souboru může vypadat následovně:
<configuration>
<configSections>
<section name="signCertificateConfiguration" type="IMP.Cryptography.SignCertificateConfigurationSection" />
</configSections>
<signCertificateConfiguration>
<signCertificate x509FindType="FindByThumbprint" findValue="862002e527e1ea0e3465d59cc4c49af0d093ad0c" storeLocation="LocalMachine" storeName="My" />
</signCertificateConfiguration>
...
</configuration>
Zbývá nám kód, který bude třídu SignCertificateConfiguration používat. Protože se načtený certifikát využívá pro podpis, je z něho nutné získat privátní klíč. V případě webové aplikace ale na to nemusí být práva, ty je potřeba patřičné identitě nastavit volbou Manage Private Keys ve správě certifikátů viz. tento návod.
Kód může vypadat například takto:
var cert = IMP.Cryptography.SignCertificateConfiguration.SignCertificate;
try
{
SignPdfWithCertificate(cert);
}
catch (System.Security.Cryptography.CryptographicException ex)
{
if (ex.Message.StartsWith("Keyset does not exist", StringComparison.Ordinal))
{
var identity = System.Security.Principal.WindowsIdentity.GetCurrent();
throw new Exception(string.Format("Nejsou nastaveny práva pro přístup IIS k Private key certifikátu {0}.\r\nProveďte nastavení pomoci volby Manage Private Keys na uživatele použitého aplikačního poolů ({1}).", cert.Thumbprint, identity == null ? "" : identity.Name), ex);
}
throw;
}
Uvedené řešení lze použít nejen ve webové aplikaci, konfigurace pak bude v souboru App.config.
(*) V metodě GetValidCertificate je vyhledání a validace platnosti certifikátu. Kromě toho je zde navíc v případě hledání podle Thumbprint certifikátu upravena jeho hodnota. Pomocná metoda TrimCertThumbprint ze stringu odstraní mezery a pokud se vyskytuje, tak je ze začátku odstraněn znak 8206. Pokud si totiž zobrazíte vlastnosti certifikátu a odsud vykopírujete jeho Thumbprint, je tam tento znak obsažen. (Párkrát jsem již kvůli tomu řešil chybu, že není možné programově certifikát ve storu najít).
(**) Ve třídě CertificateUtil je ještě jedna pomocná metoda – SelectStoreCertificate. Ta slouží v desktop aplikacích k zobrazení Windows dialogu pro výběr certifikátu ze storu.