Реализация C # XMLRPC.NET клиента и сервера через HTTPS - PullRequest
6 голосов
/ 26 октября 2011

Довольно сложно найти информацию о библиотеке XMLRPC.net, используемой с https.

Единственная документация, в которой можно задать URL-адрес "https", находится здесь: http://xml -rpc.net /faq / xmlrpcnetfaq-2-5-0.html # 2.3 , но, тем не менее, он не объясняет, как правильно выполнить настройку.

Эксперименты на основе образцов, представленных в загрузках http://xmlrpcnet.googlecode.com/files/xml-rpc.net.2.5.0.zip Я попробовал это:

Изменения в файле client.cs решения StateNameServer:

IStateName svr = (IStateName)Activator.GetObject(
typeof(IStateName), "https://localhost:5678/statename.rem");

Как выглядит код сервера

    IDictionary props = new Hashtable();
    props["name"] = "MyHttpChannel";
    props["port"] = 5678;
    HttpChannel channel = new HttpChannel(
    props,
    null,
    new XmlRpcServerFormatterSinkProvider()
    );

    ChannelServices.RegisterChannel(channel, false);

    RemotingConfiguration.RegisterWellKnownServiceType(
    typeof(StateNameServer),
    "statename.rem",
    WellKnownObjectMode.Singleton);

Клиент явно отбрасывает исключение при попытке связаться с сервером по протоколу HTTPS, потому что я не знаю, как его настроить.Может ли кто-нибудь помочь в любом случае, пожалуйста?Какие вещи я должен искать?

Большое спасибо!

Ответы [ 3 ]

3 голосов
/ 27 октября 2011

Во-первых, я хотел бы сердечно поблагодарить Чарльза Кука за помощь в решении этой проблемы и за разработку XMLRPC.NET.

Во-вторых, этот образец основан на образце XMLRPC.NET StateNameServer, доступном для загрузки здесь: http://xml -rpc.net / download.html

Итак, вот решение:

1. Создать или получить [самоподписанный] сертификат (например, с помощью makecert.exe)

2. Добавьте этот сертификат в конфигурацию вашего сервера и укажите порт, который вы хотите использовать с вашим сервером XMLRPC.NET (в данном случае 5678), используя httpcfg.exe или другой инструмент, например HttpSysConfig (с открытым исходным кодом)

3. Реализуйте свой сервер XMLRPC.NET, используя следующий код:

using System;
using System.Collections;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;

using CookComputing.XmlRpc;

using System.Net;
using System.IO;

public class _
{
    static void Main(string[] args)
    {
        HttpListener listener = new HttpListener();
        listener.Prefixes.Add("https://127.0.0.1:5678/");
        listener.Start();
        while (true)
        {
            HttpListenerContext context = listener.GetContext();
            ListenerService svc = new StateNameService();
            svc.ProcessRequest(context);
        }

        Console.WriteLine("Press <ENTER> to shutdown");
        Console.ReadLine();
    }
}

public class StateNameService : ListenerService
{
    [XmlRpcMethod("examples.getStateName")]
    public string GetStateName(int stateNumber)
    {
        if (stateNumber < 1 || stateNumber > m_stateNames.Length)
            throw new XmlRpcFaultException(1, "Invalid state number");
        return m_stateNames[stateNumber - 1];
    }

    string[] m_stateNames
      = { "Alabama", "Alaska", "Arizona", "Arkansas",
        "California", "Colorado", "Connecticut", "Delaware", "Florida",
        "Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", 
        "Kansas", "Kentucky", "Lousiana", "Maine", "Maryland", "Massachusetts",
        "Michigan", "Minnesota", "Mississipi", "Missouri", "Montana",
        "Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico", 
        "New York", "North Carolina", "North Dakota", "Ohio", "Oklahoma",
        "Oregon", "Pennsylviania", "Rhose Island", "South Carolina", 
        "South Dakota", "Tennessee", "Texas", "Utah", "Vermont", "Virginia", 
        "Washington", "West Virginia", "Wisconsin", "Wyoming" };
}

