2011-10-26 25 views
5

Trudno znaleźć informacje na temat biblioteki XMLRPC.net używanej z protokołem HTTPS.Implementacja klienta i serwera C# XMLRPC.NET za pośrednictwem protokołu HTTPS

Jedyna dokumentacja, w której można ustawić URL "https", znajduje się tutaj: http://xml-rpc.net/faq/xmlrpcnetfaq-2-5-0.html#2.3, ale nie wyjaśnia dokładnie, w jaki sposób można poprawnie skonfigurować.

Eksperymentowanie na podstawie próbek dostarczonych w pobrań http://xmlrpcnet.googlecode.com/files/xml-rpc.net.2.5.0.zip Próbowałem to:

Zmiany w pliku client.cs roztworu StateNameServer:

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

Jaki kod serwera wygląda

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); 

Klient jest obvi generalnie upuszcza wyjątek podczas próby skontaktowania się z serwerem przy użyciu protokołu HTTPS, ponieważ nie wiem, jak go skonfigurować. Czy ktokolwiek mógłby nam pomóc? Jakiego rodzaju rzeczy powinienem szukać?

Wielkie dzięki!

Odpowiedz

3

Po pierwsze, chciałbym podziękować serdecznie Charles Gotuj za pomoc na ten problem i rozwijania xmlrpc. NETTO.

drugie, próbka ta opiera się na próbie XMLRPC.NET StateNameServer dostępny do pobrania tutaj: http://xml-rpc.net/download.html

Więc oto rozwiązanie:

1. Generowanie lub dostać [autopodpisywany] certyfikat (na przykład przy użyciu makecert.exe)

2. Dodaj ten certyfikat do konfiguracji serwera i określ port, którego chcesz używać z serwerem XMLRPC.NET (w tym przypadku 5678) za pomocą httpcf g.exe lub innego narzędzia jak HttpSysConfig (Open Source)

3. Wdrożenie serwera XMLRPC.NET stosując następujący kod:

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. Wdrożenie klienta XMLRPC.NET pomocą następujący kod (kod tworzy również nowy certyfikat klienta 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); 
    } 
} 

oczywiście nie daję tu implementuje interfejs dla serwera ServerStateName, ale znajdziesz go w przykładowych plikach, korzystając z linku pobierania u góry.

Uwaga:

System.Net.ServicePointManager.CertificatePolicy = new TrustAllCertificatePolicy(); pozwoli implementacji serwera zaakceptować samopodpisany certyfikat, który sam wygenerowałeś. Myślę, że nie jest to konieczne w przypadku certyfikatów wydawanych przez urzędy certyfikacji.

Jeśli znajdziesz coś, co można poprawić i jest ono nieprawidłowe, będzie to bardzo doceniane.

+0

Po przetestowaniu mogę potwierdzić, że implementacja działa na dwóch oddzielnych komputerach z systemem Windows. – virrea

2

Utwórz proxy klienta za pomocą XmlRpcProxyGen.Create, podając adres URL https (Twój interfejs powinien pochodzić z IXmlRpcProxy). Jeśli musisz dostarczyć certyfikat klienta, proxy ma właściwość ClientCertificates, która może być używana w taki sam sposób, jak odpowiednia właściwość w klasie System.Net.HttpWebRequest.

Nie sądzę, aby program Remoting mógł obsługiwać protokół HTTPS. Można użyć HttpListener jak opisano w XML-RPC.NET FAQ ale trzeba będzie skonfigurować certyfikat, na przykład w sposób opisany w niniejszym blog

+0

Dziękuję, Charles. Próbowałem używać XmlRpcProxyGen, ale klient kontynuuje wysyłanie wyjątków podczas próby wysłania danych. Czy serwer nie potrzebuje też jakiegoś certyfikatu? – virrea

+0

Dodano akapit dotyczący implementacji serwera. –

+0

Dzięki za pomoc, Charles, Twój link pomógł mi znaleźć rozwiązanie. Dodałem certyfikat do serwera (w rzeczywistości mój komputer lokalny) ... reszta rozwiązania poniżej w mojej odpowiedzi. – virrea

1

Świetny artykuł! Bardzo mi pomaga. Ale pozycja 1 i 2 zajęły mi dzień, aby dowiedzieć się. Oto moje doświadczenie:

  1. Do wygenerowania samopodpisanego certyfikatu użyłem narzędzia openssl. Wystarczy postępować zgodnie z instrukcją pod linkiem. Ten certyfikat potrzebowałem do aplikacji klienckiej.
  2. Do aplikacji serwerowej potrzebowałem kolejnego certyfikatu. Kod serwera używa klasy HttpListener, która nie ma właściwości Certificates. Nie ma możliwości zastosowania określonego certyfikatu do instancji klasy HttpListener. Istnieje inna strategia:
    • Utwórz nowy certyfikat w lokalnym magazynie certyfikatu. Aby to zrobić, wpisz "mmc" w cmd-> Plik-> Dodaj/Usuń Snap-in> Certyfikaty-> Dodaj-> Konto komputera-> Komputer lokalny-> OK. Przejdź do Personal-> Certificates-> Right click-> All tasks-> Request new certificate. Dalej-> Dalej-> wybierz Serwer sieciowy-> kliknij niebieski link-> w zasadzie musisz poczuć tutaj tylko nazwę pospolitą (wpisać żądaną nazwę certyfikatu). Ok-> Zarejestruj się. Teraz masz swój certyfikat w lokalnej pamięci.
    • Powiąż utworzony certyfikat z określoną parą ip/port. Aby to zrobić, uruchom następujący ciąg w cmd: netsh http add sslcert ipport=192.168.111.195:4022 certhash=c8973a86141a7a564a6f509d1ecfea326a1852a2 appid={0a582a74-fc2d-476c-9281-c73b2e4bfb26}, gdzie "ipport" jest parą ip/port, która będzie używana do połączenia ssl; 'certhash' jest hash certyfikatu (certyfikat otwarty, który utworzyłeś w poprzednim kroku-> przejdź do Details-> look for Thumbprint); "appid" może być dowolny.

Jeśli podasz „https” w swojej HttpListener URL klasę automatycznie wyszuka certyfikatów zbindowanych.

+0

Glat pomógł! :) Będę próbował swoich notatek. – virrea

Powiązane problemy