我基本上遇到了与这个问题相同的问题,但是我在填写已接受的答案中遗漏的明显微不足道的部分时遇到了麻烦。我在 C# 中使用 Mono 执行此操作。
我有一个 CA 根证书,从中我可以得到一个持有公钥的 byte[]。然后我得到一个我需要验证的不受信任的证书。据我了解, RSACryptoServiceProvider.VerifyData 应该可以解决问题,但首先我需要使用公钥的模数和指数设置 RSAParameters。
(编辑:以下内容重复了我在上面链接的问题中已经显而易见的一些事情。)
应该做我需要的事情并使用我信任的根证书验证服务器证书的代码如下:
RSACryptoServiceProvider publicKey = new RSACryptoServiceProvider();
RSAParameters publicKeyParams = new RSAParameters();
publicKeyParams.Modulus = GetPublicKeyModulus();
publicKeyParams.Exponent = GetPublicKeyExponent();
publicKey.ImportParameters(publicKeyParams);
return publicKey.VerifyData(SignedValue(), CryptoConfig.MapNameToOID("SHA1"), Signature());
我的问题是 GetPublicKeyModulus() 和 GetPublicKeyExponent() 的内容。在接受的答案中,它们显然是微不足道的,只有一条评论说模数是我的公钥中第一个 TLV 的值,而指数是公钥中的第二个 TLV。我不完全明白那是什么意思。
byte[] GetPublicKeyExponent()
{
// The value of the second TLV in your Public Key
}
byte[] GetPublicKeyModulus()
{
// The value of the first TLV in your Public Key
}
byte[] SignedValue()
{
// The first TLV in your Ceritificate
}
byte[] Signature()
{
// The value of the third TLV in your Certificate
}
我的问题是这些“第一个 TLV”/“第二个 TLV”到底是什么意思,我如何从我拥有的字节数组中获取这些值?
据我了解,TLV 代表类型长度值。因此,如果我说得对,包含公钥的字节数组的第一位具有关于模数数据有多少位的信息。使用该信息,我应该将公钥中的该数量的位复制到另一个数组中,并将 RSAParameters.Modulus 设置为该值。在公钥中的模数之后是指数,我应该用它做同样的操作。但是我找不到有关包含的公钥数据中 TLV 的“TL”部分有多少位和格式的信息。
我在其他地方找到了信息,说模数是公钥中的前 1024 位,指数是余数,但是在字节数组之间复制数据时,这给了我一个关于大小的错误。
目前,我根据我链接的问题中接受的答案形成的代码基本上如下所示:
using System;
using System.Net;
using System.Security.Cryptography.X509Certificates;
X509Certificate2 trustedRootCertificate = new X509Certificate2(X509Certificate2.CreateFromCertFile(filename));
ServicePointManager.ServerCertificateValidationCallback = ValidateServerCertificate;
.
.
public bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
.
.
byte[] publicKeyBytes = trustedRootCertificate.GetPublicKey();
byte[] modulusData = // The value of the first TLV in the Public Key??
byte[] exponentData = // The value of the second TLV in the Public Key??
RSAParameters publicKeyParams = new RSAParameters();
publicKeyParams.Modulus = modulusData;
publicKeyParams.Exponent = exponentData;
RSACryptoServiceProvider publicKey = new RSACryptoServiceProvider();
publicKey.ImportParameters(publicKeyParams);
byte[] certificateData = certificate.GetRawCertData();
byte[] signedValue = // The first TLV in the certificate??
byte[] encryptedSignature = // The third TLV in the certificate??
return publicKey.VerifyData(certificateData, HashAlgorithm.Create("SHA1"), encryptedSignature);
}
或者我应该在 VerifyData 调用中使用 certificateData(certificate.GetRawCertData() 的返回值)吗?
在其他地方我发现加密签名部分是证书数据中的最后 256 位,我不确定这是否与“证书中的第三个 TLV”相同。如果没有,我会做
byte[] certificateData = certificate.GetRawCertData();
byte[] encryptedSignature = new byte[256];
System.Array.Copy(certificateData, certificateData.Length - 256, encryptedSignature, 0, 256);
然后使用 encryptedSignature 作为 VerifyData 调用中的最后一个参数。
除了所有这些 TLV 业务,我还简单地尝试过
RSACryptoServiceProvider publicKey = trustedRootCertificate.PublicKey.Key as RSACryptoServiceProvider;
作为我上面链接到的问题中的人,但是当我认为不应该使用这个时,VerifyData 调用然后返回 false。应用程序从服务器获得的证书有trustedRootCertificate 作为它的根证书,我应该可以这样做,对吗?root 的公钥应该能够验证服务器的证书吗?
很有可能我只是从一开始就错误地了解了证书验证的基础知识。如果不是这种情况,那么我的问题是如何获得这些值
// The value of the second TLV in your Public Key
..
// The value of the first TLV in your Public Key
来自我拥有的受信任根证书的公钥。
编辑:我还验证了从文件加载的根证书和应用程序从服务器获取的证书是通过打印它们的信息应该是什么,所以问题至少不在于证书错误. 我只是不知道如何正确使用它们。