public abstract class ListenerService : XmlRpcHttpServerProtocol
{
    public virtual void ProcessRequest(HttpListenerContext RequestContext)
    {
        try
        {
            IHttpRequest req = new ListenerRequest(RequestContext.Request);
            IHttpResponse resp = new ListenerResponse(RequestContext.Response);
            HandleHttpRequest(req, resp);
            RequestContext.Response.OutputStream.Close();
        }
        catch (Exception ex)
        {
            // "Internal server error"
            RequestContext.Response.StatusCode = 500;
            RequestContext.Response.StatusDescription = ex.Message;
        }
    }
}

public class ListenerRequest : CookComputing.XmlRpc.IHttpRequest
{
    public ListenerRequest(HttpListenerRequest request)
    {
        this.request = request;
    }

    public Stream InputStream
    {
        get { return request.InputStream; }
    }

    public string HttpMethod
    {
        get { return request.HttpMethod; }
    }

    private HttpListenerRequest request;
}

public class ListenerResponse : CookComputing.XmlRpc.IHttpResponse
{
    public ListenerResponse(HttpListenerResponse response)
    {
        this.response = response;
    }

    string IHttpResponse.ContentType
    {
        get { return response.ContentType; }
        set { response.ContentType = value; }
    }

    TextWriter IHttpResponse.Output
    {
        get { return new StreamWriter(response.OutputStream); }
    }

    Stream IHttpResponse.OutputStream
    {
        get { return response.OutputStream; }
    }

    int IHttpResponse.StatusCode
    {
        get { return response.StatusCode; }
        set { response.StatusCode = value; }
    }

    string IHttpResponse.StatusDescription
    {
        get { return response.StatusDescription; }
        set { response.StatusDescription = value; }
    }

    private HttpListenerResponse response;
}

public class StateNameServer : MarshalByRefObject, IStateName
{
  public string GetStateName(int stateNumber)
  {
    if (stateNumber < 1 || stateNumber > m_stateNames.Length)
      throw new XmlRpcFaultException(1, "Invalid state number");
    return m_stateNames[stateNumber-1]; 
  }

  public string GetStateNames(StateStructRequest request)
  {
    if (request.state1 < 1 || request.state1 > m_stateNames.Length)
      throw new XmlRpcFaultException(1, "State number 1 invalid");
    if (request.state2 < 1 || request.state2 > m_stateNames.Length)
      throw new XmlRpcFaultException(1, "State number 1 invalid");
    if (request.state3 < 1 || request.state3 > m_stateNames.Length)
      throw new XmlRpcFaultException(1, "State number 1 invalid");
    string ret = m_stateNames[request.state1-1] + " "
      + m_stateNames[request.state2-1] + " " 
      + m_stateNames[request.state3-1];
    return ret;
  }

  string[] m_stateNames 
    = { "Alabama", "Alaska", "Arizona", "Arkansas",
        "California", "Colorado", "Connecticut", "Delaware", "Florida",
        "Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", 
        "Kansas", "Kentucky", "Lousiana", "Maine", "Maryland", "Massachusetts",
        "Michigan", "Minnesota", "Mississipi", "Missouri", "Montana",
        "Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico", 
        "New York", "North Carolina", "North Dakota", "Ohio", "Oklahoma",
        "Oregon", "Pennsylviania", "Rhose Island", "South Carolina", 
        "South Dakota", "Tennessee", "Texas", "Utah", "Vermont", "Virginia", 
        "Washington", "West Virginia", "Wisconsin", "Wyoming" };
}

4. Реализуйте свой XMLRPC.NET-клиент, используя следующий код (код также создает новый клиентский сертификат X509)

using System;
using System.Collections;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;

using CookComputing.XmlRpc;
using System.Net;
using System.Security.Cryptography.X509Certificates;

