X509Certificate.CreateFromSignedFile retrieves only the leaf certificate - c#

I have an exe file signed using signtool.exe. If I view the signature using Windows (Right click -> Properties -> Digital Signatures -> Details -> View Certificate -> Path), the chain looks like this, as expected:
Verisign
|
---Symantec Class 3 SHA256 Code Signing CA
|
---Me
However, if I load the certificate using the .NET API X509Certificate.CreateFromSignedFile(path) and look at the certificate using X509Certificate2UI.DisplayCertificate(cert), I only see the leaf certificate. Of course, because the chain is missing, trying to build the chain using X509Chain results in a failure.
Is this the expected behavior, and is there any way to build the entire chain using managed .NET code (read, without using WinVerifyTrust p/invoke)?

Yes, and no.
In your UI workflow you when you push "View Certificate" you switch from the file properties dialog to the CertUI dialog. CertUI (probably) only looks at the leaf/end-entity certificate and then builds the cert chain itself. So at that point it's slightly moot as to what else was in the signed file.
You can get slightly further in one call by reading all of the certificate information which was embedded in the file. My local testing shows that it wrote the EE cert (because it had to) and also the intermediate CA (with no signature line) but not the root certificate (because you normally omit root certificates in transfer... the other party either already has it or won't trust it, so it's wasted bytes).
var coll = new X509Certificate2Collection();
coll.Import("signedfile.exe");
// My coll had 2 certs at this point.
So you could pass all of those certificates into X509Chain.ChainPolicy.ExtraStore in case it needs help resolving intermediates, but to determine the root you still need to build the chain.
using (X509Certificate2 cert = new X509Certificate2("signedfile.exe"))
{
X509Chain chain = new X509Chain();
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreNotTimeValid;
bool validChain = chain.Build(cert);
if (!validChain)
{
// Whatever you want to do about that.
foreach (var status in chain.ChainStatus)
{
// In reality you can == this, since X509Chain.ChainStatus builds
// an object per flag, but since it's [Flags] let's play it safe.
if ((status.Status & X509ChainStatusFlags.PartialChain) != 0)
{
// Incomplete chain.
}
}
}
X509Certificate2Collection chainCerts = new X509Certificate2Collection();
foreach (var element in chain.ChainElements)
{
chainCerts.Add(element.Certificate);
}
// now chainCerts has the whole chain in order.
}

Related

Cant get private key from certificate

