web-dev-qa-db-ja.com

SSL証明書はlocalhostで機能しますが、コンピューター名またはIPでは機能しません

サーバー上で実行されているWebアプリケーションがあり、それはXDomainRequestを介してhttpリクエストを送信します(IE9のため)。

ソケットリスナーを介してポートでリッスンするコンソールアプリケーションを持つクライアントコンピューターはたくさんあります。クライアントはIE9ブラウザーでWebアプリケーションを開き、リンクをクリックすると、Webページは次のようなリクエストを送信します。

" https:// localhost:portNumber/applicationName/doSomething " " https:// computerName:portNumber/applicationName/doSomething " " https:// ipAddress :portNumber/applicationName/doSomething "

2番目と3番目の要求は、他のユーザーのコンピューターのコンソールアプリケーションに対して行われます。

問題は、リクエストがlocalhostに付属している場合、コンソールアプリケーションは着信データの読み取りと応答の返信について問題がないことです。ただし、リクエストにコンピューター名またはIPアドレスが含まれている場合、ブラウザーは認証の警告を表示し、ユーザーに[このWebサイトに進む(推奨されません)]リンクをクリックするように求めます。

コードで3つの異なる証明書を作成することを考えました。ただし、sslstreamを3つ使用することも可能です。最初に認証を行ってからデータを受信するため、真の認証を選択することはできません。したがって、着信要求をキャッチするとき、認証はすでに行われている必要があります。

もう1つの方法は、ソケットリスナーまたはsslstreamがこれら3つの要求すべてをローカルホストであるかのように動作させることです。したがって、各1つの認証はlocalhostとして行われます。しかし、私はそのための実際の方法を見つけることができませんでした。

これがコードです。 SslStreamの使い方が間違っている可能性があるため、コードを示します。

using System;
using System.Net.Sockets;
using System.Net;
using System.Configuration;
using System.Security.Cryptography.X509Certificates;
using System.Windows.Forms;
using System.IO;
using System.Net.Security;
using System.Security.Authentication;
using System.Threading;
using System.Text;

namespace StackOverFlowProject
{
    class StackOverFlowSample
    {
        private static ManualResetEvent _manualResetEvent = new ManualResetEvent(false);
        private static X509Certificate _cert = null;

        static void Main(string[] args)
        {
            StackOverFlowSample stackOverFlowSample = new StackOverFlowSample();
            stackOverFlowSample.StartListening();
        }

        private void StartListening()
        {
            GetCertificate();

            IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, 1234);

