Very good question.
There is always the possibility that the end user will create a valid chain of certificates with your subject name and as an issuer, another for issuer certificates, right down to the root.
What they can do is sign these certificates with the issuer's certificate private key.
Therefore, the code below downloads the application certificate from the personal certificate store of the current user, then downloads the issuer's issuer certificate from 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, an issuer certificate is added to resources with the EmertCertificate key
I really love going out with that decision.
ASN.1. ,
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)
{
}
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);
byte[] certificateData = matchingCertificate.RawData;
MemoryStream asn1Stream = new MemoryStream(certificateData);
BinaryReader asn1StreamReader = new BinaryReader(asn1Stream);
byte leadingOctet = asn1StreamReader.ReadByte();
ReadTagNumber(leadingOctet, asn1StreamReader);
ReadDataLength(asn1StreamReader);
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);
leadingOctet = asn1StreamReader.ReadByte();
ReadTagNumber(leadingOctet, asn1StreamReader);
int sequence1ContentLength = ReadDataLength(asn1StreamReader);
byte[] sequence1Content = new byte[sequence1ContentLength];
asn1StreamReader.Read(sequence1Content, 0, sequence1ContentLength);
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);
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)
{
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)
{
while (((temporaryByte = inputStreamReader.ReadByte()) & 0x80) > 0)
{
readBytes++;
byts.Add((byte)(temporaryByte & 0x7F));
}
byts.Add(temporaryByte);
}
else
{
byts.Add((byte)(leadingOctet & 0x1F));
}
return byts.ToArray();
}