SslStream.AuthenticateAsClient() VERY slow with uncached CRL - c#

A server I connect to has recently changed it's SSL certificate. Since the change, SSL authentication is taking in excess of ten seconds to complete when the Certificate Revocation List is downloaded.
I'm using the RemoteCertificateChainCallback to validate the certificate, however the delay occurs BEFORE the callback is called, so it's not the building of the cert chain or any other action there that's causing the delay
The issue only occurs when the CRL is NOT CACHED, i.e. I need to delete the CRL cache (Documents&settings/[user]AppData/Microsoft/CertificateUrlCache or something similar) to repro it more than once on a single day.
If I disable CRL checking in the AuthenticateAsClient() call, the authentication is quick.
Using a network sniffer, I can see that when the CRL is eventually requested, it downloads almost instantaneously, so the delay is not a network latency one (at least not to the CRL server).
One odd thing that I see with the network sniffer is that after the initial SSL certificate retrieval from the server, there is a five second delay until the CRL is downloaded.**
Has anyone got any suggestions as to what may be going on during this stage, and what the delay may be caused by?
Thanks!
UPDATE: OK, I've used reflector and a memory profiler to delve into. AuthenticateAsClient. It looks like most of the time is spent building the certificate chain, i.e.:
if (!CAPISafe.CertGetCertificateChain(hChainEngine, pCertContext, ref pTime, invalidHandle, ref cert_chain_para, dwFlags, IntPtr.Zero, ref ppChainContext))
If I don't request CRL validation, then this returns almost instantaneously, with CRL-checking enabled, about 4 seconds.
I suspect I'll see the same delay if I manually attempt to build the chain in my RemoteCertificateValidationCallback.
This wouldn't really be a problem if the CRL was cached, however it seems like this caching is not working on a Windows7 customer. Why?? Well I guess that's the next task...
Could anyone explain what could be causing the chain-building to take so long?

It seems that here is an answer for this question:
https://blogs.msdn.microsoft.com/alejacma/2011/09/27/big-delay-when-calling-sslstream-authenticateasclient/
Digging a bit further to understand why CertGetCertificateChain took
so long, I saw that we were trying to download the following file from
the Internet:
http://www.download.windowsupdate.com/msdownload/update/v3/static/trustedr/en/authrootstl.cab
Why were we downloading this file? Well, this will happen by default
on Windows when we build the chain of a cert which root CA cert is not
installed in the system. This is called the Automatic Root
Certificates Update feature, and it is available on Windows XP/Server
2003 and later OS versions, including Windows 7/Server 2008 R2.

Related

Recurring ERR_CONNECTION_RESET in C# implementation of Selenium ChromeDriver

