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

Verify CRL signature

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:\>

Determining whether the certificate is listed in CRL

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.

Encoding CRLs

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.

Display CRL in UI

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.


Share this article:

Comments:

Paul

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:

Please, solve this little equation and enter result below. Captcha