2009-10-29 5 views
11

Sto tentando di autenticarmi contro WebService utilizzando il mio certificato client, ma, per qualche motivo (spiego), non voglio caricare il certificato dall'archivio, piuttosto leggerlo dal disco .Utilizzo del certificato client non nell'archivio certificati

la seguente:

// gw is teh WebService client 
X509Certificate cert = new X509Certificate(PathToCertificate); 
_gw.ClientCertificates.Add(ClientCertificate()); 
ServicePointManager.ServerCertificateValidationCallback = (a,b,c,d) => true; 
_gw.DoSomeCall(); 

restituisce sempre 403 - il servizio non mi autorizzazione. Ma quando salvo quel certificato in CertStore, funziona. (Come dichiarato in MSDN.)

È possibile utilizzare il certificato non in negozio?

(la ragione è, che ho ottenuto servizio di Windows (client) a volte chiama webservice (server), e dopo quantità di tempo non specificato il servizio 'dimentica' i miei certificati e entrate autorizzo contro di server, senza alcun motivo apparente)

risposta

22

Che tipo di file è PathToCertificate? Se è solo un file .cer, non conterrà la chiave privata per il certificato e tenterà di utilizzare quel certificato per SSL/TLS avrà esito negativo.

Tuttavia, se si dispone di un file PKCS7 o PKCS12 che include la chiave pubblica e privata per il certificato, il codice funzionerà (potrebbe essere necessario utilizzare il sovraccarico che accetta una password se la chiave privata ne ha uno).

Per verificarlo, sono passato a http://www.mono-project.com/UsingClientCertificatesWithXSP e ho creato il mio file client.p12 seguendo queste istruzioni. Ho anche creato un semplice server HTTPS utilizzando HttpListener per il test.

Poi ho compilato il seguente programma in 'Client.exe' e correre come:

client.exe https://<MYSSLSERVER>/ client.p12 password 

dove client.p12 è il file PKCS12 generato prima e 'password' è la password ho impostato per la chiave privata del certificato.

using System; 
using System.IO; 
using System.Net; 
using System.Security.Cryptography.X509Certificates; 
using System.Text; 

public class HttpWebRequestClientCertificateTest : ICertificatePolicy { 

    public bool CheckValidationResult (ServicePoint sp, X509Certificate certificate, 
      WebRequest request, int error) 
    { 
      return true; // server certificate's CA is not known to windows. 
    } 

    static void Main (string[] args) 
    { 
      string host = "https://localhost:1234/"; 
      if (args.Length > 0) 
        host = args[0]; 

      X509Certificate2 certificate = null; 
      if (args.Length > 1) { 
        string password = null; 
        if (args.Length > 2) 
          password = args [2]; 
        certificate = new X509Certificate2 (args[1], password); 
      } 

      ServicePointManager.CertificatePolicy = new HttpWebRequestClientCertificateTest(); 

      HttpWebRequest req = (HttpWebRequest) WebRequest.Create (host); 
      if (certificate != null) 
        req.ClientCertificates.Add (certificate); 

      WebResponse resp = req.GetResponse(); 
      Stream stream = resp.GetResponseStream(); 
      StreamReader sr = new StreamReader (stream, Encoding.UTF8); 
      Console.WriteLine (sr.ReadToEnd()); 
    } 
} 

Fammi sapere se vuoi che carichi il codice del server e i certificati utilizzati su entrambi i lati del test.

+0

sai come fare lo stesso lavoro su Windows .NET? Per qualche motivo non riesco a farlo funzionare senza registrare il certificato nel negozio x509 – galets

1

Hai bisogno di una password per il certificato? Se è così, c'è un campo per questo nel costruttore.

X509Certificate cert = new X509Certificate(PathToCertificate,YourPassword); 
2

si hanno le potenzialità per almeno due problemi ...

Prima ...

Il file di certificato client non può contenere una chiave privata meno che non sia accessibile con una password. Dovresti utilizzare un certificato PKCS # 12 (* .pfx) con una password in modo che il tuo cliente abbia accesso alla chiave privata. Il codice cliente dovrà fornire la password quando si apre il certificato come altri hanno già pubblicato. Ci sono diversi modi per creare questo, il modo più semplice è quello di utilizzare la seguente riga di comando per generare prima il certificato, quindi utilizzare il gestore certificato di MMC per esportare i certificati chiave privata:

