.NET の HttpWebRequest/Response での自己署名証明書の使用
-
22-08-2019 - |
質問
自己署名 SSL 証明書を使用する API に接続しようとしています。これには、.NET の HttpWebRequest オブジェクトと HttpWebResponse オブジェクトを使用します。そして、次のような例外が発生します。
基礎となる接続が閉じられました。SSL/TLS セキュア チャネルの信頼関係を確立できませんでした。
これが何を意味するのか理解しています。そして、私は理解しています なぜ .NET は、私に警告して接続を閉じる必要があると感じています。ただし、この場合は、中間者攻撃は絶対に避けたいので、とにかく API に接続したいと考えています。
では、この自己署名証明書に例外を追加するにはどうすればよいでしょうか?それとも、証明書をまったく検証しないように HttpWebRequest/Response に指示するアプローチですか?どうすればいいでしょうか?
解決
@Domster:動作しますが、あなたが証明書ハッシュは、あなたが期待するものと一致するか否かをチェックすることによって、セキュリティのビットを強制する場合があります。
:だから拡張バージョンは、(私たちが使用しているいくつかのライブのコードに基づいて)このようなビットに見えますstatic readonly byte[] apiCertHash = { 0xZZ, 0xYY, ....};
/// <summary>
/// Somewhere in your application's startup/init sequence...
/// </summary>
void InitPhase()
{
// Override automatic validation of SSL server certificates.
ServicePointManager.ServerCertificateValidationCallback =
ValidateServerCertficate;
}
/// <summary>
/// Validates the SSL server certificate.
/// </summary>
/// <param name="sender">An object that contains state information for this
/// validation.</param>
/// <param name="cert">The certificate used to authenticate the remote party.</param>
/// <param name="chain">The chain of certificate authorities associated with the
/// remote certificate.</param>
/// <param name="sslPolicyErrors">One or more errors associated with the remote
/// certificate.</param>
/// <returns>Returns a boolean value that determines whether the specified
/// certificate is accepted for authentication; true to accept or false to
/// reject.</returns>
private static bool ValidateServerCertficate(
object sender,
X509Certificate cert,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
{
// Good certificate.
return true;
}
log.DebugFormat("SSL certificate error: {0}", sslPolicyErrors);
bool certMatch = false; // Assume failure
byte[] certHash = cert.GetCertHash();
if (certHash.Length == apiCertHash.Length)
{
certMatch = true; // Now assume success.
for (int idx = 0; idx < certHash.Length; idx++)
{
if (certHash[idx] != apiCertHash[idx])
{
certMatch = false; // No match
break;
}
}
}
// Return true => allow unauthenticated server,
// false => disallow unauthenticated server.
return certMatch;
}
他のヒント
、あなただけの完全に証明書の検証を無効にしたい場合は、あなたがServicePointManagerにServerCertificateValidationCallbackを変更することができ、判明しそうのように:
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
このは(無効、期限切れか、自己署名のものを含む)すべての証明書を検証します。
注、その中に.NET 4.5あなたはHttpWebRequestの自身あたり(とないすべての要求に影響を与えグローバルデリゲート経由)SSL検証を無効にすることができます:
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
request.ServerCertificateValidationCallback = delegate { return true; };
ローカルコンピュータの信頼されたルート証明機関への自己署名証明書を追加します。
あなたは管理者としてMMCを実行して、証明書をインポートすることができます。
方法:MMCスナップインを持つ証明書の表示
Domsterの答えのに使用される検証コールバックの範囲は、上の送信者のパラメータを使用して、特定の要求に制限することができますServerCertificateValidationCallback
デリゲート。次の単純なスコープクラスは、一時的にのみ指定された要求オブジェクトに対して実行する検証コールバックを配線するこの技術を使用します。
public class ServerCertificateValidationScope : IDisposable
{
private readonly RemoteCertificateValidationCallback _callback;
public ServerCertificateValidationScope(object request,
RemoteCertificateValidationCallback callback)
{
var previous = ServicePointManager.ServerCertificateValidationCallback;
_callback = (sender, certificate, chain, errors) =>
{
if (sender == request)
{
return callback(sender, certificate, chain, errors);
}
if (previous != null)
{
return previous(sender, certificate, chain, errors);
}
return errors == SslPolicyErrors.None;
};
ServicePointManager.ServerCertificateValidationCallback += _callback;
}
public void Dispose()
{
ServicePointManager.ServerCertificateValidationCallback -= _callback;
}
}
上記のクラスは、以下のように特定の要求のためのすべての証明書のエラーを無視するために使用することができます。
var request = WebRequest.Create(uri);
using (new ServerCertificateValidationScope(request, delegate { return true; }))
{
request.GetResponse();
}
他の人に役立つ可能性があるものとして追加するには...ユーザーに自己署名証明書をインストールするように求める場合は、このコード (上記を変更) を使用できます。
管理者権限は必要なく、ローカル ユーザーの信頼されたプロファイルにインストールされます。
private static bool ValidateServerCertficate(
object sender,
X509Certificate cert,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
{
// Good certificate.
return true;
}
Common.Helpers.Logger.Log.Error(string.Format("SSL certificate error: {0}", sslPolicyErrors));
try
{
using (X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
{
store.Open(OpenFlags.ReadWrite);
store.Add(new X509Certificate2(cert));
store.Close();
}
return true;
}
catch (Exception ex)
{
Common.Helpers.Logger.Log.Error(string.Format("SSL certificate add Error: {0}", ex.Message));
}
return false;
}
これは私たちのアプリケーションではうまく機能するようで、ユーザーが「いいえ」を押すと通信は機能しません。
アップデート:2015-12-11 - StoreName.Root を StoreName.My に変更 - My は、Root ではなくローカル ユーザー ストアにインストールされます。一部のシステムでは、「管理者として実行」しても root が機能しません。
ただ、対象と発行者...コメントを含めるように devstuff のからの回答を踏まえ歓迎...
public class SelfSignedCertificateValidator
{
private class CertificateAttributes
{
public string Subject { get; private set; }
public string Issuer { get; private set; }
public string Thumbprint { get; private set; }
public CertificateAttributes(string subject, string issuer, string thumbprint)
{
Subject = subject;
Issuer = issuer;
Thumbprint = thumbprint.Trim(
new char[] { '\u200e', '\u200f' } // strip any lrt and rlt markers from copy/paste
);
}
public bool IsMatch(X509Certificate cert)
{
bool subjectMatches = Subject.Replace(" ", "").Equals(cert.Subject.Replace(" ", ""), StringComparison.InvariantCulture);
bool issuerMatches = Issuer.Replace(" ", "").Equals(cert.Issuer.Replace(" ", ""), StringComparison.InvariantCulture);
bool thumbprintMatches = Thumbprint == String.Join(" ", cert.GetCertHash().Select(h => h.ToString("x2")));
return subjectMatches && issuerMatches && thumbprintMatches;
}
}
private readonly List<CertificateAttributes> __knownSelfSignedCertificates = new List<CertificateAttributes> {
new CertificateAttributes( // can paste values from "view cert" dialog
"CN = subject.company.int",
"CN = issuer.company.int",
"f6 23 16 3d 5a d8 e5 1e 13 58 85 0a 34 9f d6 d3 c8 23 a8 f4")
};
private static bool __createdSingleton = false;
public SelfSignedCertificateValidator()
{
lock (this)
{
if (__createdSingleton)
throw new Exception("Only a single instance can be instanciated.");
// Hook in validation of SSL server certificates.
ServicePointManager.ServerCertificateValidationCallback += ValidateServerCertficate;
__createdSingleton = true;
}
}
/// <summary>
/// Validates the SSL server certificate.
/// </summary>
/// <param name="sender">An object that contains state information for this
/// validation.</param>
/// <param name="cert">The certificate used to authenticate the remote party.</param>
/// <param name="chain">The chain of certificate authorities associated with the
/// remote certificate.</param>
/// <param name="sslPolicyErrors">One or more errors associated with the remote
/// certificate.</param>
/// <returns>Returns a boolean value that determines whether the specified
/// certificate is accepted for authentication; true to accept or false to
/// reject.</returns>
private bool ValidateServerCertficate(
object sender,
X509Certificate cert,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
return true; // Good certificate.
Dbg.WriteLine("SSL certificate error: {0}", sslPolicyErrors);
return __knownSelfSignedCertificates.Any(c => c.IsMatch(cert));
}
}
心に留めておくべきことの一つは、ServicePointManager.ServerCertificateValidationCallbackを持つことは、それが唯一の彼らの結果を無効にする手段を提供し、CRLチェック、サーバー名の検証が行われていないことを意味していないようだということです。だからあなたのサービスがまだCRLを取得するにはしばらく時間がかかるかもしれませんが、あなたはそれがいくつかのチェックを失敗したことを後で知っているよ。
私は、Web要求がその正確な例外をスローしOPと同じ問題に実行していました。私は、セットアップが正しく、私は証明書がインストールされていた、と思ったすべてのものを持っていた、私はうまくマシンストアにそれを見つけて、Web要求に添付し、そして私が要求コンテキストに証明書の検証を無効にした可能性があります。
これは私が私のユーザーアカウントで実行されていたことが判明し、証明書がマシンストアに設置されたこと。これは、この例外をスローするWeb要求を引き起こしました。私が持っていた問題を解決するために、いずれかの管理者として実行していることや、ユーザーストアに証明書をインストールし、そこからそれを読みます。
C#は、それがWeb要求で使用できない場合でも、マシンストアに証明書を見つけることができ、これはOPの例外が発生することをウェブ要求が発行されるとスローされると思われます。