2011-10-26 11 views
5

È piuttosto difficile trovare informazioni sulla libreria XMLRPC.net utilizzata con https.Implementazione di C# XMLRPC.NET client e server su HTTPS

L'unica documentazione in cui è possibile impostare un URL "https" è qui: http://xml-rpc.net/faq/xmlrpcnetfaq-2-5-0.html#2.3 ma non spiega esattamente come si può configurare correttamente.

sperimentazione sulla base di campioni forniti nel download http://xmlrpcnet.googlecode.com/files/xml-rpc.net.2.5.0.zip ho provato questo:

modifiche nel file Client.cs di soluzione StateNameServer:

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

Quello che il codice del server assomiglia

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

Le ovviamente incentrate client rilascia un'eccezione quando provi a contattare il server usando HTTPS perché non so come configurarlo. Qualcuno potrebbe aiutare comunque? Che tipo di cose dovrei cercare?

Grazie mille!

risposta

3

primo luogo, vorrei ringraziare calorosamente Charles Cook per il suo aiuto su questo problema e per lo sviluppo di XMLRPC. NETTO.

In secondo luogo, questo campione si basa sul campione XMLRPC.NET StateNameServer disponibile per il download qui: http://xml-rpc.net/download.html

Quindi, ecco la soluzione:

1. Generare o ottenere un [autofirmato] certificato (usando Makecert.exe per esempio)

2. Aggiungi questo certificato per la configurazione del server e specificare la porta che si desidera utilizzare con il server XMLRPC.NET (in questo caso 5678) utilizzando httpcf g.exe o un altro strumento simile HttpSysConfig (Open Source)

3. Implementare il server XMLRPC.NET utilizzando il seguente codice:

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. Implementare il client di XMLRPC.NET utilizzando il seguente codice (il codice crea anche un nuovo certificato client 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); 
    } 
} 

Certo, non me ne frega qui l'interfaccia implem entazione per ServerStateName ma lo troverai nei file di esempio usando il link di download in alto.

Nota:

System.Net.ServicePointManager.CertificatePolicy = new TrustAllCertificatePolicy(); consentirà all'implementazione del server di accettare il certificato autofirmato da te generato. Penso che questo non sia necessario con i certificati emessi dalle autorità di certificazione.

Se trovi qualcosa che potrebbe essere migliorato e non è corretto, sarà molto apprezzato.

+0

Dopo il test, posso confermare che l'implementazione funziona su due macchine Windows separate. – virrea

2

Creare un proxy client utilizzando XmlRpcProxyGen.Create, specificando l'https https (l'interfaccia dovrebbe derivare da IXmlRpcProxy). Se è necessario fornire certificati client, il proxy ha una proprietà ClientCertificates che può essere utilizzata allo stesso modo della proprietà corrispondente nella classe System.Net.HttpWebRequest.

Non credo che Remoting possa supportare HTTPS. È possibile utilizzare HttpListener come descritto nella XML-RPC.NET FAQ, ma è necessario configurare un certificato, ad esempio come descritto in questo blog

+0

Grazie Charles. Ho provato a utilizzare XmlRpcProxyGen ma il client continua a rilasciare un'eccezione durante il tentativo di inviare dati. Il server non ha bisogno anche di un qualche tipo di certificato? – virrea

+0

Aggiunto paragrafo sull'implementazione di un server. –

+0

Grazie per il tuo aiuto, Charles, il tuo link mi ha aiutato a scoprire la soluzione. Ho aggiunto il certificato al server (il mio computer locale in effetti) ... il resto della soluzione qui sotto nella mia risposta. – virrea

1

Grande articolo! Mi aiuta molto Ma gli articoli 1 e 2 mi hanno preso un giorno per capirlo. Quindi ecco la mia esperienza:

  1. Per generare un certificato autofirmato ho utilizzato lo strumento openssl. Basta seguire le istruzioni nel link. Questo certificato mi serviva per l'applicazione client.
  2. Per l'applicazione server avevo bisogno di un altro certificato. Il codice server utilizza la classe HttpListener che non ha alcuna proprietà Certificati. Non c'è modo di applicare un certificato specifico all'istanza della classe HttpListener. C'è un'altra strategia:
    • Creare un nuovo certificato nella memoria del certificato locale. Per eseguire questo tipo 'mmc' in cmd-> File-> Aggiungi/Rimuovi snap-in> Certificati-> Aggiungi-> Account computer-> Computer locale-> OK. Vai a Personale-> Certificati-> Fare clic con il tasto destro-> Tutte le attività-> Richiedi nuovo certificato. Avanti-> Avanti-> seleziona Server Web-> fai clic sul link blu-> in pratica devi sentire solo il Nome comune qui (inserisci il nome del certificato desiderato). Ok-> Iscriviti. Ora hai il tuo certificato nella memoria locale.
    • Associare il certificato creato a una specifica coppia di porte/ip. Per eseguire questa operazione, eseguire la stringa seguente in cmd: netsh http add sslcert ipport=192.168.111.195:4022 certhash=c8973a86141a7a564a6f509d1ecfea326a1852a2 appid={0a582a74-fc2d-476c-9281-c73b2e4bfb26}, dove "ipport" è una coppia ip/port che verrà utilizzata per la connessione ssl; 'certhash' è un hash del certificato (certificato aperto che hai creato nel passaggio precedente-> vai a Dettagli-> cerca Thumbprint); 'Appid' potrebbe essere qualsiasi.

Se si specifica 'https' nel vostro HttpListener url questa classe cercherà automaticamente i certificati rilegata.

+0

Glat ha aiutato! :) Avrò una prova ai tuoi appunti. – virrea