class _
{
    public class TrustAllCertificatePolicy : System.Net.ICertificatePolicy
    {
        public TrustAllCertificatePolicy() { }
        public bool CheckValidationResult(ServicePoint sp,
           X509Certificate cert,
           WebRequest req,
           int problem)
        {
            return true;
        }
    }
    static void Main(string[] args)
    {
        System.Net.ServicePointManager.CertificatePolicy = new TrustAllCertificatePolicy();
        IStateName proxy = XmlRpcProxyGen.Create<IStateName>();
        XmlRpcClientProtocol cp = (XmlRpcClientProtocol)proxy;
        cp.Url = "https://127.0.0.1:5678/";
        cp.ClientCertificates.Add(new System.Security.Cryptography.X509Certificates.X509Certificate(@"C:\path\to\your\certificate\file\my.cer"));
        cp.KeepAlive = false;
        //cp.Expect100Continue = false;
        //cp.NonStandard = XmlRpcNonStandard.All;

        string stateName = ((IStateName)cp).GetStateName(13);
    }
}

Конечно, я не даю здесь реализацию интерфейса для ServerStateName, но вы найдете его в примерах файлов, используя ссылку для скачивания вверху.

Примечание :

System.Net.ServicePointManager.CertificatePolicy = new TrustAllCertificatePolicy(); позволит реализации сервера принять самоподписанный сертификат, сгенерированный вами. Я думаю, что это не нужно с сертификатами, выданными сертификационными органами.

Если вы найдете что-то, что может быть улучшено и является неправильным, это будет высоко ценится.

2 голосов
/ 26 октября 2011

Создайте клиентский прокси с помощью XmlRpcProxyGen.Create, указав URL-адрес https (ваш интерфейс должен быть производным от IXmlRpcProxy). Если вам необходимо предоставить клиентские сертификаты, прокси-сервер имеет свойство ClientCertificates, которое можно использовать так же, как и соответствующее свойство в классе System.Net.HttpWebRequest.

Я не думаю, что Remoting может поддерживать HTTPS. Вы можете использовать HttpListener, как описано в XML-RPC.NET FAQ , но вам потребуется настроить сертификат, например, как описано в этом блоге

1 голос
/ 25 февраля 2013

Отличная статья! Помогает мне много Но пункты 1 и 2 заняли у меня день, чтобы разобраться. Итак, вот мой опыт:

  1. Для создания самозаверяющего сертификата я использовал инструмент openssl . Просто следуйте инструкциям в ссылке. Этот сертификат мне нужен для клиентского приложения.
  2. Для серверного приложения мне нужен был другой сертификат. Код сервера использует класс HttpListener, у которого нет свойства Certificates. Невозможно применить конкретный сертификат к экземпляру класса HttpListener. Есть еще одна стратегия:
    • Создать новый сертификат в локальном хранилище сертификатов. Для этого введите «mmc» в cmd-> Файл-> Добавить / Удалить оснастку-> Сертификаты-> Добавить-> Учетная запись компьютера-> Локальный компьютер-> ОК. Перейдите в Личные-> Сертификаты-> Правой кнопкой мыши-> Все задачи-> Запросить новый сертификат. Далее-> Далее-> выберите веб-сервер-> нажмите синюю ссылку-> в основном вам нужно чувствовать только общее имя (укажите желаемое имя сертификата). OK-> Записаться. Теперь у вас есть ваш сертификат в локальном хранилище.
    • Привязать созданный сертификат к определенной паре ip / port. Для этого запустите следующую строку в cmd: netsh http add sslcert ipport=192.168.111.195:4022 certhash=c8973a86141a7a564a6f509d1ecfea326a1852a2 appid={0a582a74-fc2d-476c-9281-c73b2e4bfb26}, где 'ipport' - это пара ip / port, которая будет использоваться для подключения ssl; 'certhash' - это хэш сертификата (откройте сертификат, который вы создали на предыдущем шаге -> перейдите в раздел Details -> ищите Thumbprint); 'appid' может быть любым.

Если вы укажете «https» в URL-адресе HttpListener, этот класс будет автоматически искать связанные сертификаты.

...