6

私の C# アプリケーションでは、https 経由で Web サービスを呼び出し、既に持っている .crt ファイルを使用して検証する必要がありました。そのようなニーズに対する正しいソリューションがここにあります。私のような他の人に役立つかもしれないと考えて、実用的な解決策を得たら、この投稿を更新しました。

解決策 : 以下のコードは、アプリケーションの実行全体で 1 回だけ実行する必要があります。これにより、リクエストが呼び出されるたびに使用される ServerCertification および SSL プロパティを設定します。

        public static void setSSLCertificate()
    {
        clientCert = new X509Certificate2(AUTHEN_CERT_FILE); // Pointing to the .crt file that will be used for server certificate verification by the client
        System.Net.ServicePointManager.ServerCertificateValidationCallback += new System.Net.Security.RemoteCertificateValidationCallback(customXertificateValidation);
    }

    public static bool customXertificateValidation(Object sender, X509Certificate certificate, X509Chain chain, System.Net.Security.SslPolicyErrors sslPoicyErrors)
    {
        switch (sslPoicyErrors)
        {
            case System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors:
            case System.Net.Security.SslPolicyErrors.RemoteCertificateNameMismatch:
            case System.Net.Security.SslPolicyErrors.RemoteCertificateNotAvailable:
                break;
        }

        return clientCert.Verify();  // Perform the Verification and sends the result
    }

リクエストは、SSL を実装しない場合と同様に通常どおり行われます。投稿リクエストコードは次のとおりです。

        private static String SendPost(String uri, String post_data)
    {
        String resData = "";
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
        request.KeepAlive = false;
        request.ProtocolVersion = HttpVersion.Version10;
        request.ContentType = "application/x-www-form-urlencoded";
        request.Method = "POST";

        // turn request string into byte[]
        byte[] postBytes = Encoding.ASCII.GetBytes(post_data);

        Stream requestStream = null;

        try
        {
            // Send it
            request.ContentLength = postBytes.Length;
            requestStream = request.GetRequestStream();
            requestStream.Write(postBytes, 0, postBytes.Length);
        }
        catch (WebException we)
        {   // If SSL throws exception that will be handled here
            if (we.Status == WebExceptionStatus.TrustFailure)
                throw new Exception("Exception Sending Data POST : Fail to verify server " + we.Message);
        }
        catch (Exception e)
        {
            throw new Exception("Exception Sending Data POST : " + e.Message, e.InnerException);
        }
        finally
        {
            if (requestStream != null)
                requestStream.Close();
        }

        // Get the response
        HttpWebResponse response = null;
        try
        {
            response = (HttpWebResponse)request.GetResponse();
            if (response == null)
                return "";
            StreamReader sr = new StreamReader(response.GetResponseStream());
            resData = sr.ReadToEnd().Trim();
            sr.Close();
        }
        catch (Exception e)
        {
            throw new Exception("Error receiving response from POST : " + e.Message, e.InnerException);
        }
        finally
        {
            if (response != null)
                response.Close();
        }

        return resData;
    }

サーバー証明書を受け入れることで、目標を大幅に達成するのに役立つ説明をしてくれた Dipti Mehta に感謝します。彼女は私の混乱を解決するのを手伝ってくれました。クライアントが .crt ファイルを使用してサーバー証明書を検証する方法をついに見つけました。

これが誰かに役立つことを願っています。

ありがとう

4

4 に答える 4

11

HTTPS サイトを参照すると、Web サーバーから提供された証明書を信頼するかどうかを尋ねるダイアログ ウィンドウが表示されることがあります。そのため、証明書を受け入れる責任はユーザーが処理します。Web サービスのシナリオに戻りましょう。SSL と HTTPS を使用する Web サーバー上にある Web サービスを呼び出したい場合、問題があります。

コードから呼び出しを行う場合、ダイアログ ウィンドウがポップアップして、証明書を信頼するかどうかを確認することはありません。おそらく、次の例外が発生します。

タイプ 'System.Net.WebException' の未処理の例外が system.dll で発生しました

追加情報: 基になる接続が閉じられました: リモート サーバーとの信頼関係を確立できませんでした。

ただし、この問題には解決策があります。独自の CertificatePolicy クラス (ICertificatePolicyインターフェースを実装する) を作成することにより、コードでこれを解決できます。このクラスでは、ダイアログ ウィンドウで [はい] または [いいえ] を押すように、true または false を返す独自の CheckValidationResult 関数を記述する必要があります。開発目的で、すべての証明書を受け入れる次のクラスを作成したので、厄介な WebException が発生することはもうありません。

public class TrustAllCertificatePolicy : System.Net.ICertificatePolicy
{
  public TrustAllCertificatePolicy() 
  {}

  public bool CheckValidationResult(ServicePoint sp, X509Certificate cert,WebRequest req, int problem)
  {
    return true;
  }
}

ご覧のとおり、CheckValidationResult 関数は常に true を返すため、すべての証明書が信頼されます。このクラスをもう少し安全にしたい場合は、たとえば X509Certificate パラメーターを使用して追加のチェックを追加できます。この CertificatePolicy を使用するには、ServicePointManager にそれを使用するように指示する必要があります。

System.Net.ServicePointManager.CertificatePolicy = new TrustAllCertificatePolicy();

これは、Web サービスを呼び出す前に (アプリケーションのライフ サイクル中に 1 回) 実行する必要があります。

于 2011-04-19T07:41:16.210 に答える
1

こんにちは。

あなたが提供する解決策が実際に問題の有効な解決策であるかどうかはわかりません. また、これに関するあなたのコメントのいくつかHttpWebRequest.ClientCertificatesはこれを示しています。

まず、クライアント証明書を検証するサーバーと、サーバー証明書を検証するクライアントを区別することが重要です。コレクションHttpWebRequest.ClientCertificatesは、クライアント証明書をサーバーに送信するために使用されるため、サーバーはクライアントが誰であるかを検証できます。あなたの質問(私が理解している限り)は、デフォルトの検証(自己署名証明書など)に合格しないサーバー証明書を、クライアントにローカルに保存されている証明書に対して検証する方法でした。

この場合の解決策はSystem.Net.ServicePointManager.ServerCertificateValidationCallback、カスタム検証を使用して提供することです。ただし、検証方法が間違っているようです。ローカル証明書を検証し、サーバーから送信された証明書を気にしません。私が使うのは次のようなものです:

public static bool customXertificateValidation(
    Object sender, X509Certificate certificate, 
    X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
    if (sslPolicyErrors == SslPolicyErrors.None)
        return true;

    return clientCert.Equals(certificate);
};

このメソッドは、サーバー証明書がデフォルトの検証に合格した (エラーがない) 場合にそれを受け入れ、証明書のローカルのクライアント コピーをサーバーによって提供されたものと比較することを保証します。Equals テストに合格した場合にのみ、クライアントは安全に続行できます。

于 2012-07-05T11:46:33.270 に答える
0

「検証」とは、認証を意味しますか?その場合、.crt では不十分で、公開鍵のみが含まれています。自分自身を認証し、それを ClientCertificates に入れるには、秘密鍵が必要です。.pfx ファイルから読み取るか、それを証明書コンテナーにインポートして、そこから使用することができます。

于 2011-04-19T07:42:24.517 に答える