C# 如何确保证书未更改?

C# 如何确保证书未更改?,c#,certificate,x509certificate,C#,Certificate,X509certificate,我想到了一个问题。 只有在系统中安装了证书的情况下,才可以使用常见的方法使应用程序运行。我希望这样的证书能由我自己签署的证书颁发和验证 我可以通过其名称从存储中获取证书,但如何确保此类证书由我的自签名证书签名,并且没有人颁发具有相同名称的证书并替换本地存储中的证书 或者换句话说,我如何确保在本地存储处签署证书的证书不是伪造的 如果问题不正确或不清楚,我很抱歉,但我很乐意得到帮助。问题确实很好 最终用户始终有可能创建一个有效的证书链,其中包含您的使用者姓名和作为颁发者的身份,以及作为颁发者的另一个

我想到了一个问题。 只有在系统中安装了证书的情况下,才可以使用常见的方法使应用程序运行。我希望这样的证书能由我自己签署的证书颁发和验证

我可以通过其名称从存储中获取证书,但如何确保此类证书由我的自签名证书签名,并且没有人颁发具有相同名称的证书并替换本地存储中的证书

或者换句话说,我如何确保在本地存储处签署证书的证书不是伪造的


如果问题不正确或不清楚,我很抱歉,但我很乐意得到帮助。

问题确实很好

最终用户始终有可能创建一个有效的证书链,其中包含您的使用者姓名和作为颁发者的身份,以及作为颁发者的另一个证书链certificate,所有这些证书链一直到根

他们不能做的是用颁发者证书的私钥签署这些证书

因此,下面的代码从当前用户的个人证书存储中加载应用程序证书,然后从资源中加载颁发者的颁发者证书,并使用颁发者证书的公钥验证安装在客户机上的应用程序证书上的签名

在我的源代码中,使用密钥IssuerCertificate将颁发者证书添加到资源中

我真的很喜欢提出这样的解决方案

在代码中,我提到了编码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)
    {
        // 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();
}
static void Main(字符串[]args)
{
字符串expectedSubjectName=“我的应用程序”;
X509Certificate2发行人证书=新的X509Certificate2(资源1.发行人证书);
字符串expectedIssuerName=issuerCertificate.Subject;
bool结果=验证证书颁发者(预期的主体名称、预期的颁发名称、颁发者证书);
}
私有静态void ThrowCertificateNotFoundException(字符串expectedSubjectName、字符串ExpectedAssuerName、bool IsThumbPrintMatch)
{
如果(不匹配)
{
//关于可能伪造证书的通知
}
抛出新的SecurityException(“运行此应用程序需要由“+expectedSubjectName+”颁发的使用者名称为“+expectedSubjectName+”的证书”);
}
私有静态X509Certificate2 GetCertificate(字符串expectedSubjectName,字符串ExpectedAssuerName)
{
X509Store personalCertificateStore=新的X509Store(StoreName.My,StoreLocation.CurrentUser);
personalCertificateStore.Open(OpenFlags.ReadOnly);
X509CertificateCollection certificatesBySubjectName=personalCertificateStore.Certificates.Find(X509FindType.FindBySubjectName,expectedSubjectName,true);
if(certificatesBySubjectName.Count==0)
{
ThrowCertificateNotFoundException(expectedSubjectName、ExpectedSubserName、false);
}
X509Certificate2 matchingCertificate=null;
foreach(certificatesBySubjectName中的X509Certificate2 CertificateSubjectName)
{
if(certificateBySubjectName.Issuer==ExpectedAssuerName)
{
matchingCertificate=certificateBySubjectName;
打破
}
}
if(matchingCertificate==null)
{
ThrowCertificateNotFoundException(expectedSubjectName、ExpectedSubserName、false);
}
返回匹配证书;
}
私有静态布尔验证证书颁发者(字符串expectedSubjectName、字符串ExpectedAssuerName、X509Certificate2 issuerCertificate)
{
X509Certificate2 matchingCertificate=GetCertificate(expectedSubjectName,ExpectedAssuerName);
X509Chain chain=新的X509Chain();
链.构建(匹配证书);
//bool x=Encoding.ASCII.GetString(chain.ChainElements[1].Certificate.RawData)==Encoding.ASCII.GetString(issuerCertificate.RawData);
字节[]certificateData=matchingCertificate.RawData;
MemoryStream asn1Stream=新的MemoryStream(certificateData);
BinaryReader asn1StreamReader=新的BinaryReader(asn1Stream);
//der编码的证书结构如下所示:
//根序列
//顺序(证书内容)
//序列(签名算法(如SHA256WithRSA加密)
//序列(签名)
//我们需要对ASN.1内容进行解码以获得
//序列0(由颁发者签名的实际证书内容,包括序列定义、标签号和长度)
//序列2(这是签名。X509Certificate2类没有给我们这些信息。年份是2015年)
//读取根序列(忽略)
byte leadingOctet=asn1StreamReader.ReadByte();
ReadTagNumber(LeadingActet,asn1StreamReader);
ReadDataLength(asn1StreamReader);
//保存当前位置,因为我们需要它来包含带有证书内容的序列头
int sequence0StartPosition=(int)asn1Stream.Position;
leadingOctet=asn1StreamReader.ReadByte();
ReadTagNumber(LeadingActet,asn1StreamReader);
int sequence0ContentLength=ReadDataLength(asn1StreamReader);
int sequence0HeaderLength=(int)asn1Stream.Position-sequence0StartPosition;
sequence0ContentLength+=sequence0HeaderLength;
字节[]sequence0Content=新字节[sequence0ContentLength];
ASN1流位置-=4;
asn1StreamReader.Read(sequence0Content,0,sequence0ContentLength);
//跳过序列1(签名算法),因为我们不需要它,并假设我们知道它,因为我们拥有颁发者证书
//此序列包含签名过程中使用的算法,默认情况下对我们隐藏。年份为2015年。
//真正应该做的是,获取算法ID(哈希算法和非对称算法)并使用它们
//验证过程中的算法
leadingOctet=asn1StreamReader.ReadByte();
ReadTagNumber(引线CTET,asn1Strea