Issue with X509RevocationMode.Online in revoke certificate validation - c#

I am validating the certificate revocation in online mode but the url mentioned in CRL Distribution Point is not getting hit if the CRL is already cached in memory. I am using fiddler to verify if the URL is accessed or not. I am following these steps.
Run the fiddler.
Start certificate validation in X509RevocationMode.Online
Verify fiddler, the url mentioned in CRL Distribution Point is not caught.
Clear the crl from memory by the command certutil -urlcache CRL delete
Start certificate validation in X509RevocationMode.Online
Now the Fiddler caught the URL mentioned in CRL Distribution Point.
From above steps it is clear that the CRL's url will be hit only if the CRL is not cached. Now my questions are:
What are the scenarios when URL mention in CRL Distribution Point is accessed in online mode?
If CRL is already cached, how X509Certificate validated that the CRL is updated or not without hitting the URL?
Am I missing the concept of CRL?
Here is my code
private void BuildCertificateChain(X509Certificate2 certificate)
{
string error = null;
X509Chain certificateChain = new X509Chain();
certificateChain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
certificateChain.ChainPolicy.VerificationTime = DateTime.Now;
certificateChain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
certificateChain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 0, 15);
try
{
if (certificateChain.Build(certificate))
{
foreach (X509ChainElement element in certificateChain.ChainElements)
{
Trace.WriteLine(string.Format("Issuer = {0}\nSubject = {1}", element.Certificate.Issuer, element.Certificate.Subject));
element.Certificate.Verify();
}
}
else
{
error = string.Format("File {0} digital signature seems to be not valid due to a certificate in certificate chain being revoked. Revocation reasons are:\n", filename);
foreach (X509ChainStatus status in certificateChain.ChainStatus)
{
error += status.StatusInformation;
}
}
}
catch (Exception ex)
{
error = string.Format("Exception building certificate chain for executing application {0}. The error is {1}", _executingAppFileName, ex.Message);
}
if (!string.IsNullOrEmpty(error))
{
//SetError(error);
}
}
}

Using a cached version and not re-retrieving the CRL is usually a feature, not a bug.
What should happen:
The CRL issuing website should be using proper caching instructions to the https client who is retrieving the CRL for verification.
The cache on your machine should be implemented according to the instructions from the server. (By the way, disks are usually used for cached internet files, not memory.)
But, neither of the above may be true. If you want to be paranoid, you could flush the internet doc cache in the OS.
Re your questions:
What are the scenarios when URL mention in CRL Distribution Point is accessed in online mode? [When the CRL is not in the cache]
If CRL is already cached, how X509Certificate validated that the CRL is updated or not without hitting the URL? [The cache controls of https are used to assume that the cached version of the CRL is the same as the version on the remote server.]
Am I missing the concept of CRL? [Perhaps. The CRL process is not meant to be a gonzo-realtime synchronized multi-machine system. The idea is that most of the time, certs naturally expire from their expiration date. The revocation / CRL process should not be a normal process, should be more of an exception process. Your questions imply that the CRL is being updated on a second by second basis--so quickly that normal web caching techniques are not acceptable. Why do you believe this? What are you trying to protect yourself against? Are humans making the decision to revoke the certs before their normal expiration time or machines?]
To put it another way, if the CRL is being updated all the time, then it should be sent with the caching headers set accordingly. In that case, you should test that your OS is correctly not caching the result. If you're worried that the OS is wrong, then you should explicitly delete the cache.
Added:
A blog entry about examining malware's digital certificates.

Related

x509Chain.build fails, certutil -verify passes

