I have a .Net executable which I have digitally signed using a certificate generated through makecert.exe and signed using signtool. How to verify that exe has not been tampered or it is still using the certificate digitally signed by me.
For ex - A situation where anyone can replace the exe which is digitally signed by another certificate and placed into Trusted Root Authorities.
From various internet sources I read that the below code would just check if the certificate is valid
X509Certificate signer = X509Certificate.CreateFromSignedFile(executablePath);
X509Certificate2 certificate = new X509Certificate2(signer);
var certificateChain = new X509Chain
{
ChainPolicy = {
RevocationFlag = X509RevocationFlag.EntireChain,
RevocationMode = X509RevocationMode.Online,
UrlRetrievalTimeout = new TimeSpan(0, 1, 0),
VerificationFlags = X509VerificationFlags.NoFlag
}
};
var chainIsValid = certificateChain.Build(certificate);
if (chainIsValid)
{}
And it is suggested to use WinVerifyTrust. My question is WinVerifyTrust would also validate the certificate, if the same exe is signed by another certificate deployed in Trusted Root Authorities. How can I associate the exe with my certificate? Or how the WinVerifyTrust can be helpful in this situation as mentioned everywhere? Please help!!
Thanks
Related
I have an .NET 6 application needing to identify and extract the different certificates from a "child" (client) pfx file. The pfx file contains the root ca certificate and also the child (client) certificate. The certificates from the pfx file are iterated using the X509Certificate2Collection object:
var clientCertPfxFilePath = "xxx.pfx";
var certPw = "...";
X509Certificate2Collection certCollection = new X509Certificate2Collection();
certCollection.Import(clientCertPfxFilePath, certPw, X509KeyStorageFlags.PersistKeySet);
foreach (X509Certificate2 cert in certCollection)
{
// Check if "cert" is root ca certificate - how?
}
Alternatively the certificates can be iterated using the X509Chain object
var clientCertificate = new X509Certificate2(clientCertPfxFilePath, certPw);
X509Chain certChain = new X509Chain();
certChain.Build(clientCertificate);
foreach (var element in certChain.ChainElements)
{
// Check if "element" is root ca certificate - how?
}
I am looking for a solution how to programmatically identify which from the iterated certificates is the root ca certificate, with either one of the both above code snippets
For X509Chain the answer is easy... if chain.Build returned true then the chain ends in a trusted element, and therefore the last element will be a trusted root certificate. (If you are reading this in the far future then maybe X509Chain will have been enhanced to trust non-root anchors, but that would almost certainly require a different call pattern than was presented here.)
For certificates, detecting a self-signed certificate is hard, but you can check if a certificate is self-issued fairly easily:
bool selfIssued = cert.SubjectName.RawData.SequenceEquals(cert.IssuerName.RawData);
(That is, a self-issued certificate is one whose subject name and issuer name are encoded identically.)
A self-signed certificate is a self-issued certificate whose public key can validate the signature on the certificate. The signature verification isn't exposed through .NET, so it's hard to check, but many applications assume that self-issued and self-signed are the same anyways, so you can, too.
#bartonjs Unfortunately the solution with chain.Build does not work on a computer where no certificates were installed in the windows certificate store. The root ca certificate is available in the certificate chain after calling "chain.Build" only on the computers where the root ca is installed in the certificate store. On these computers the certificate chain has 2 elements, the client certificate and the root ca certificate. On the computers having no root ca installed in the certificate store the chain contains only 1 element after the "chain.Build" call (the client certificate). The root ca does not appear anymore, although using absolutely the same pfx file!
I hope I can explain this correctly. I inherited a couple of windows applications that need a certificate installed to the local cert store in order to access an Azure Key Vault's Secret to do what the applications do. Currently everything is working correctly. The cert in Azure is set to expire on 10/31/2019.
A new certificate has been created with an expiration in September of 2020.
When I had these applications dumped on me I was give the cert to use but it has a .p12 extension. I can only export the new Azure certificate as .cer or .pfx.
When I install the newly exported cert as either .pfx or .cer the applications fail. If I install the old cert with .p12 extension they work.
Both apps use the code below to get (I think) the local cert that is current via the "Issuer" which is CN = Value. I've checked both the old and new values of "Issuer/CN =" and they are identical.
Does the cert exported in Azure need to have a .p12 extension? If so how do I do that.
If the cert in Azure exported is okay as a .pfx where might my problem(s) be?
C# code in apps that get local cert to in turn gets the necessary Azure secret to do the work:
private static X509Certificate2 ReadCertificateFromStore(string certName)
{
X509Certificate2 cert = null;
try
{
using (X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
{
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certCollection = store.Certificates;
// Find unexpired certificates.
X509Certificate2Collection currentCerts = certCollection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
// From the collection of unexpired certificates, find the ones with the correct name.
X509Certificate2Collection signingCert = currentCerts.Find(X509FindType.FindBySubjectDistinguishedName, certName, false);
// Return the first certificate in the collection, has the right name and is current.
cert = signingCert.OfType<X509Certificate2>().OrderByDescending(c => c.NotBefore).FirstOrDefault();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return cert;
}
First .p12 as well as .pfx are extension for the PKCS#12 format.
Both apps use the code below to get (I think) the local cert that is current via the "Issuer" which is CN = Value. I've checked both the old and new values of "Issuer/CN =" and they are identical.
Based on your code that is not true
// From the collection of unexpired certificates, find the ones with the correct name.
X509Certificate2Collection signingCert =
currentCerts.Find(X509FindType.FindBySubjectDistinguishedName, certName, false);
It says FindBySubjectDistinguishedName which means that the subject of both certificates need to be exactly the same. Here is an example:
And another one with multiple elements in the subject:
You could also install both certificates and play around to figure the parameters to get the right certificate. I converted parts of your code to PowerShell:
$store =
new-object System.Security.Cryptography.X509Certificates.X509Store( `
[System.Security.Cryptography.X509Certificates.StoreName]::My, `
[System.Security.Cryptography.X509Certificates.StoreLocation]::CurrentUser);
$store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadOnly);
$signingCert =
$store.Certificates.Find(
[System.Security.Cryptography.X509Certificates.X509FindType]::FindBySubjectDistinguishedName,
"CN=...", `
$false);
$signingCert
Mystery solved. In addition to installing the certificate on the machines in question you also need to register the cert (.cer portion) in Azure's App Registrations.
I have been using this repo that allows creating a certificate based on self-signed Root Certificate.
My Root Certificate was given to me in the form of MyCARoot.cer and MyCARoot.pvk They were created (and already deployed) previously with makecert. So in order for me to use the code in the above link I had to combine .cer and .pvk files in .pfx using pvk2pfx.exe
My problem is that after generating the certificate and importing it to Personal store it seems to be invalid. On the Certification Path tab in MMC it says:
The issuer of the certificate could not be found
Note: the only change I made to the code are the password that were used.
The problem with that source was that it was supposed to do this:
X509Certificate issueCert = Org.BouncyCastle.Security.DotNetUtilities.FromX509Certificate(issuerCertificate);
var authorityKeyIdentifier = new AuthorityKeyIdentifierStructure(issueCert);
certificateGenerator.AddExtension(X509Extensions.AuthorityKeyIdentifier.Id, false, authorityKeyIdentifier);
instead of this:
var authorityKeyIdentifierExtension =
new AuthorityKeyIdentifier(
SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(issuerKeyPair.Public),
new GeneralNames(new GeneralName(issuerDN)),
issuerSerialNumber);
certificateGenerator.AddExtension(
X509Extensions.AuthorityKeyIdentifier.Id, false, authorityKeyIdentifierExtension);
I got one Digital certificate from NeutralUS CA. I have installed in my local system. I found the installed certificate under Personal Certificates in MMC and my application validated with this certificate. I just moved to my application to our production server and installed same certificate. here also i can see my certificate under personal certificates in MMC on windows server 2008 R2. But when I am trying to load certificate with serial number, it is showing the store certificates count is zero. Could you please tell me what would be the reason? Why it is not recognize from the Personal folder. i have only one certificate in in personal folder.
var clientCertStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
clientCertStore.Open(OpenFlags.OpenExistingOnly & OpenFlags.ReadOnly);
base.Data.ErrorLog.Append("Certificates Count : " + clientCertStore.Certificates.Count);
I just log the data into one text file... it is giving clientCertStore.Certificates.Count as 0.
I got one Digital certificate from NeutralUS CA. I have installed in my local system.
What is installed? Is the end entity cert installed and trusted? Or is it the NeutralUS CA?
I could not find the NeutralUS CA for download on the net. That's unusual.
Could you please tell me what would be the reason? Why it is not recognize from the Personal folder.
I suspect its a problem with accounts (but its just a guess). What account was the certificate installed under, and what account is the program running under?
I really despise how difficult Java and .Net make it to use a damn certificate. Here's the code I use to avoid wasting time with those damn stores. It allows you to load directly from the filesystem or an app bundle. It also does not use the hundreds of CAs and subordinates that Windows carries around.
static bool VerifyServerCertificate(object sender, X509Certificate certificate,
X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
try
{
String CA_FILE = "ca-cert.der";
X509Certificate2 ca = new X509Certificate2();
ca.Import(CA_FILE);
X509Chain chain2 = new X509Chain();
chain2.ChainPolicy.ExtraStore.Add(ca);
// Check all properties
chain2.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
// This setup does not have revocation information
chain2.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
// Are there any failures from building the chain?
chain2.Build(new X509Certificate2(certificate));
if (chain2.ChainStatus.Length == 0)
return true;
// Verify the status is NoError
bool result = chain2.ChainStatus[0].Status == X509ChainStatusFlags.NoError;
Debug.Assert(result == true);
return result;
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
return false;
}
And I still have not figured out how to set the original X509Chain chain that comes in as a parameter to the X509Chain chain2 that I want to use before the callback VerifyServerCertificate is invoked.
I want to create certificates programmatically in C#.net which are signed by a CA. I was able to create a self signed certificate with CertCreateSelfSignCertificate as described here:
http://msdn.microsoft.com/en-us/library/aa376039(VS.85).aspx
Self Signed Certificate in Windows without makecert?
I was looking through the MSDN documentation and I can't seem to find a function to generate a certificate and sign it from a request. Most functions seem to be for manipulating the certificate store. Am I barking up the wrong dll here?
I tried the unmanaged approach as well without success. In contrast to that, creating certificates with BouncyCastle is a breeze:
var keygen = new RsaKeyPairGenerator();
keygen.Init(new KeyGenerationParameters(new SecureRandom(), 2048));
var keys = keygen.GenerateKeyPair();
var certGen = new X509V3CertificateGenerator();
var dnName = new X509Name("CN=Test CA");
certGen.SetSerialNumber(BigInteger.ValueOf(1));
certGen.SetIssuerDN(dnName);
certGen.SetNotBefore(DateTime.Today);
certGen.SetNotAfter(DateTime.Today.AddYears(10));
certGen.SetSubjectDN(dnName);
certGen.SetPublicKey(keys.Public);
certGen.SetSignatureAlgorithm("SHA1WITHRSA");
var cert = certGen.Generate(keys.Private);
This is a self-signed CA certificate, but creating a signed certificate is the same. Just change the issuer and the signing private key. You can also export certificates to DER (.cer) and PKCS12 (.p12) as well.
My tendency would be to try this with capicom.dll, first. It's basically a wrapper for cryptoapi.
I found the answer to this. I loaded up makecert.exe in a debugger and found it was using this call to create a signed certificate: CryptSignAndEncodeCertificate
http://msdn.microsoft.com/en-us/library/aa380277(VS.85).aspx