私は基本的にこの質問と同じ問題を抱えていますが、受け入れられた回答から除外された、明らかに些細な部分を埋めるのに苦労しています。Mono を使用して C# でこれを行っています。
私は 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の値であり、指数は公開鍵の2番目の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」/「2 番目の TLV」とは正確には何を意味し、どのようにしてそれらの値をバイト配列から取得するのでしょうか?
私が理解していることから、TLVはtype-length-valueの略です。したがって、それが正しければ、公開鍵を含むバイト配列の最初のビットには、係数データが何ビットであるかに関する情報があります。その情報を使用して、公開鍵からその量のビットを別の配列にコピーし、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() の戻り値) を使用する必要がありますか?
他の場所では、暗号化された署名部分が certificateData の最後の 256 ビットであることがわかりましたが、それが「証明書の 3 番目の TLV」と同じかどうかはわかりません。そうでなければ、私はやっているだろう
byte[] certificateData = certificate.GetRawCertData();
byte[] encryptedSignature = new byte[256];
System.Array.Copy(certificateData, certificateData.Length - 256, encryptedSignature, 0, 256);
次に、VerifyData 呼び出しの最後のパラメーターとして encryptedSignature を使用します。
このすべてのTLVビジネスの代わりに、私も単純に試しました
RSACryptoServiceProvider publicKey = trustedRootCertificate.PublicKey.Key as RSACryptoServiceProvider;
上記でリンクした質問の人として、しかしこれを使用すると、VerifyData呼び出しは、そうすべきではないと思ったときにfalseを返しました。アプリケーションがサーバーから取得する証明書には、そのルート証明書としてtrustedRootCertificateが含まれています。これでこれを実行できるはずですよね? ルートの公開鍵はサーバーの証明書を検証できるはずですか?
証明書検証の基本が最初から間違っている可能性が非常に高いです。そうでない場合、私の質問はこれらの値をどのように取得するかです
// The value of the second TLV in your Public Key
..
// The value of the first TLV in your Public Key
私が持っている信頼されたルート証明書の公開鍵から。
編集:ファイルからロードされたルート証明書とアプリがサーバーから取得した証明書が、それらの情報を出力することによって想定されているものであることも確認したため、問題は少なくとも証明書が間違っていることではありません. それらを正しく使用する方法がわかりません。