I've been battling the following error over the past few weeks trying to run UI tests with Selenium (Chrome 92):
WebDriverException unknown error: net::ERR_CONNECTION_RESET. (Session info: chrome=92.0.4515.107)
This occurs most often trying to locate HTML elements using XPath.
Originally, I added a retry loop which checks if the WebDriverException is thrown three times, and if so, recycle the IWebDriver and try again (via IWebDriver.Quit() and IWebDriver.Dispose(). This does not fix the problem, nor does having my application restart after exceeding the retry limit. Thus, I'm starting to think this is a problem at the system-level (Windows Server 2016). Even a reboot does not always fix the issue - when it starts to fail, it just totally freezes and I end up having to completely rebuild the environment.
I'm using the Selenium WebDriver NuGet v92.0.4515.4300; I realize this isn't in total parity with the version listed above, but having the exact same versions does not fix the problem.
I know 'unknown error' is vague. Does anyone have suggestions for what could be causing the problem?
Thanks in advance.
EDIT: Posting browser arguments here, rather than cramming them into a comment. I forgot to mention, there is an instance of Fiddler which we run as a proxy on the local system and use in conjunction with some tests. It's started and ended with the testing application.
The following are injected as options when creating the Chrome instance:
--user-data-dir=C:\temp\{uniquedir}
options.AddUserProfilePreference("credentials_enable_service", false);
options.AddUserProfilePreference("profile.password_manager_enabled", false);
And these are added as command-line arguments through Selenium:
--lang=en-GB
--ignore-certificate-errors
--no-experiments
--disable-translate
--disable-plugins
--enable-logging
--no-sandbox
--dns-prefetch-disable
--disable-gpu
--disable-field-trial-config
(We are doing some security-related detections, hence the disabling of certificate errors.)
You mentioned that you use Fiddler, if the issue is reproducible only when it is started, you can try to regenerate the HTTPS certificates
The issue can be due to the application itself. If the error is shown when you are not using Selenium/ChromeDriver you can ask your developers/devops to check the IIS settings or follow one of the suggestions below.
Check your Internet options from computer Settings->Network&Internet->Proxy. On my computer "Automatically detect Settings" is enabled.
You can also try to delete the Chrome browser cache.
Flush the DNS (ipconfig /flushdns)
It could be some rule (firewall, internal DNS record) configured for the network where is your Windows Server.
Check if the issue is reproducible in incognito mode (without any browser extensions enabled). You can pass --incognito argument when starting the driver.
Issue could be caused by a certificate or SSL/TLS problem.

Idenitfy if certificate is a host, intermediate, or root cert

I need a way to, given a list of X509Certificate2 objects, classify each as being either a root, intermediate, or host cert.
I've got a TCP service running in a docker container on AWS. I have set up a secret-management system and external provisioning system to provide certs to this service for TLS. However, the certs are PEM and this is infamously painful in c#. After strugging about 5 times over the last 8 years to even load PEM certs when a private key is involved, I have finally solved that cleanly with some new syntax provided in dotnet core. So yay.
Now my application has the certs with the private keys, and the chain, but still cannot make use of them because of the AuthenticateAsServer method interface which does not allow you to provide a chain. Rather you provide a certificate and then it will dig a chain out of the cert store if it can and then you have to look on the other end to see if the chain came out. (I will spare you an hour long tirade of my feelings about this pattern) Since I cannot provide a chain my only option is to install the chain prior to calling AuthenticateAsServer so that the super opaque black box will find them and send them.
Here is the problem. My chain is a big string of the kind you would get by cat'ing together the relevant OpenSSL created cert files. I've already written some code to split that text up into cert chunks and then initialize a collection of X509Certificate2 objects that I can foreach through and install each into a store. But which store? I need a way to check each one and know which store it should go into.
Here is my working idea in psuedocode
bool isSelfSigned = cert.Issuer == cert.Subject;
bool isCa = HasBasicConstraintCA();
if (isCa)
{
if (isSelfSigned) root=true;
else intermediate=true;
}
else if(some hopefully affirmative condition)
{
host=true;
}
Is this reasonable considering the situation? Am I going to hit any unexpected traps with this logic?
Is there any affirmative condition I can check for the host cert, other than it isn't either of the other two?
Your logic is almost correct; Please note that for end-entity certificates ( in your term host certificate) following values are possible;
no basicConstraint flag present
basicConstraint:CA:False is present
In both above cases it can be interpreted as end-entity ( host ) certificates. Please make sure your HasBasicConstraintCA() function checks for both cases. Apart the logic looks simple and wonderful.

C# x509Certificate2 cannot be created

im creating a certificate via a byte array I make from an Apple .p12 file.
X509Certificate2 cert = new X509Certificate2(AppleCertBytes, ApplePassword);
this works fine and creates the cert,BUT, only when im on localhost.
When i build and publish my WebService to the server it doesn't work.
I placed logs wherever I can, and i noticed that the request sort of gone when running this row.
I get no response for server and sometimes even get Err: connection_reset.
When i debug the process i don't see any errors, and i thought maybe the .dll file was the issue bug I think i checked it for like 11 times with different logs and it seems updated at anytime.
EDIT
No exception is thrown.
Is there anything I'm missing here? ApplicationPool maybe, plugin or such?
PLEASE NOTICE.
I DON'T want any plugins that create the certificate, i want a possible results.
Thanks.
You need to use another overload of the constructor to store the private keys in the local computer store.
Your constructor should read:
var cert = new X509Certificate2(
AppleCertBytes,
ApplePassword,
X509KeyStorageFlags.MachineKeySet);

Auto Update Downloading Latest .exe file - how to verify it's not been tampered with?

We have a small console application (under 200kb) that will be distributed to clients and we want to ensure they run the latest version (verified by a WCF service). Currently it downloads the new .exe file over HTTPS and replaces the current .exe file with it.
Assuming our server isn't compromised, this would be ok. However we also sign our .exe file with a Code Signing certificate. Is there a way to verify this and delete the file if it doesn't match? We would need to be able to verify and delete the file without it ever being executed in case it is a virus.
How can we verify our signed .exe file? For example, Windows will show if it is invalid:
Edit: would this code do the job?
X509Certificate basicSigner = X509Certificate.CreateFromSignedFile(file);
X509Certificate2 cert = new X509Certificate2(basicSigner);
if (cert.Subject.Contains("CN=MY COMPANY NAME IN CERTIFICATE"))
valid = true;
Edit: if we also check StrongNameSignatureVerificationEx, it comes back failed if one bit is changed in the file. Perhaps this is enough?
[DllImport("mscoree.dll", CharSet = CharSet.Unicode)]
static extern bool StrongNameSignatureVerificationEx(string wszFilePath, bool fForceVerification, ref bool pfWasVerified);
Edit: I've implemented this code too which calls WinVerifyTrust in WinTrust.dll to actually verify the Authenticode signature: http://www.pinvoke.net/default.aspx/wintrust.winverifytrust
Now, it checks if the digital signature contains the correct subject, is from a valid trusted root, the signature is valid and if the code is strong named with it's digital signature. This must be safe enough now?
This is a nice walkthrough including source code on the options available to achieve what you want...
Basically you need to pinvoke StrongNameSignatureVerificationEx since there is no managed API to do what you need.
Another option might be to call SignTool.
This is a pretty fundamentally wrong way to go about it. The only thing that a code signing certificate proves is the identity of the person or company that signed the EXE. The certificate authority merely proves that identity is valid. What you haven't proved at all is that it is your certificate, you only proved that it is somebody's certificate. An attacker could trivially replace your EXE with another one that was signed by him.
You'll probably object with "but can't I just verify it is mine!". And the answer is no, if the attacker can replace the EXE then he'll have no trouble replacing your checking code either. There is zero security in having the verification performed on the same machine.
Code certificates serve only one purpose, they prove the identity of the signer to the user. Making them do anything else is a security hole. The really bad kind, the kind that make you feel that your system is secure. And make you stop thinking about implementing real security.

Why does RSACryptoServiceProvider.VerifyHash need an LDAP check?

I recently encountered an odd problem with RSACryptoServiceProvider.VerifyHash.
I have a web application using it for decryption. When users running the web service were doing so over our VPN it became very very slow. When they had no connection or a internet connection they were fine.
After much digging I found that every time RSACryptoServiceProvider.VerifyHash is called it makes an LDAP request to check MyMachineName\ASPNET.
This doesn't happen with our WebDev (cassini based) servers as they run as the current user, and it is only really slow over the VPN, but it shouldn't happen at all.
This seems wrong for a couple of reasons:
Why is it checking the domain controller for a local machine user?
Why does it care? The encryption/decryption works regardless.
Does anyone know why this occurs or how best to work around it?
From this KB it looks like a 'wrinkle' in the code that needs sorting:
http://support.microsoft.com/kb/948080
Thanks (+1 & ans)
Tested and works.
From the KB article:
The SignData or VerifyData methods
always perform an OID lookup query
which is sent to the domain
controller, even when the application
is running in a local user account.
This may cause slowness while signing
or verifying data. Logon failure
audit events occur on the DC because
the client machine's local user
account is not recognized by the
domain. Therefore, the OID lookup
fails.
This is exactly what we were seeing.
We changed this line:
rsa.VerifyHash( hashedData, CryptoConfig.MapNameToOID( "SHA1" ), signature );
To this:
rsa.VerifyHash( hashedData, null, signature );
And that fixed it.

Categories