Process p = Process.Start(
    "makecert.exe", 
    String.Join(" ", new string[] { 
     "-r",//      Create a self signed certificate 
     "-pe",//     Mark generated private key as exportable 
     "-n", "CN=" + myHostName,// Certificate subject X500 name (eg: CN=Fred Dews) 
     "-b", "01/01/2000",//  Start of the validity period; default to now. 
     "-e", "01/01/2036",//  End of validity period; defaults to 2039 
     "-eku",//     Comma separated enhanced key usage OIDs 
     "1.3.6.1.5.5.7.3.1," +// Server Authentication (1.3.6.1.5.5.7.3.1) 
     "1.3.6.1.5.5.7.3.2", //  Client Authentication (1.3.6.1.5.5.7.3.2) 
     "-ss", "my",//    Subject's certificate store name that stores the output certificate 
     "-sr", "LocalMachine",// Subject's certificate store location. 
     "-sky", "exchange",//  Subject key type <signature|exchange|<integer>>. 
     "-sp",//     Subject's CryptoAPI provider's name 
     "Microsoft RSA SChannel Cryptographic Provider", 
     "-sy", "12",//    Subject's CryptoAPI provider's type 
     myHostName + ".cer"//  [outputCertificateFile] 
    }) 
); 

Seconda ...

Il tuo prossimo problema sarà sul lato server. Il server deve consentire questo certificato. Hai la logica giusta, ma sul lato sbagliato del filo, sposta questa linea sul server web che gestisce la richiesta. Se non puoi, devi quindi prendere il '.file di cer' salvato in precedenza al server e aggiungerlo alla lista di fiducia del computer server:

ServicePointManager.ServerCertificateValidationCallback = (a,b,c,d) => true; 
+0

-1. Il server non ha bisogno di conoscere la chiave privata del certificato client. Ha solo bisogno di fidarsi della CA che ha firmato il certificato del cliente. Durante la negoziazione di SSL3/TLS, il server invia un elenco con il DN di tutte le CA consentite. A quel punto il client dovrebbe vedere che la CA del client cert. è nella lista e inviarlo. – Gonzalo

+0

Corretto, non ho mai detto che il SERVER abbia bisogno della chiave privata. Deve fidarsi del certificato o consentirlo esplicitamente con il codice. Per quanto riguarda il resto del tuo commento "... il server invia una lista con il DN di tutte le CA ..." che semplicemente non è vero. –

+1

Non è vero? Vedere http://tools.ietf.org/html/rfc5246#page-53, sezione 7.4.4: Autorità di certificazione Un elenco di nomi distinti [X501] di autorità di certificazione accettabili , rappresentati in formato codificato DER. Questi nomi distinti possono specificare un nome distinto desiderato per una CA radice o una CA subordinata; quindi, questo messaggio può essere utilizzato per descrivere le radici conosciute e lo spazio di autorizzazione desiderato. Se l'elenco certificate_authorities è vuoto, allora il client MAGGIO invia qualsiasi certificato del ClientCertificateType appropriato, unles – Gonzalo

2

Il potenziale problema potrebbe essere il caching delle sessioni SSL (cache Schannel). Solo la prima richiesta negozia l'handshake SSL. Le richieste successive utilizzeranno lo stesso ID di sessione e sperano che il server lo accetti. Se il server cancella SessionId, le richieste non riusciranno con errore 403. Per disabilitare locale caching sessione SSL (e forzare negoziazione SSL per ogni richiesta) è necessario aprire windows registro:

[HKEY_LOCAL_MACHINE] [System] [CurrentControlSet] [controllo] [SecurityProviders] [SCHANNEL]

e aggiungere la chiave di nome ClientCacheTime (DWORD) con valore 0.

Questo problema è coperto qui:

http://support.microsoft.com/?id=247658

+0

Questo è piuttosto enorme. Se non lo sai, puoi dedicare molto tempo a pensare che stai riducendo la tua soluzione di "tentativi ed errori" alla sua forma più semplice, mentre in realtà infastidisci le cose. – Chazt3n