Idenitfy if certificate is a host, intermediate, or root cert - c#

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.

Related

Validating a Code Signing Certificate on a Windows Application

I am trying to programmatically validate a code signing certificate as described in this question from 2014. After following the suggestions, I am still not able to find a consistent working solution to determine when my .exe has been tampered with (i.e. the hash no longer matches)
The WinVerifyTrust method from wintrust.dll is the most accurate however I seem to getting a false positive after my executable has been obfuscated/VM'd. In this scenario, I am getting a result of (int)2148204800 even though the .exe is validly signed and un-tampered with. I am also averse to using Windows API calls as I want the code to be as portable as possible.
The other methods such as X509Certificate.CreateFromSignedFile().Verify() and the Powershell method of course do not validate the exe's hash so return true.
Is there any new way that's come about in the last 4 years to validate the hash of the .exe, preferably in pure .NET? I am happy with a solution that simply validates that the hash and not the entire certificate (if that's possible).
If you pull from Mono.Security as a Nuget Package they have a nice AuthenticodeDeformatter class that will verify code signing certificates:
var authenticode = new AuthenticodeDeformatter(Filename);
authenticode.IsTrusted(); //Should return False if tampered with.
You can use the WinVerifyTrust API.
A largish working class with PInvoke calls too long to post here, is available at PInvoke.net: WinVerifyTrust. You can copy that code verbatim and call WinTrust.VerifyEmbeddedSignature() to verify the signature and file hash.
Additionally, it's also a good idea to check the certificate (first) to make sure it's the correct certificate. Somebody could change the file, then sign with a new certificate.
To put it together:
Check the certificate
Use the WinTrust helper
var theSigner = X509Certificate.CreateFromSignedFile(filePath);
var theCertificate = new X509Certificate2(theSigner);
// ensure it's my application's cerfificate
var subject = theCertificate.Subject;
if (!subject.Contains("ACME Inc.")
return false;
// verify the authenticode signature and file hash
if (WinTrust.VerifyEmbeddedSignature(filePath))
return true;
return false;

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.

SslStream.AuthenticateAsClient() VERY slow with uncached CRL

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.

Inserting Certificate (with privatekey) in Root, LocalMachine certificate store fails in .NET 4

I'm having problems inserting a new CA certificate with privatekey in the Root certificate store of the localmachine.
This is what happens:
//This doesn't help either.
new StorePermission (PermissionState.Unrestricted) { Flags = StorePermissionFlags.AddToStore }.Assert();
var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
privkey.PersistKeyInCsp = true;
//This shouldn't be necessary doesn't make a difference what so ever.
RSACryptoServiceProvider.UseMachineKeyStore = true;
cert.PrivateKey = privkey;
store.Open (OpenFlags.MaxAllowed);
store.Add (cert);
store.Close ();
The certificate gets inserted and it all looks dandy: (see!)
Note: is says it has a privatekey.
So you'd say one would be able to find it with FindPrivateKey
C:\Users\Administrator\Desktop>FindPrivateKey.exe Root LocalMachine -t "54 11 b1 f4 31 99 19 d3 5a f0 5f 01 95 fc aa 6f 71 12 13 eb"
FindPrivateKey failed for the following reason:
Unable to obtain private key file name
Use /? option for help
It's cute .... BUT IT'S WRONG!! (2 stupid dogs reference)
And the Certificate export dialog gives me this very fine message:
This code is run while impersonating an administrator using this snippet: click here
I'd just love to know WHY?
(tested on Windows Server 2008 R2 & Windows 7)
I'll be damned!
It works when I compile it to v3.5!!!!
What to do?
I had exactly the same problem and the solution turned out to be really simple.
All I had to do is to pass
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet
to X509Certificate2's ctor.
Now you are using the DotNetUtilities to convert the bouncycastle certificate to the .net one, but the helper method creates the .net cert with the DefaultKeySet (instead of MachineKeySet + PersistKeySet
).
And arrange the private key like this:
var cspParams = new CspParameters
{
KeyContainerName = Guid.NewGuid().ToString(),
KeyNumber = (int)KeyNumber.Exchange,
Flags = CspProviderFlags.UseMachineKeyStore
};
var rsaProvider = new RSACryptoServiceProvider(cspParams);
I hope this helps.
It seems to me you should import the key in a little other way. See http://support.microsoft.com/kb/950090 for an example.
Moreover I find not good to save private key in UseMachineKeyStore. In the most cases you need import certificate with the private key in My store of some user and import in Root only certificate without private key.
It you do need save private key on Machine key store, that you should at least protect the key for reading only for some selected users and not from Everyone. The key container is just a file in the file system (see files in the diriectory "%ALLUSERSPROFILE%\Microsoft\Crypto\Keys") which has security descriptors like other files in NTFS. To change security descriptors of the files you can use CspKeyContainerInfo.CryptoKeySecurity property and AddAccessRule, RemoveAccessRule and so on.
UPDATED: First of all sorry for the long answer.
I could divide your program code in two parts. In the first part you generate a self-signed certificate which can be used as a CA certificates and you save it as rootcert.pfx file. In the second part you import the certificate, but use RSACryptoServiceProvider filled with properties of previous created key instead of using rootcert.pfx.
I suggest to replace the second part of your code to more standard and simple code: import certificate with the private key from rootcert.pfx like it described in http://support.microsoft.com/kb/950090. It works very well.
I don't use myself the BouncyCastle, so I could not comment the first part of your code, but in general what you do in the code you could do also with respect of MakeCert.exe utility from the Windows SDK. You can do like following
MakeCert.exe -pe -ss MY -a sha1 -cy authority -len 2048 -m 120 -r -# 1
-n "CN=Some Root CA, C=NL, OU=BleedingEdge, ST=Somewhere, L=Somelane"
Then you can export certificate with or without private key with respect of Certificate Snap-In (for mmc.exe). In the example above I don't restrict CA for some special EKU, so you can use it without any restriction, but if you do need the restrictions you can just add additional parameters to MakeCert.exe. You can also use MakeCert.exe to create other certificate which are signed with the CA certificate. So you are able to make small PKI with respect of MakeCert.exe only.
It seems to me that creating of the certificate is really a separate part of your code. Your main problem is in the second part.
If you want import CA certificate you should take in consideration some important things:
You should import it in Root or AuthRoot in localMachine on every (or many) computer of your organization, but you should import the certificate without the private key. You can do this with respect of following
CertMgr.exe -add -c CA.cer -s -r localMachine AuthRoot
You should import CA certificate with private key on the computer on one computer and only for the user who will issue other certificates (who will sign new certificates with the private key of CA). One use to import the certificate in the My certificate store of CurrentUser. So the code on the computer could looks like
following:
// import PFX
X509Certificate2 cert = new X509Certificate2 (#"c:\Oleg\rootcert.pfx", "password",
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
// save certificate and private key
X509Store storeMy = new X509Store (StoreName.My, StoreLocation.CurrentUser);
storeMy.Open (OpenFlags.ReadWrite);
storeMy.Add (cert);
// get certificate without private key
// one can import certificate from rootcert.cer instead
byte[] certBlobWithoutPrivateKey = cert.Export (X509ContentType.Cert);
// save pure certificate in Root of the local machine
X509Certificate2 certWithoutPrivateKey = new X509Certificate2 (certBlobWithoutPrivateKey);
X509Store storeRoot = new X509Store (StoreName.Root, StoreLocation.LocalMachine);
storeRoot.Open (OpenFlags.ReadWrite);
storeRoot.Add (certWithoutPrivateKey);
The code will work if you do will change StoreName.My and StoreLocation.CurrentUser to another values, but I don't recommend you to do this.
In general importing of certificates in .NET code look like a little strange and not shows what will be done under the hood. Windows knows only Key Containers where private keys (to be exactly the key pair) will be saved with respect of CSP and Certificate Stores where certificates will be saved (see http://msdn.microsoft.com/en-us/library/bb204781.aspx about location of the store). To be able to save information about the key container in the certificate store Microsoft introduced so named Certificate Extended Properties. If you use in .NET properties of X509Certificate2 like Thumbprint, FriendlyName, HasPrivateKey, Archived and so on you work with the Extended Properties of the certificate. So I recommend you to import CA certificate twice. One in Root or AuthRoot without setting CERT_KEY_PROV_INFO_PROP_ID Certificate Extended Properties and one more time in My store with the setting of information about the place of Key Container with the private key (CERT_KEY_PROV_INFO_PROP_ID). Moreover you can consider to remove private key directly after the usage, import it only if you really need to use it and not hold it permanently. All this is important to have better security.
I have encountered this problem and it seems that even the user with which you are running the FindPrivateKey tool does not have access to the key and therefore you would get the "Unable to obtain private key file name" message. You could run the tool as LocalSystem process.
More information here:
http://www.itsolutionbraindumps.com/2011/02/finding-private-key-for-your.html
Dinko
new X509Certificate2(localPFXPath, inputPass, X509KeyStorageFlags.MachineKeySet & X509KeyStorageFlags.PersistKeySet); with the & instead of the | worked for me.
Usually Certificates in Root won't have private key to manage. You should import to My folder if you are associating key in the web request. I have TLS/SSl exception where I have chain of client certificates. If you store all the chain of certificates in My store then I got rid of that exception. Where the problem is with user accounts. Utility to store the certificates uses current user account and the actual application runs on system account.
The basic problem is that the .NET certificates API is just a wrapper around the C++ advapi32 certificate manager api, so you don’t get to specify all the options that get passed to this api that is actually responsible for sticking the cert into the Windows cert store and persisting the keys. The bottom line is that the “UseMachineStore” option needs to get passed to the CspProviderFlags which in turn gets passed to the CAPI.CRYPT_MACHINE_KEYSET. This is the little guy that determines whether the key gets persisted for real or not. There seem to be several different reasons why this option doesn’t get set even though you set the X509KeyStorageFlags.PersistKeySet and MachineKeySet and Exportable. All these options only live as long as the stupid key stays in the C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\ folder. If CRYPT_MACHINE_KEYSET doesn’t get set at time of import then advapi32 blows the key away as soon as the certificate handle gets disposed by GC.
Solution: Add the certificate to the Trusted Root BEFORE you import the certificate into the Personal machine store. In reading the logs from CAPI2, I actually see two calls to “X509 Objects” every time the Certificate is “Imported”. One always has the <Flags value="20" CRYPT_MACHINE_KEYSET="true"/>, (what we want) but the other does not UNLESS “Verify Chain Policy” returns no errors. So it looks like advapi32 is checking the “validity” of the cert and either returns an exception that gets swallowed by X509Certificate2 (I love how many empty catch blocks they have in that code) or advapi32 just unilaterally decides to not persist the keys for untrusted certificates. (By the way, I suspect this is a behavior change between 2008 and 20012, but I haven’t proven that.) To work around this, I added an If-check to my code to add the certificate that if the Issuer equals the Subject (it is a self-signed cert) then add the cert to the Root before adding it to My.
if (certificate.Issuer.Equals(certificate.Subject))
{
using (X509Store store = new X509Store(StoreName.Root, StoreLocation.LocalMachine)) {
store.Open(OpenFlags.ReadWrite);
store.Add(certificate);
store.Close();
}
}
using (X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine)){
store.Open(OpenFlags.ReadWrite);
store.Add(certificate);
store.Close();
}
Note: I have found that this is unneccessary if using a certificate that does not have a Subject Key Identifier already in it. Somehow when you trigger the api to actually generate the SKI instead of handing it in, it triggers the conditional to pass the magic CRYPT_MACHINE_KEYSET flag to advapi32.

Categories