I have a root certificate and a leaf. The leaf has a CRL URL OID extension which points to a valid online location. Doing this:
certutil -verify .\leaf.cer
fails with
ERROR: Verifying leaf certificate revocation status returned The revocation function was unable to check revocation because the revocation server was offline. 0x80092013 (-2146885613 CRYPT_E_REVOCATION_OFFLINE)
If I do this:
certutil -verify .\leaf.cer .\root.cer
Then verification passes, and I see the CRL getting pulled from online in Fiddler.
In my C# code, I do this:
X509Chain childCertChain = new X509Chain();
childCertChain.ChainPolicy.ExtraStore.Add(rootCert);
childCertChain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
childCertChain.ChainPolicy.UrlRetrievalTimeout = TimeSpan.FromSeconds(10);
if (!childCertChain.Build(childCert))
{
// The root cert is not in the windows certificate store, that is fine
if (childCertChain.ChainStatus.Length != 1 || childCertChain.ChainStatus.First().Status != X509ChainStatusFlags.UntrustedRoot)
{
throw new Exception("Certificate validation error.");
}
}
This will hit my exception, and even though chainElements will be correctly filled with the 2 certs, ChainStatus will show:
OfflineRevocation, RevocationStatusUnknown
I also will not see any web requests in Fiddler. I can programmatically download the CRL given the URL so it's not my debug environment AFAIK. Any ideas how to get x509Chain.Build to succeed?
I've read your code from PC and figured what's up: your root certificate is not trusted on your system. Windows CryptoAPI throws CERT_E_UNTRUSTEDROOT when root certificate is not trusted. Along with this, CryptoAPI terminates revocation checking and throw two addition errors: CRYPT_E_NO_REVOCATION_CHECK and CERT_E_UNTRUSTEDROOT, because revocation checking was skipped. In addition with untrusted root, CryptoAPI skips other checks (such as constraints, for example), but do not report them. Everything is correct and as expected.
You are trying to allow untrusted root, but do it incorrectly, because this way you skip other checks (as mentioned in previous paragraph) and your logic is vulnerable.
You can exclude untrusted root exception in chain settings and force CryptoAPI to continue validation and return success if no other errors found, but I strongly recommend to not do that, because then you are open to any kind of MITM.
You have two options:
make root certificate trusted by a machine (local system). This may not be possible.
call CertCreateCertificateChainEngine function directly and put your root certificate in hRestrictedTrust member of CERT_CHAIN_ENGINE_CONFIG structure.
If you are on Windows, 2nd approach is the most secure and reliable. As of now, .NET's X509Chain doesn't implement this functionality. It requires extra coding, but there are no much alternatives.

Using C#, how to programmatically determine if a certificate in a Windows certificate store has been disabled

I'm currently working on refining communications between mutually authenticated client/server applications using HTTPS. I am currently building validation logic into a C# client to help identify configuration issues when a TLS connection fails. In verifying the connection, I validate that the root CA certificate presented by the server is installed on the client, in the appropriate store, and is valid. I'm using X509Store to pull the X509Certificate2, and validating it using X509Chain.
My issue is that the certificate will report as valid even if the certificate has been disabled via MMC. So the TLS connection will fail, despite the chain reporting as valid.
It's an unlikely case, but one I'd like to handle by reporting something like "Could not connect because root CA is disabled."
Could anyone point me in the direction of a .NET or Win32 call that could be made to determine the value of "Certificate Purposes" for a certificate? Or to read the "Certificate Status" for a cert?
I read through MSDN's listing of what's in the System.Security.Cryptography namespace, and started looking into CryptoAPI and CNG, but didn't find anything so far.
Thanks!
That dialog does not "disable" a certificate it disables it "for all purposes". What this means is it counts as having an empty Enhanced Key Usage extension for purposes of EKU validation.
Normally a root certificate (or an intermediate CA certificate) will not have an EKU extension, so if you do a chain build with any ApplicationPolicy value it will be a match. Once you set it to Disable for all purposes you'll get a chain error X509ChainStatusFlags.NotValidForUsage.
If you want to build something for validating TLS you'd check either the Server Authentication or Client Authentication EKUs (depending on what you're checking):
// Server EKU or Client EKU
Oid eku = forServer ? new Oid("1.3.6.1.5.5.7.3.1") : new Oid("1.3.6.1.5.5.7.3.2");
// Test if it's valid for that purpose
chain.ChainPolicy.ApplicationPolicy.Add(eku);
If you want to "Disable" a root CA altogether, add a copy of the certificate to the Disallowed store (called "Untrusted Certificates" in the UI). That will result in a chain build producing X509ChainStatusFlags.ExplicitDistrust.

Identityserver fails to load selfsigned certificate

