Hello S-1-1-0!
Today I'll describe some interesting features implemented in X509CRL2 class. This class is available within my PowerShell PKI module only (is not a part of .NET Framework).
A time ago a came into an issue with CRL copy to remote web server.
My internal CA publish CRLs to a local drive and custom script copies it to all required distribution points (internal and external web servers). And one day certificate validation failed due to Revocation Offline error. I've checked that CRL is correctly formed, is time valid and so on. But certificate chaining engine still reported mentioned error. Detailed investigation showed that unexpected network-level error occurred during file copy over internet and CRL signature become invalid. Since CRL object contains only signature value without signer certificate, normally it is impossible to verify whether the CRL signature is valid. In order to verify signature, you must obtain signer certificate (issuing CA certificate) and use custom steps to verify the signature.
In order to simplify this process I've added a VerifySignature() method to an X509CRL2 class. Here is a little explanation, how it works:
PS C:\> ipmo pspki PS C:\> $crl = Get-CRL C:\CertData\pica-1.crl PS C:\> $crl Version : 2 Type : Base CRL IssuerDN : System.Security.Cryptography.X509Certificates.X500DistinguishedName Issuer : CN=Sysadmins LV Internal Class 1 SubCA-1, OU=Information Systems, O=Sysadmins LV, C=LV ThisUpdate : 2012.03.31. 14:18:00 NextUpdate : 2012.04.04. 14:38:00 SignatureAlgorithm : System.Security.Cryptography.Oid Extensions : {System.Security.Cryptography.Oid, System.Security.Cryptography.Oid, System.Security.Cryptography .Oid, System.Security.Cryptography.Oid...} RevokedCertificates : {Serial number: 40d897f4000000000109 revoked at: 2012.03.26. 21:17:00, Serial number: 497285fb000 000000105 revoked at: 2012.03.26. 18:21:00, Serial number: 1ad52cdc0000000000fb revoked at: 2012. 02.04. 22:32:00, Serial number: 222017d10000000000f1 revoked at: 2012.01.28. 11:25:15...} RawData : {48, 130, 4, 171...} Handle : 457143024
Here we see CRL object. Get the exposed public methods:
PS C:\> $crl | gm -MemberType method TypeName: System.Security.Cryptography.X509Certificates.X509CRL2 Name MemberType Definition ---- ---------- ---------- CertificateInCrl Method bool CertificateInCrl(System.Security.Cryptography.X509Certificates.X509Certificate2 cert) Encode Method string Encode(System.Security.Cryptography.X509Certificates.X509EncodingType encoding) Equals Method bool Equals(System.Object obj) Export Method System.Void Export(string path, System.Security.Cryptography.X509Certificates.X509Encodi... GetHashCode Method int GetHashCode() GetType Method type GetType() Import Method System.Void Import(string path), System.Void Import(byte[] rawData) Reset Method System.Void Reset() ToString Method string ToString() VerifySignature Method bool VerifySignature(System.Security.Cryptography.X509Certificates.X509Certificate2 issuer) PS C:\> $crl.VerifySignature.OverloadDefinitions bool VerifySignature(System.Security.Cryptography.X509Certificates.X509Certificate2 issuer)
As we see, here is VerifySignature() method that accepts X509Certificate2 object (which represents possible issuer) and returns True (bool type) if signature is successfully decoded and verified by using issuer's public key. Otherwise false. Here are two examples:
PS C:\> $crl.VerifySignature("C:\CertData\pica-1.crt") True PS C:\> $crl.VerifySignature("C:\CertData\rca-1.crt") False PS C:\>
Another funny feature is to determine whether the particular certificate is revoked and it's reference is contained in the CRL's RevokedCertificates property. Ok, you can do this by processing this property over foreach loop and compare each entry's SerialNumber property with the presented certificate. However this method is not effective for large CRLs and may consume a lot of time. Fortunately, here is an unmanaged CryptoAPI function which do this task much faster: CertFindCertificateInCRL. And this function is used for CertificateInCrl() method of X509CRL2 class (see console view above). As a method parameter you pass X509Certificate2 object to verify. And how it works:
PS C:\> $crl.CertificateInCrl("C:\CertData\revoked.cer") True PS C:\> $cert = New-Object security.cryptography.x509certificates.x509certificate2 C:\CertData\revoked.cer PS C:\> $crl.RevokedCertificates | ?{$_.serialnumber -eq $cert.serialnumber} SerialNumber RevocationDate ReasonCode ReasonMessage ------------ -------------- ---------- ------------- 222017d10000000000f1 2012.01.28. 11:25:15 4 Superseded PS C:\>
In the first line I'm using native method to verify the certificate against CRL. In the rest lines I perform slow looping to show revoked certificate entry.
Sometimes you may need to serialize CRL object so it can be transferred as a plain text and de-serialized by recipient. To address this issue I've added Encode() and Export() methods. Encode() method just encodes CRL to a Base64 (with or without headers) string and sends to caller (PowerShell console). Export() method exports CRL object to a file by using specified encoding type. The following encoding types are defined:
PS C:\> [enum]::GetNames([System.Security.Cryptography.X509Certificates.X509EncodingType]) Base64 Base64Header Binary PS C:\>
Note: Binary encoding type is not supported by Encode() method.
End here is encoding example:
PS C:\> $crl.Encode("base64header") -----BEGIN X509 CRL----- MIIEqzCCA5MCAQEwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCTFYxFTATBgNVBAoTDFN5c2Fk bWlucyBMVjEcMBoGA1UECxMTSW5mb3JtYXRpb24gU3lzdGVtczEuMCwGA1UEAxMlU3lzYWRtaW5z IExWIEludGVybmFsIENsYXNzIDEgU3ViQ0EtMRcNMTIwMzMxMTExODAwWhcNMTIwNDA0MTEzODAw WjCCAhMwKQIKQNiX9AAAAAABCRcNMTIwMzI2MTgxNzAwWjAMMAoGA1UdFQQDCgEFMCkCCklyhfsA AAAAAQUXDTEyMDMyNjE1MjEwMFowDDAKBgNVHRUEAwoBBTAbAgoa1SzcAAAAAAD7Fw0xMjAyMDQy MDMyMDBaMCkCCiIgF9EAAAAAAPEXDTEyMDEyODA5MjUxNVowDDAKBgNVHRUEAwoBBDApAgod0b+v AAAAAADuFw0xMjAxMTkxNTMzMDBaMAwwCgYDVR0VBAMKAQUwKQIKYUd0bAAAAAAA1xcNMTEwOTI2 MjM1NzQ0WjAMMAoGA1UdFQQDCgEFMCkCCmFHbB8AAAAAANYXDTExMDkyNjIzNTc0NFowDDAKBgNV HRUEAwoBBTApAgphR2Q/AAAAAADVFw0xMTA5MjYyMzU3NDRaMAwwCgYDVR0VBAMKAQUwKQIKEiJk SAAAAAAAwBcNMTEwOTI2MTg1NzQxWjAMMAoGA1UdFQQDCgEFMCkCChIg0YIAAAAAALYXDTExMDky NjE4NTc0MVowDDAKBgNVHRUEAwoBBTApAgphS+KzAAAAAADYFw0xMTA5MjYxODU2MzJaMAwwCgYD VR0VBAMKAQUwKQIKYT+l4wAAAAAA1BcNMTEwOTI2MTg1NjMyWjAMMAoGA1UdFQQDCgEFMBsCCjv+ jncAAAAAAHgXDTEwMTIyNzE3MzIwMFqggdUwgdIwHwYDVR0jBBgwFoAUG/pecy1nE1zO0w7m6Hqp YIwLY/wwEAYJKwYBBAGCNxUBBAMCAQAwCwYDVR0UBAQCAgWIMBwGCSsGAQQBgjcVBAQPFw0xMjA0 MDMxMTI4MDBaMDgGA1UdLgQxMC8wLaAroCmGJ2h0dHA6Ly93d3cuc3lzYWRtaW5zLmx2L3BraS9w aWNhLTErLmNybDA4BgNVHRwBAf8ELjAsoCqgKIYmaHR0cDovL3d3dy5zeXNhZG1pbnMubHYvcGtp L3BpY2EtMS5jcmwwDQYJKoZIhvcNAQEFBQADggEBAGWjqNf0lDqt7LR4d9d3LaMf9E3XRvbPLwtG UobBSuLe/Y/clQV0ZmzW8WdPuAapSDB1y0Q7bIAQSIQ90S3TXdY2zsB1Of96+LoepKDHdFQscRzj FVLq6ZlamAHXQFDlqGHb3b8UcePrfARLG4/K2S1pHlBclEbzV8isARycks2D5fE58jW0azft9/u2 vsByYAf0/gNxDzOiF6iLSAydTnKSQvWAQ9XptGw5EpOaqj2ONds19v4nwMJDZwTnqxeW4U/OoTS3 TpwFWjaiGNtPoBdFlvAKZWtg+EIj7MtzhVdFzWfkq+6mRCNlhGEfP2aCGf+XaiO8rPMctgffkEgk ivg= -----END X509 CRL----- PS C:\>
Default X509CRL2 constructor (which accepts the path to a CRL file) and Import() method support CRL files with any of mentioned encoding types.
What if you want to see the CRL in UI (like you double-clicked CRL file in Windows Explorer)? My module contains Show-CertificateRevocationList cmdlet. Previously the code was very dirty and you MUST NOT use it:
$tmpfile = ([IO.Path]::GetTempFileName()) + ".crl" Set-Content -Path $tmpfile -Value $CRL.RawData -Encoding Byte & $tmpfile Start-Sleep 2 Remove-Item $tmpfile -Force
The cmdlet just saved CRL to a temp file and removed upon completion. When I explored MSDN for some useful CryptoAPI functions (really, I like them) I found one good function: CryptUIDlgViewContext. This function displays certificate (this part is implemented in an X509Certificate2UI class) CRL or CTL by using it's context handle. I just p/invoked this function and use it in Show-CertificateRevocationList cmdlet.
Hi,
When I use the "CertificateInCrl" function, if the certificate is found in the CRL (e.g. it is revoked), the return status is "False". If the cert is not found in the CRL, the return status is "True".
$crl.CertificateInCrl("C:\CertData\revoked.cer")
Not sure if this is a bug?
Btw, great work!
Thanks,
Paul
Post your comment:
Comments: