How to get certificate from specific binding C# - c#

I found on the internet only way to got all the certificates from the iis and i do it like the following (c#):
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
store.Certificates;
Now I try to get a specific certificate of specific binding, how can I do it in C#?

The certificates themselves hold absolutely no information about the bindings used in IIS, so you cannot retrieve the certificates from the machine and expect them to have anything related to IIS. You would need to query that information from IIS.
To do this, you will need add a reference to the library that can be found under %windir%\system32\inetsrv\Microsoft.Web.Administration.dll (note: IIS 7 or newer must be installed). After this, you can do something like the following to get the certificate:
ServerManager manager = new ServerManager();
Site yourSite = manager.Sites["yourSiteName"];
X509Certificate2 yourCertificate = null;
foreach (Binding binding in yourSite.Bindings)
{
if (binding.Protocol == "https" && binding.EndPoint.ToString() == "127.0.0.1" /*your binding IP*/)
{
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
yourCertificate = store.Certificates.Find(X509FindType.FindByThumbprint, ToHex(binding.CertificateHash), true)[0];
break;
}
}
public static string ToHex(byte[] ba)
{
var hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
{
hex.AppendFormat("{0:x2}", b);
}
return hex.ToString();
}

I think Camilo's answer has a small problem. As far as I can see (tested it) the code to find the certificate does not work, because System.Convert.ToBase64String(binding.CertificateHash) does not return a valid certificate thumbprint.
My version:
/// <summary>
/// Returns the https certificate used for a given local IIS website.
/// </summary>
/// <param name="sWebsite">Website url, e.g., "https://myserver.company.com"</param>
/// <returns>certificate, null if not found</returns>
private X509Certificate2 FindIisHttpsCert(string sWebsite)
{
Uri uriWebsite = new Uri(sWebsite);
using (ServerManager sm = new ServerManager())
{
string sBindingPort = string.Format(":{0}:", uriWebsite.Port);
Binding bdBestMatch = null;
foreach (Site s in sm.Sites)
{
foreach (Binding bd in s.Bindings)
{
if (bd.BindingInformation.IndexOf(sBindingPort) >= 0)
{
string sBindingHostInfo = bd.BindingInformation.Substring(bd.BindingInformation.LastIndexOf(':') + 1);
if (uriWebsite.Host.IndexOf(sBindingHostInfo, StringComparison.InvariantCultureIgnoreCase) == 0)
{
if ((bd.Protocol == "https") && ((bdBestMatch == null) || (bdBestMatch.BindingInformation.Length < bd.BindingInformation.Length)))
bdBestMatch = bd;
}
}
}
}
if (bdBestMatch != null)
{
StringBuilder sbThumbPrint = new StringBuilder();
for (int i = 0; i < bdBestMatch.CertificateHash.Length; i++)
sbThumbPrint.AppendFormat("{0:X2}", bdBestMatch.CertificateHash[i]);
X509Store store = new X509Store(bdBestMatch.CertificateStoreName, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection coll = store.Certificates.Find(X509FindType.FindByThumbprint, sbThumbPrint.ToString(), true);
if (coll.Count > 0)
return coll[0];
}
}
return null; // if no matching site was found
}
This function also works if multiple https sites are hosted on the same server (tested) and should work if the site uses a port other than 443 (not tested). To get Binding info, %windir%\system32\inetsrv\Microsoft.Web.Administration.dll is used, as in Camilo's answer.

I had tried the solution, but ran into problems NOT finding the certificate. Ended up being that the cert store needs to be properly specified based on the binding:
ServerManager manager = new ServerManager();
Site yourSite = manager.Sites["yourSiteName"];
X509Certificate2 yourCertificate = null;
foreach (Binding binding in yourSite.Bindings)
{
if (binding.Protocol == "https" && binding.EndPoint.ToString() == "127.0.0.1" /*your binding IP*/)
{
var store = new X509Store(binding.CertificateStoreName, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var certs = store.Certificates.Find(X509FindType.FindByThumbprint, ToHex(binding.CertificateHash), true);
if (certs.Count > 0)
yourCertificate = certs[0];
break;
}
}
public static string ToHex(byte[] ba)
{
var hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
{
hex.AppendFormat("{0:x2}", b);
}
return hex.ToString();
}

The following link should help:
basically store.Certificates returns a collection of all certificates in the particular store, you can then search through for the one you want. The link shows how to do this if you know the subject name of the certificate you want.
How to get X509Certificate from certificate store and generate xml signature data?

Related

SSL/TSL - Issue in finding Certificates

I am testing the example given in below link.
https://msdn.microsoft.com/en-us/library/system.net.security.sslstream.aspx
To generate certificates I am using the one with 40 userful answers SSLStream example - how do I get certificates that work?
To run the server I am using command
SslTcpServer.exe TempCert.cer
Below is the code from msdn where I am facing problem.
public static int Main(string[] args)
{
string serverCertificateName = null;
string machineName = null;
if (args == null ||args.Length <1 )
{
DisplayUsage();
}
// User can specify the machine name and server name.
// Server name must match the name on the server's certificate.
machineName = args[0];
if (args.Length <2 )
{
serverCertificateName = machineName;
}
else
{
serverCertificateName = args[1];
}
SslTcpClient.RunClient (machineName, serverCertificateName);
return 0;
}
I get below error when calling X509Certificate.CreateFromCertFile:
System.Security.Cryptography.CryptographicException: 'The system cannot find the file specified.
public static void RunServer(string certificate)
{
serverCertificate = X509Certificate.CreateFromCertFile(certificate);
// Create a TCP/IP (IPv4) socket and listen for incoming connections.
//serverCertificate = new X509Certificate2(certificate,"");
}
serverCertificateName is passed as argument and it should be just the name of the certificate or should i give the full path of the certificate?
If I give path of the certificate it is working fine.Then what is point in installing the certificates in the store? How can I get it from store and use it?
Here is some code that will return a list of the host names supported by installed certificates (that's a little more than you wanted, but should point you in the right direction):
System.Security.Cryptography.X509Certificates.X509Store store = new System.Security.Cryptography.X509Certificates.X509Store(System.Security.Cryptography.X509Certificates.StoreLocation.LocalMachine);
store.Open(System.Security.Cryptography.X509Certificates.OpenFlags.ReadOnly);
HashSet<string> certificateNames = new HashSet<string>();
foreach (System.Security.Cryptography.X509Certificates.X509Certificate2 mCert in store.Certificates)
{
// is this a UCC certificate?
System.Security.Cryptography.X509Certificates.X509Extension uccSan = mCert.Extensions["2.5.29.17"];
if (uccSan != null)
{
foreach (string nvp in uccSan.Format(true).Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries))
{
string[] parts = nvp.Split('=');
string name = parts[0];
string value = (parts.Length > 0) ? parts[1] : null;
if (String.Equals(name, "DNS Name", StringComparison.InvariantCultureIgnoreCase))
{
certificateNames.Add(value.ToLowerInvariant());
}
}
}
else // just a regular certificate--add the single name
{
string certificateHost = mCert.GetNameInfo(System.Security.Cryptography.X509Certificates.X509NameType.SimpleName, false);
certificateNames.Add(certificateHost.ToLowerInvariant());
}
}
return certificateNames;

How to check if digital signature of signed document signed by trusted certificate?

I develop application which works with pdf documents and I've to understand that my document signed by trusted signature.
I use itextsharp for getting information, but I don't how to check validity of signature.
var pdfReader = new PdfReader(document.FilePath);
var acroFields = pdfReader.AcroFields;
var names = acroFields.GetSignatureNames();
foreach (var name in names)
{
var signatureName = name as string;
var pk = acroFields.VerifySignature(signatureName);
var signatureIsValid = false;
foreach (var certificate in pk.Certificates)
{
signatureIsValid = certificate.IsValidNow; // It just check date
}
}
A document on the screen bellow has two digital signatures, but they signed without trusted certificate. I have to show some similar message for a user.
In order to check for trusted authority you need to have trusted CA certificate to check against. If you have one you could use code like this to check if cert came from trusted authority you are expecting it to be:
X509Certificate2 authorityCert = GetAuthorityCertificate();
X509Certificate2 certificateToCheck = GetYourCertificate();
X509Chain chain = new X509Chain();
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
chain.ChainPolicy.VerificationTime = DateTime.Now;
chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 0, 0);
//Adding your CA root to the chain
chain.ChainPolicy.ExtraStore.Add(authorityCert);
bool isChainValid = chain.Build(certificateToCheck);
if (!isChainValid)
{
//Ok, let c what is wrong...
string[] errors = chain.ChainStatus
.Select(m => $"{m.StatusInformation.Trim()}, status: {m.Status}")
.ToArray();
string certificateErrors = "Error occured during checking certificate.";
if (errors != null && errors.Length > 0)
certificateErrors = string.Join(" \n", errors);
throw new ApplicationException("Trust chain is not from known authority. Errors: " + certificateErrors);
}
//Let see if our chain actually contains known root, for which you are cheking
if (!chain.ChainElements
.Cast<X509ChainElement>()
.Any(m => m.Certificate.Thumbprint == authorityCert.Thumbprint))
throw new ApplicationException("Could not locate CA root!Thumbprints did not match.");

How to make sure certificate is not changed?

There is an issue came into my mind.
Is there any common way to make an app running only when there is an installed certificate in the system. And I want such a certificate to be issued and verified by my self-signed certificate?
I can get a certificate by it's name from the storage, but how do I make sure such a certificate is signed by my self-signed certificate and nobody have issued a certificate with the same name and replaced the one in the local storage?
Or in other words, how do I make sure the certificate which signed the certificate at local storage is not a forged one?
I'm sorry if its not correct and|or clear question, but I'll be happy to have help regarding it.
Very good question indeed.
There is always a possibility of the end-user creating a valid certificate chain with your subject name and as the issuer, another for the issuer ceritificate, all up to the root.
What they canot do is to sign those certifcates with the issuer certificate's private key.
Therefore, the code below loads the application certificate from the personal certificate store of the current user, then, loads the issuer certificate of the issuer from the resources and verifies the signature on the application certificate installed on the client machine using the public key of the issuer certificate.
In my source code, the issuer certificate is added to the resources with the key IssuerCertificate
I am actually fond of coming out with a solution like this.
In the code I mention an encoding ASN.1. Check it here if you need
static void Main(string[] args)
{
string expectedSubjectName = "My Application";
X509Certificate2 issuerCertificate = new X509Certificate2(Resource1.IssuerCertificate);
string expectedIssuerName = issuerCertificate.Subject;
bool result = VerifyCertificateIssuer(expectedSubjectName, expectedIssuerName, issuerCertificate);
}
private static void ThrowCertificateNotFoundException(string expectedSubjectName, string expectedIssuerName, bool isThumbprintMismatch)
{
if (isThumbprintMismatch)
{
// Notification for possible certificate forgery
}
throw new SecurityException("A certificate with subject name " + expectedSubjectName + " issued by " + expectedIssuerName + " is required to run this application");
}
private static X509Certificate2 GetCertificate(string expectedSubjectName, string expectedIssuerName)
{
X509Store personalCertificateStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
personalCertificateStore.Open(OpenFlags.ReadOnly);
X509CertificateCollection certificatesBySubjectName = personalCertificateStore.Certificates.Find(X509FindType.FindBySubjectName, expectedSubjectName, true);
if (certificatesBySubjectName.Count == 0)
{
ThrowCertificateNotFoundException(expectedSubjectName, expectedIssuerName, false);
}
X509Certificate2 matchingCertificate = null;
foreach (X509Certificate2 certificateBySubjectName in certificatesBySubjectName)
{
if (certificateBySubjectName.Issuer == expectedIssuerName)
{
matchingCertificate = certificateBySubjectName;
break;
}
}
if (matchingCertificate == null)
{
ThrowCertificateNotFoundException(expectedSubjectName, expectedIssuerName, false);
}
return matchingCertificate;
}
private static bool VerifyCertificateIssuer(string expectedSubjectName, string expectedIssuerName, X509Certificate2 issuerCertificate)
{
X509Certificate2 matchingCertificate = GetCertificate(expectedSubjectName, expectedIssuerName);
X509Chain chain = new X509Chain();
chain.Build(matchingCertificate);
// bool x = Encoding.ASCII.GetString(chain.ChainElements[1].Certificate.RawData) == Encoding.ASCII.GetString(issuerCertificate.RawData);
byte[] certificateData = matchingCertificate.RawData;
MemoryStream asn1Stream = new MemoryStream(certificateData);
BinaryReader asn1StreamReader = new BinaryReader(asn1Stream);
// The der encoded certificate structure is like this:
// Root Sequence
// Sequence (Certificate Content)
// Sequence (Signature Algorithm (like SHA256withRSAEncryption)
// Sequence (Signature)
// We need to decode the ASN.1 content to get
// Sequence 0 (which is the actual certificate content that is signed by the issuer, including the sequence definition and tag number and length)
// Sequence 2 (which is the signature. X509Certificate2 class does not give us that information. The year is 2015)
// Read the root sequence (ignore)
byte leadingOctet = asn1StreamReader.ReadByte();
ReadTagNumber(leadingOctet, asn1StreamReader);
ReadDataLength(asn1StreamReader);
// Save the current position because we will need it for including the sequence header with the certificate content
int sequence0StartPosition = (int)asn1Stream.Position;
leadingOctet = asn1StreamReader.ReadByte();
ReadTagNumber(leadingOctet, asn1StreamReader);
int sequence0ContentLength = ReadDataLength(asn1StreamReader);
int sequence0HeaderLength = (int)asn1Stream.Position - sequence0StartPosition;
sequence0ContentLength += sequence0HeaderLength;
byte[] sequence0Content = new byte[sequence0ContentLength];
asn1Stream.Position -= 4;
asn1StreamReader.Read(sequence0Content, 0, sequence0ContentLength);
// Skip sequence 1 (signature algorithm) since we don't need it and assume that we know it because we own the issuer certificate
// This sequence, containing the algorithm used during the signing process IS HIDDEN FROM US BY DEFAULT. The year is 2015.
// What should have been done for real is, to get the algorithm ID (hash algorithm and asymmetric algorithm) and to use those
// algorithms during the verification process
leadingOctet = asn1StreamReader.ReadByte();
ReadTagNumber(leadingOctet, asn1StreamReader);
int sequence1ContentLength = ReadDataLength(asn1StreamReader);
byte[] sequence1Content = new byte[sequence1ContentLength];
asn1StreamReader.Read(sequence1Content, 0, sequence1ContentLength);
// Read sequence 2 (signature)
// The actual signature of the certificate IS HIDDEN FROM US BY DEFAULT. The year is 2015.
leadingOctet = asn1StreamReader.ReadByte();
ReadTagNumber(leadingOctet, asn1StreamReader);
int sequence2ContentLength = ReadDataLength(asn1StreamReader);
byte unusedBits = asn1StreamReader.ReadByte();
sequence2ContentLength -= 1;
byte[] sequence2Content = new byte[sequence2ContentLength];
asn1StreamReader.Read(sequence2Content, 0, sequence2ContentLength);
// At last, we have the data that is signed and the signature.
bool verificationResult = ((RSACryptoServiceProvider)issuerCertificate.PublicKey.Key)
.VerifyData
(
sequence0Content,
CryptoConfig.MapNameToOID("SHA256"),
sequence2Content
);
return verificationResult;
}
private static byte[] ReadTagNumber(byte leadingOctet, BinaryReader inputStreamReader)
{
List<byte> byts = new List<byte>();
byte temporaryByte;
if ((leadingOctet & 0x1F) == 0x1F) // More than 1 byte is used to specify the tag number
{
while (((temporaryByte = inputStreamReader.ReadByte()) & 0x80) > 0)
{
byts.Add((byte)(temporaryByte & 0x7F));
}
byts.Add(temporaryByte);
}
else
{
byts.Add((byte)(leadingOctet & 0x1F));
}
return byts.ToArray();
}
private static int ReadDataLength(BinaryReader inputStreamReader)
{
byte leadingOctet = inputStreamReader.ReadByte();
if ((leadingOctet & 0x80) > 0)
{
int subsequentialOctetsCount = leadingOctet & 0x7F;
int length = 0;
for (int i = 0; i < subsequentialOctetsCount; i++)
{
length <<= 8;
length += inputStreamReader.ReadByte();
}
return length;
}
else
{
return leadingOctet;
}
}
private static byte[] GetTagNumber(byte leadingOctet, BinaryReader inputStreamReader, ref int readBytes)
{
List<byte> byts = new List<byte>();
byte temporaryByte;
if ((leadingOctet & 0x1F) == 0x1F) // More than 1 byte is used to specify the tag number
{
while (((temporaryByte = inputStreamReader.ReadByte()) & 0x80) > 0)
{
readBytes++;
byts.Add((byte)(temporaryByte & 0x7F));
}
byts.Add(temporaryByte);
}
else
{
byts.Add((byte)(leadingOctet & 0x1F));
}
return byts.ToArray();
}

Removing certificate from storage does not work

I'm trying to remove certificate from storage and using this function:
public static void RemoveFromStorage(StoreName storeName, IEnumerable<CertInfo> certificates)
{
var store = new X509Store(storeName, StoreLocation.CurrentUser);
store.Open(OpenFlags.MaxAllowed | OpenFlags.IncludeArchived);
foreach (var cert in certificates)
{
var toRemove = store.Certificates.Find(X509FindType.FindByThumbprint, cert.Thumbprint, false);
store.Certificates.Remove(toRemove[0]);
}
store.Close();
}
Function doesn't throw any exception, Find function returns proper certificate from storage, but after calling Remove function it is not removed.
What am I doing wrong?
The store needs to be opened for ReadWrite.
Try this:
public static void RemoveFromStorage(StoreName storeName, IEnumerable<CertInfo> certificates)
{
var store = new X509Store(storeName, StoreLocation.CurrentUser);
store.Open(OpenFlags.MaxAllowed | OpenFlags.IncludeArchived | OpenFlags.ReadWrite);
foreach (var cert in certificates)
{
var toRemove = store.Certificates.Find(X509FindType.FindByThumbprint, cert.Thumbprint, false);
store.Certificates.Remove(toRemove[0]);
}
store.Close();
}
The problem was this line
store.Certificates.Remove(toRemove[0]);
Correct is:
store.Remove(toRemove[0]);

C# RunAs /Smartcard

My application used to accept username/password to authenticate to a remote share (on Windows server) and then get the file listings etc.
public int ConnectNetResource(string server, string user, string password, ref string driveLeter) {
NETRESOURCE net = new NETRESOURCE();
net.dwScope = 0;
net.dwType = 0;
net.dwDisplayType = 0;
net.dwUsage = 0;
net.lpRemoteName = server;
net.lpLocalName = driveLeter;
net.lpProvider = null;
return WNetAddConnection2(ref net, password, user, 0);
}
Now, we need to authenticate using SmartCard. I have code to GetSmartCards, but I do not know how to make use of the certificate to authenticate.
public static X509Certificate2 GetClientCertificate()
{
X509Certificate2 certificate = null;
var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
try
{
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
// Nothing to do if no cert found.
if (store.Certificates != null && store.Certificates.Count > 0)
{
if (store.Certificates.Count == 1)
{
// Return the certificate present.
certificate = store.Certificates[0];
}
else
{
// Request the user to select a certificate
var certificates = X509Certificate2UI.SelectFromCollection(store.Certificates, "Digital Certificates", "Select a certificate from the following list:", X509SelectionFlag.SingleSelection);
// Check if one has been returned
if (certificates != null && certificates.Count > 0)
certificate = certificates[0];
}
}
}
finally
{
store.Close();
}
return certificate;
}
GetClientCertificate function will return a user selected certificate, now HOW do I use this certificate to connect to a remote share. What API's or .dll can i use.
Btw, I am a windows engineer who can google code and make it work somehow. Will greatly appreciate a working code. Thank you.

Categories