I'm trying to set a Certificate for identityserver and it keeps failing with a "no access to private key error".
Taking it out of identityserver, the following code throws an access denied error
static X509Certificate2 GetCertificateFromDisk()
{
using (var stream = File.Open(#"patht-to-pfx", FileMode.Open))
{
var cert = new X509Certificate2(ReadStream(stream), "password", X509KeyStorageFlags.MachineKeySet);
return cert;
}
}
When running the code as administrator it works fine, not when running it under my own account. Eventually I want to run it as localsystem.
I even added 'Everyone' under the certificates private key permissions in my local computer certificate store,
screenprint here
... still I get the exception.
What is wrong here? Going Crazy about it
Update
Great tips from CryptoGuy in the answer below. Important note: Opening the file is not correct only Identityserver3 still failed when getting the certificate from the store. What made it work was to regenerate the certificate using Keith Sparkjoy's tool SerfCert. My previous certificate was generated using powershell. So keep in mind that powershell certificates have issues with accessibility of private key. Thanks to Keith for the tool!
There are few things to consider.
1) you are performing write access to Local Machine store. X509KeyStorageFlags.MachineKeySet attempts to save private key to Local Machine store. Therefore, you need administrator permissions to write there. You should remove this flag to perform read-only access
2)
Documentation says that adding permissions in MMC (manage private key-option on a certificate) should allow this, but it doesn't seem to work
it works on an already saved private keys.
What you really should do is to import certificate and private key to Local Machine store and then configure your application to reference installed certificate.
3) if your application runs under unpriveleged account and the key don't need to be shared, then you should use Current User store.

How to proper validate SSL certificate with X509Certificate2 on Mono and multiple platforms

I have to validate several SSL certificates in a none-browser application for HttpRequests and websocket connections which should run on IOS, Android and Linux.
When a connection via HTTPS happens I receive an array of X509Certificate2 objects where the bottom one is the server certificate and the most top one the root CA (hopefully). As an example, when I connect to https://google.com I receive 3 X509Certificate2 with the following SubjectName.Name:
0 : "CN=google.com, O=Google Inc, L=Mountain View, S=California, C=US"
1 : "CN=Google Internet Authority G2, O=Google Inc, C=US"
2 : "CN=GeoTrust Global CA, O=GeoTrust Inc., C=US"
I now need to verify the certificate validation with the given information and the following verifications:
Chain-of-trust verification
Hostname verification
Certificate revocation verification
What I tried and not understood and failed:
When I call the X509Certificate2.Verify() method on each certificate independently it returns false everytime. I also do not understand why it can return anything else then false because the verification happens independently. Instead the complete chain, meaning all certificates, should be checked as far as I understood the theory.
I then used the X509Chain class:
foreach (X509Certificate2 cert in allthreecerts)
{
X509Chain chain = new X509Chain();
X509ChainPolicy chainPolicy = new X509ChainPolicy()
{
RevocationMode = X509RevocationMode.Offline,
RevocationFlag = X509RevocationFlag.EntireChain
};
chain.ChainPolicy = chainPolicy;
if (!chain.Build(cert))
{
foreach (X509ChainElement chainElement in chain.ChainElements)
{
foreach (X509ChainStatus chainStatus in chainElement.ChainElementStatus)
{
Debug.WriteLine(chainStatus.StatusInformation);
}
}
}
}
This prints out RevocationStatusUnknown and OfflineRevocation for each certificate.
Again, I do not understand why this should work since a chain is build which each certificate independently. Should not be the parent certificate be the Issue of the child certificate all down to the root CA?
What I think I need somehow but do not know how.
In order to verify the certificate revocation all clients need to provide a certificate revocation list. Where do I get such a list and where to I load it and tell the chain to use this local list?
The same problem is the Chain-of-trust verification since the last certification should be a root certification and must be one of the clients trusted root CA's. So I have to load, for example, all root CA which are shipped with Firefox and check if the root CA is one of them? What is the best way to do that?
[Update]
I made a copy paste mistake in the google.com example where I pasted two identical certificate subject names.
When a connection via HTTPS happens I receive an array of X509Certificate2 objects where the bottom one is the server certificate and the most top one the root CA (hopefully).
If you really did get root ca certificate from SSL server then the SSL server is not configured correctly. It should return its server certificate and the whole chain Except the root CA certificate.
When I call the X509Certificate2.Verify() method on each certificate independently it returns false everytime.
As stated in the documentation of X509Certificate2.Verify method This method builds a simple chain for the certificate and applies the base policy to that chain. Now what is the base policy I do not know. But on of the default values of ChainPolicy is RevocationMode = X509RevocationMode.Online. As for Mono the source of this method can be found here. The source of Mono implementation of X509Chain can be found here.
This prints out RevocationStatusUnknown and OfflineRevocation for each certificate.
What else should it print when you specified RevocationMode = X509RevocationMode.Offline and there are no CRLs or OCSP responses in cache (probably)?
In order to verify the certificate revocation all clients need to provide a certificate revocation list. Where do I get such a list and where to I load it and tell the chain to use this local list?
Each certificate (well except root ca certificate) in the chain contains a link (or links) to CRL. .NET and most likely Mono too has an implementation that will find link to CRL in the certificate, will download it and will check if the certificate is not revoked.
The same problem is the Chain-of-trust verification since the last certification should be a root certification and must be one of the clients trusted root CA's. So I have to load, for example, all root CA which are shipped with Firefox and check if the root CA is one of them? What is the best way to do that?
No, RevocationFlag = X509RevocationFlag.EntireChain will do that for you using some store that mono uses. I don't know if it is the Firefox store but on Linux it has its own store and you can import root ca certificates from Firefox store. Check ChainElements and see for yourself what certificates did it find.
I would suggest that you build the chain with SSL server certificate only (as this will check all certs in the chain upwards) with RevocationMode = X509RevocationMode.Online and RevocationFlag = X509RevocationFlag.EntireChain. I would also try to set ExtraStore property of X509Chain to a list of certificate you got from SSL server. After the Build method I would chceck ChainStatus property of X509Chain object which is an array of statuses. I would pick all statuses that have not set X509ChainStatus.Status to NoError. If there will be any such status I would throw and log each X509ChainStatus.Status and X509ChainStatus.StatusInformation.
HTH