            if (localEndPoint != null)
            {
                Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                if (listener != null)
                {
                    listener.Bind(localEndPoint);
                    listener.Listen(10);

                    Console.WriteLine("Socket listener is running. Waiting for requests...");

                    listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);
                }
            }
        }

        private static void GetCertificate()
        {
            byte[] pfxData = File.ReadAllBytes(Application.StartupPath + @"\" + "localhost.pfx");

            _cert = new X509Certificate2(pfxData, "password", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
        }


        private void AcceptCallback(IAsyncResult result)
        {
            Socket listener = null;
            Socket handler = null;
            StateObject state = null;
            SslStream sslStream = null;

            _manualResetEvent.Set();

            listener = (Socket)result.AsyncState;

            handler = listener.EndAccept(result);

            state = new StateObject();

            if (handler.RemoteEndPoint != null)
            {
                state.clientIP = ((IPEndPoint)handler.RemoteEndPoint).Address.ToString();
            }

            sslStream = new SslStream(new NetworkStream(handler, true));
            sslStream.AuthenticateAsServer(_cert, false, SslProtocols.Tls, true);

            sslStream.ReadTimeout = 100000;
            sslStream.WriteTimeout = 100000;

            state.workStream = sslStream;

            if (state.workStream.IsAuthenticated)
            {
                state.workStream.BeginRead(state.buffer, 0, StateObject.BufferSize, ReceiveCallback, state);
            }

            listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);
        }

        private void ReceiveCallback(IAsyncResult result)
        {
            StateObject stateObject = null;
            SslStream sslStreamReader = null;

            byte[] byteData = null;

            stateObject = (StateObject)result.AsyncState;
            sslStreamReader = stateObject.workStream;

            int byteCount = sslStreamReader.EndRead(result);

            Decoder decoder = Encoding.UTF8.GetDecoder();
            char[] chars = new char[decoder.GetCharCount(stateObject.buffer, 0, byteCount)];
            decoder.GetChars(stateObject.buffer, 0, byteCount, chars, 0);
            stateObject.sb.Append(chars);

            if (byteCount > 0)
            {
                stateObject.totalReceivedBytes += byteCount;

                string[] lines = stateObject.sb.ToString().Split('\n');

                if (lines[lines.Length - 1] != "<EOF>")
                {
                    // We didn't receive all data. Continue reading...
                    sslStreamReader.BeginRead(stateObject.buffer, 0, stateObject.buffer.Length, new AsyncCallback(ReceiveCallback), stateObject);
                }
                else
                {
                    Console.WriteLine("We received all data. Sending response...");

                    byteData = Encoding.UTF8.GetBytes("Hello! I received your request!");

                    string httpHeaders = "HTTP/1.1" + "\r\n"
                                    + "Cache-Control: no-cache" + "\r\n"
                                    + "Access-Control-Allow-Origin: *" + "\r\n"
                                    + "\r\n";

                    byte[] byteHttpHeaders = Encoding.UTF8.GetBytes(httpHeaders);

                    byte[] concat = new byte[byteHttpHeaders.Length + byteData.Length];
                    Buffer.BlockCopy(byteHttpHeaders, 0, concat, 0, byteHttpHeaders.Length);
                    Buffer.BlockCopy(byteData, 0, concat, byteHttpHeaders.Length, byteData.Length);

                    stateObject.sslStreamReader = sslStreamReader;

                    sslStreamReader.BeginWrite(concat, 0, concat.Length, new AsyncCallback(SendCallback), stateObject);
                }
            }
        }

        private void SendCallback(IAsyncResult ar)
        {
            SslStream sslStreamSender = null;

            StateObject stateObject = (StateObject)ar.AsyncState;

            sslStreamSender = stateObject.sslStreamReader;
            sslStreamSender.EndWrite(ar);

            Console.WriteLine(stateObject.totalReceivedBytes.ToString() + " bytes sent to " + stateObject.clientIP + " address");

            sslStreamSender.Close();
            sslStreamSender.Dispose();
        }

    }

    public class StateObject
    {
        public SslStream workStream = null;

        public SslStream sslStreamReader = null;

        public const int BufferSize = 1024;
        public byte[] buffer = new byte[BufferSize];
        public StringBuilder sb = new StringBuilder();

        public string clientIP = "";

        public int totalReceivedBytes = 0;
    }
}
17
Orkun Bekar

発生している証明書の警告は、実際には名前の不一致エラーです。これは、SSL証明書の共通名(ドメイン名)が、Webサイト/サーバーへのアクセスに使用されるURL /アドレスと一致しないことを示します。

https://www.sslshopper.com/ssl-certificate-name-mismatch-error.html