So, I having trouble to get the private key from the certificate. This is my first time working with certificates and I have tried to find a solution to this but I cant make i work. It's a console application and a need the key to sign a SOAP message/request.
Here is a code sample; (Let me know if you need anything more)
public static X509Certificate2 FindCertificate(string issuedBy)
{
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
//We gets the certificate from store
var certificate = store.Certificates.Cast<X509Certificate2>().FirstOrDefault(c => c.Issuer.Contains($"CN={issuedBy}"));
if (certificate.HasPrivateKey) // Result: True
{
//PrivateKey throws error: Keyset dosent exist
var key = certificate.PrivateKey;
}
if (certificate == null)
{
throw new ArgumentException($"Could not find certificate issued by: '{issuedBy}'", nameof(issuedBy));
}
var expireDate = DateTime.Parse(certificate.GetExpirationDateString());
if (expireDate < DateTime.Now)
{
throw new Exception($"Certificate has already expired: '{expireDate}'");
}
return certificate;
}
An alternative to your own solution (so you don't have to run VS in administrator mode is to give your own user privileges to read the private key.
Open the certificate manager for your machine certificates (type in "certificates" in Windows home menu search and choose "Manage computer certificates"
Find the certificate and right click -> "All Tasks" -> "Manage Private Keys..."
Click "Add..."
Type in your username (and click "Check Names" to see if you typed it in correctly)
Click "OK" and "OK"
Now you should be able to run your application (and VS) normally with access to the private key.
I was able to get it to work now just by start Visual Studio in Admin mode.

How can i write in the "Enterprise Trust" Store?

I have a .p7b file with a few certificates and i want to install them in the "Enterprise Trust" Store. A program i want to use expects it there.
I have writte code in c# which extract all the certificates from the file and installs them into a X509Store ("Storename.My") which works.
If i try to use the same code to write in a different store it (which already exists) it creates a new empty store and writes in there.
The StoreName.My is taken from system.Security.Cryptography.X509Certificates public enum StoreName, but there is no option for the "Enterprise Trust" store.
So i tried to use the constructor where i can give the StoreName as a string.
I use the certmgr from windows to check what certificats are stored in which stores.
// open file
var certificateStore = new CmsSignedData(new
FileStream(_tempFileName.ToString(), FileMode.Open));
// get all certificats
IX509Store x509Certs = certificateStore.GetCertificates("Collection");
var a = new ArrayList(x509Certs.GetMatches(null));
// this works
var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
// this doesnt work
// var store = new X509Store("Enterprise Trust", StoreLocation.CurrentUser);
// open store
store.Open(OpenFlags.ReadWrite);
// write in store
foreach (object o in a) {
var cert = (Org.BouncyCastle.X509.X509Certificate)o;
var cert509 = new X509Certificate2();
cert509.Import(cert.GetEncoded());
store.Add(cert509);
}
store.Close();
How do i write correctly in the a store which isnt the StoreName enum?
If you want to be sure you aren't creating a new store, you can use the OpenFlags value OpenExistingOnly. Asserting that flag and checking for, e.g. "Trust2" yields System.Security.Cryptography.CryptographicException: The system cannot find the file specified. Therefore we get the best level of assurance that "Trust" is the right answer by specifying it as:
using (X509Store store = new X509Store("Trust", StoreLocation.CurrentUser))
{
store.Open(OpenFlags.ReadWrite | OpenFlags.OpenExistingOnly);
...
}
(Note that this is less good to use on Linux (.NET Core), because only Windows pre-initializes the stores)
We can get confirmation on the programmatic name to the display name via the certutil.exe command-line utility:
>certutil -store trust
trust "Enterprise Trust"
CertUtil: -store command completed successfully.
(I just don't have anything in that store)

PushSharp's notification to Apple generate temp RSA files on server

I'm having a problem with my WCF push notification service.
Every time it sends a push notification to apple, a new RSA is generated inside the "C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys" folder.
I think that the problem causing this might be related to the Apple p12 Certificate.
Why does that happen? Is there any way to prevent the generating of those RSA?
I'm using PushSharp library and my code looks like this:
pushBroker = new PushBroker();
pushBroker.RegisterAppleService(
new PushSharp.Apple.ApplePushChannelSettings(false, appleCert, p12Pass, false)
, null);
The temporary file is where Windows has put the RSA private key that it loaded out of the appleCert PFX blob.
If the file appears, then disappears (since you called it a "temp" file), then everything's working as expected. If it appears and stays forever then PushSharp is loading the certificate with PersistKeySet, which says that the private key shouldn't be deleted.
I don't know if https://github.com/has-taiar/PushSharp.Web/blob/master/PushSharp.Apple/ApplePushChannelSettings.cs is the real source of ApplePushChannelSettings, but it'll do. Looking at that source, they seem to be using PersistKeySet, making the file permanent.
You have two practical approaches:
1) Load the certificate into an X509Store as a one-time operation (using PersistKeySet on load because you need to for store persistence). The file becomes permanent, but gets used for forever, so it's cool.
2) Load the certificate yourself from the PFX and (ideally) Dispose the certificate when you're done. I don't know if you have any sort of on-shutdown notification that you can more-or-less reliably use, but calling cert.Dispose() (or, on older Framework versions, cert.Close()) is more reliable than waiting on the GC and Finalizer to do the cleanup for you.
If you do have a good shutdown event:
// Note that I'm not specifying any X509KeyStorageFlags here.
// You don't want it PersistKeySet (that's the whole point) and you don't
// need it to be exportable (unless you get exceptions without Exportable, in which case
// PushSharp is doing something naughty).
private X509Certificate _cert = new X509Certificate2(appleCert, p12CerPass);
...
_broker.RegisterAppleService(new PushSharp.Apple.ApplePushChannelSettings(_cert, false));
...
// In some OnShutdown type location
_cert.Dispose();
If not:
// Still don't specify any load flags. Especially not PersistKeySet.
var cert = new X509Certificate2(appleCert, p12CerPass);
_broker.RegisterAppleService(new PushSharp.Apple.ApplePushChannelSettings(cert, false));
No matter what you'll still end up with a permanently leaked file every time your process abnormally terminates, since the file-deletion code won't get a chance to kick in.
A solution exists to completely avoid creating the temporary file, and that is to use the EphemeralKeySet flag which was added to .NET Core 2.0 (currently in preview). It's not yet available in .NET Framework.
// Keep the private key in memory, never let it touch the hard drive.
var cert = new X509Certificate2(appleCert, p12CerPass, X509KeyStorageFlags.EphemeralKeySet);
_broker.RegisterAppleService(new PushSharp.Apple.ApplePushChannelSettings(cert, false));
But I don't know if PushSharp is available for .NET Core.
I had the same problem and i found the solution
to prevent generate key every time you send new push notification,
the code should be like this. and it work perfect for me
var cert = X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable;
var certificate1 = new X509Certificate2(appleCert, p12CerPass,cert);
_broker.RegisterAppleService(new PushSharp.Apple.ApplePushChannelSettings(certificate1, false));

Visual C# - Checking installed certificate - Does nothing if it doesnt exist

This is probably a very basic error, but i am pulling my hair trying to understand why this is happening.
I have a check against the store certificates if it contains a certain certificate with a name. If it doesnt, then update a label.text.
It does the check just fine and it finds it, but no matter what i do it doesnt handle if it isnt there. Its not doing anything. No text beeing displayed. I have also tried a simple else without the (!mCert) but still no go.
// Certificate controls
X509Store store = new X509Store("My", StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
foreach (X509Certificate2 mCert in store.Certificates)
{
if (mCert.Issuer.Contains("Cert-Name"))
{
label3.Text = "Found certificate";
}
else if (!mCert.Issuer.Contains("Cert-Name"))
{
label3.Text = "Didnt find the certificate";
}
}
So the else if statement isnt doing anything. Even if i just put an else instead there it isnt updating the label3.text.
If the store is empty and cant find any certificates the foreach will never run, thats why the if statements never get processed.
Adding this before the foreach will solve it
if (store.Certificates.Count==0)
"My" store of local machine is very likely to be empty (unless you install things such as IIS, where the installer generates test certificates).
Thus, switch to other stores, such as using StoreName enumeration,
https://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.storename(v=vs.110).aspx
You can always open MMC to see stores and certificates,
https://msdn.microsoft.com/en-us/library/ms788967(v=vs.110).aspx

How to obtain CN of the certificates in particular store?

i want to obtain the CN of the certificates stored in the MY store as i want to verify if the certificate exists or not in that store.
I don't know the which method should be used to perform this task.
I tried using below code but it doesn't works
X509Certificate2Collection cers = store.Certificates.Find(X509FindType.FindBySubjectName,"Root_Certificate",false);
if(cers.Count>0)
{
//certificate present
}
else
{
//certificate not present
}
Does the subjectName gives CN?
is there any other method?
Please suggest me how to check whether a particular certificate is present or not and i want to do it using CN.
You could use the store.Certificates.Find(X509FindType.FindBySubjectName, "SubjectName", false)
function to search for a certificate by its subject name. Do NOT include "CN=" in the subject name.
To search more specific you could use the thumbprint to search for your certificate.
The following code sample demonstrates this:
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly | OpenFlags.IncludeArchived);
foreach (var c in store.Certificates)
{
Console.Out.WriteLine(c.Thumbprint);
Console.Out.WriteLine(c.Subject);
}
// Find by thumbprint
X509Certificate2Collection col =
store.Certificates.Find(X509FindType.FindByThumbprint, "669502F7273C447A62550D41CD856665FBF23E48", false);
store.Close();
I've added a foreach loop to the code sample to iterate over all certificates in the selected store.
Your certificate must be listed there. If not, you probably use the wrong store.
Note, there is a My store for the Machine and the Current User. So, be sure to open the right store.
To get the thumbprint of your certificate follow these steps:
Open certmgr.msc.
Double click on your certificate.
Go to the details tab.
Under thumbprint you will find the thumbprint of your certificate.
Hope, this helps.

Categories

Resources