C# How to get .dll or .exe files digital signer certificate info even the certificate expired?

I'm using C# check some exe or dll file digital certificate.
Assembly asm = Assembly.LoadFrom(assembly);
Module module = asm.GetModules().First();
System.Security.Cryptography.X509Certificates.X509Certificate certificate = module.GetSignerCertificate();
// it will null if the certificate time expired
if (null != certificate)
{
if (certificate.Subject.Contains("Company Name"))
Console.Write("success");
else
Console.Write("invalid");
}
else
{
Console.Write("failed");
}
But now problem is : If the certificate time is expired, or because modify local time to after certificate time the code module.GetSignerCertificate() always get null.
Because I just want check the assembly has digital certificate and company name, not check the time, So Is there any way can check it use other way or not let it null?
This code does work well in my case, including expired certificate case.
using System.Security.Cryptography.X509Certificates;
...
// "assembly" is a string of full path for .exe or .dll.
var cert = X509Certificate.CreateFromSignedFile(assembly);
The module is incorrectly signed. A properly signed module includes the timestamp when the module was signed. The validation rule dictates that the certificate should had been valid at the moment of signing (this, at the timestamp). Read this answer here https://stackoverflow.com/a/3428386/105929 for more details.
If this is your own module, then the best action is to modify your release sign-off to include a timestamp in the signature. Read Time Stamping Authenticode Signatures to understand how to do it (time stamping involves a URL service provided by your certificate CA). Also read Everything you need to know about Authenticode Code Signing.
As to your question: if you look at module.cs reference code, you'll see that the implementation is native and not much you can do about it to modify it:
[System.Security.SecurityCritical] // auto-generated
[ResourceExposure(ResourceScope.None)]
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity]
static private extern void GetSignerCertificate(RuntimeModule module, ObjectHandleOnStack retData);
You could instead use the signtool.exe verify command, afaik it has options to display all certificates, including expired ones on modules lacking a proper timestamp.
The module is correct. Because before it expired, it can use very well. not get null. after it expired, or I change my system time. it will get null
This can easily be proven to be incorrect. Find an old correctly signed assembly on your machine, one distributed by a serious vendor that knows what is doing. Eg. I found this on my machine: c:\Program Files (x86)\Microsoft SQL Server\100\Setup Bootstrap\Release\x64\Microsoft.AnalysisServices.DLL. Look at the Digital Signature properties:
Do note the signature timestamp (20110922) and the certificate expiration (20120521).
Write a small app to get the cert using CLR API:
static void Main(string[] args)
{
Assembly asm = Assembly.LoadFile(#"c:\Program Files (x86)\Microsoft SQL Server\100\Setup Bootstrap\Release\x64\Microsoft.AnalysisServices.DLL");
Module m = asm.GetModules()[0];
var cert = m.GetSignerCertificate();
Console.WriteLine("{0}", cert);
}
Is the certificate found? Yes. Is the certificate expired? Yes. Is the module properly signed, using a timestmap? Yes.
QED

Categories

Resources