使用シナリオでは、コンピューター名を利用する単純なドメインモデルを優先して、localhostおよびipアドレスから移行することができます。 (例:computerName.someDomain.com

次に、ワイルドカード証明書(例:*.someDomain.com)プロセス間通信の認証に使用できます。

https://www.sslshopper.com/best-ssl-wildcard-certificate.html

3
Seymour

セキュリティ担当者は正しいです。これを実現しようとしている方法は、SSLでは機能しません。

証明書がある場合は、1つのCNを認証するように設定されています。つまり、単純化しすぎた例についてです。 Googleには証明書があります。 https://*.google.comを認証します。つまり、google.comへのリクエストはすべて、有効な証明書を持っていると見なされます。そして、あなたのブラウザは満足しています。

次に、コマンドプロンプトを開いて、google.comにpingします。 IPアドレスを取得します(私の場合は216.58.210.14でした)。 https://216.58.210.14 と入力します。あなたのブラウザは、サイトが安全でないなどと不平を言っています。サーバーがあなたの以前のリクエストを提供したものと同じかもしれないという理由ですが、CNはグーグルではないので、あなたがそれに到達する方法は証明書に従って有効ではありません。 com、ただしIPアドレス。

したがって、(たとえば)127.0.0.1、10.92.1.4およびmyserver.comに接続する必要があるサービスがある場合は、それぞれのケースで有効な証明書が必要になります。

5
chris.ellis

_"https://computerName:portNumber/applicationName/doSomething"_の意味がわかりません。したがって、証明書の権利を使用しているかどうか、またはコードでアクセスまたは使用しているパス/接続が正しいかどうかはわかりません。 https://technet.Microsoft.com/en-us/library/dd891009.aspx

TCPに限定されない任意のプロトコルにアクセスできます。 https://technet.Microsoft.com/en-us/library/cc784450(v = ws.10).aspx

  • ワイルドカードSSL証明書を使用すると、管理者は1つのSSL証明書で同じドメイン上の無制限の数のサブドメインを保護できます。ワイルドカードSSL証明書は* .yourdomain.comに発行され、サブドメインごとに多数のSSL証明書を購入する必要があり、1つのIPアドレスのみを使用する必要があるため、コストを節約できます。
  • 一般的なSSL証明書は、1つの完全修飾ドメイン名のみを保護します。ユニファイドコミュニケーション証明書を使用すると、1つの証明書で複数のホスト名(サブジェクト代替名またはSANと呼ばれる)を割り当てることができます。完全な柔軟性を実現するために、マルチドメインSSL証明書では、サブジェクトの別名(SAN)フィールドを使用できるため、単一のSSL証明書を使用して最大100の異なるドメイン名、サブドメイン、またはパブリックIPアドレスを保護できます。 IPアドレス。

**値は、アクセスするホスト名またはドメイン名と正確に一致する必要があります。それ以外の場合は、証明書がインポートされて信頼された後も、ブラウザー証明書エラーを受け取ります。例:以前に_http://101.10.10.1:9000/_を使用してサイトにアクセスしていて、新しい証明書の共通名が「mydomain」である場合、ショートカットを_http://mydomain:9000/_に更新する必要があります。 _mydomain:9000_の証明書を作成した場合。次に、サーバーのアドレス指定に使用するホスト名またはCNAMEを使用します。これは非常に重要です。ウェブサーバーの実際のホスト名がmysubdomain.mydomain.comであるが、人々がwww.mydomain.comを使用してボックスをアドレス指定する場合は、後者の名前を使用して「一般名」(CNAME)の質問に答えます。

** _openssl s_time -connect remote.Host:443_(特定のsslコマンドを使用してトラブルシューティングを行い、opensslを引用しています)または_openssl s_time -connect remote.Host:443 -www /test.html -new_を使用して接続できるかどうかを確認します** SSL対応のWebサーバーがない場合使用可能な場合は、s_serverオプションを使用してエミュレートできます。

# on one Host, set up the server (using default port 4433) openssl s_server -cert mycerti.pem -www# on second Host (or even the same one), run s_time openssl s_time -connect mydomain:9001 -www / -new -ssl3

または

# on second Host (or even the same one), run s_time openssl s_time -connect mydomain:9001 -www / -new -ssl3 **証明書が使用されているかどうかを確認しますか? _$ openssl s_client -connect [ip_or_dns_name]:[port] $ man s_client_

1
Gary

私が提案しているソリューションは仮定を持っています:

  • サイトはIISでホストされています

  • 問題なくネットワーク経由でマシン名でアプリケーションにアクセスできます。

  • インターネット上のwebappにアクセスする必要はありません(認証機関からの証明書が必要になります)。

試してください:

  1. IISによって自己署名証明書を作成します:

    私。 IISを開き、次のようなサーバー証明書を選択します enter image description here

    ii。次のような自己署名証明書を作成します enter image description here

    iii。次のようにその証明書を使用するようにWebサイトを構成します。

    • webサイトを選択し、バインディングをクリックします。

    • httpsのバインドの編集をクリックします

enter image description here

iv。次のように作成した証明書を使用するようにサイトバインディングを編集します。

enter image description here

これで、マシン名でアプリケーションにアクセスできます。

インターネットのドメイン証明書についても同じプロセスを繰り返すことができますが、認証局によって登録/提供された証明書が必要です。

ローカルとネットワークの両方でMachine Nameを使用してアプリケーションにアクセスできるようになりました。証明書は単一のホスト名/共同ドメインに対して発行されるため、ローカルマシンでもlocalhostではなくマシン名を使用します。

0
Pranav Singh