2009-05-21 9 views
9

Apparentemente stavo facendo la domanda sbagliata nel mio post precedente. Dispongo di un servizio Web protetto con un certificato X.509, in esecuzione come sito Web protetto (https://...). Voglio utilizzare il certificato del computer del cliente (anche X.509) emesso dalla CA principale della società per verificare sul server che il computer client è autorizzato a utilizzare il servizio. Per fare questo, ho bisogno di ispezionare il certificato e cercare qualche caratteristica identificativa e abbinarla a un valore memorizzato in un database (forse l'impronta digitale?).Come posso ottenere il certificato X509Certificato inviato dal client nel servizio web?

Ecco il codice che uso per ottenere il certificato dall'archivio certificati locale (sollevato direttamente dal http://msdn.microsoft.com/en-us/magazine/cc163454.aspx):

public static class SecurityCertificate 
{ 
    private static X509Certificate2 _certificate = null; 

    public static X509Certificate2 Certificate 
    { 
     get { return _certificate; } 
    } 

    public static bool LoadCertificate() 
    { 
     // get thumbprint from app.config 
     string thumbPrint = Properties.Settings.Default.Thumbprint; 
     if (string.IsNullOrEmpty(thumbPrint)) 
     { 
      // if no thumbprint on file, user must select certificate to use 
      _certificate = PickCertificate(StoreLocation.LocalMachine, StoreName.My); 
      if (null != _certificate) 
      { 
       // show certificate details dialog 
       X509Certificate2UI.DisplayCertificate(_certificate); 
       Properties.Settings.Default.Thumbprint = _certificate.Thumbprint; 
       Properties.Settings.Default.Save(); 
      } 
     } 
     else 
     { 
      _certificate = FindCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, thumbPrint); 
     } 

     if (null == _certificate) 
     { 
      MessageBox.Show("You must have a valid machine certificate to use STS."); 
      return false; 
     } 

     return true; 
    } 

    private static X509Certificate2 PickCertificate(StoreLocation location, StoreName name) 
    { 
     X509Store store = new X509Store(name, location); 
     try 
     { 
      // create and open store for read-only access 
      store.Open(OpenFlags.ReadOnly); 

      X509Certificate2Collection coll = store.Certificates.Find(X509FindType.FindByIssuerName, STSClientConstants.NBCCA, true); 
      if (0 == coll.Count) 
      { 
       MessageBox.Show("No valid machine certificate found - please contact tech support."); 
       return null; 
      } 

      // pick a certificate from the store 
      coll = null; 
      while (null == coll || 0 == coll.Count) 
      { 
       coll = X509Certificate2UI.SelectFromCollection(
         store.Certificates, "Local Machine Certificates", 
         "Select one", X509SelectionFlag.SingleSelection); 
      } 

      // return first certificate found 
      return coll[ 0 ]; 
     } 
     // always close the store 
     finally { store.Close(); } 
    } 

    private static X509Certificate2 FindCertificate(StoreLocation location, StoreName name, X509FindType findType, string findValue) 
    { 
     X509Store store = new X509Store(name, location); 
     try 
     { 
      // create and open store for read-only access 
      store.Open(OpenFlags.ReadOnly); 

      // search store 
      X509Certificate2Collection col = store.Certificates.Find(findType, findValue, true); 

      // return first certificate found 
      return col[ 0 ]; 
     } 
     // always close the store 
     finally { store.Close(); } 
    } 

Poi, allego il certificato al flusso in uscita nel seguente modo:

public static class ServiceDataAccess 
{  
    private static STSWebService _stsWebService = new STSWebService(); 

    public static DataSet GetData(Dictionary<string,string> DictParam, string action) 
    { 
     // add the machine certificate here, the first web service call made by the program (only called once) 
     _stsWebService.ClientCertificates.Add(SecurityCertificate.Certificate); 
     // rest of web service call here... 
    } 
} 

La mia domanda è questa: come faccio a "ottenere" il certificato nel codice del servizio web? La maggior parte dei frammenti di codice di esempio che ho trovato in merito a come eseguire la convalida personalizzata hanno una chiamata GetCertificate(), apparentemente supponendo che la parte sia così facile che tutti dovrebbero sapere come farlo?

La mia classe principale eredita da WebService, quindi posso usare Context.Request.ClientCertificate per ottenere un certificato, ma questo è un HttpClientCertificate, non un X509Certificate2. HttpContext mi dà lo stesso risultato. Altri approcci utilizzano tutti il ​​codice di configurazione Web per chiamare il codice di verifica predefinito, senza alcun indizio su come chiamare un metodo C# personalizzato per effettuare la verifica.

Sono sicuro che questa è un'altra "domanda stupida" che qualcuno tornerà e dirà "guarda, fai solo questo e funziona", ma va bene. Ho passato molte ore a cercare di farlo funzionare, e il mio orgoglio è quasi inesistente a questo punto. Qualcuno può mostrarmi l'errore dei miei modi?

Grazie, Dave

+0

Se non si dispone di HttpContext, consultare la risposta per http: // StackOverflow.it/questions/7528455/how-to-get-the-x509certificate-from-a-client-request? lq = 1 –

risposta

12

Ricordo fare qualcosa di simile, il suo stato un po ', ma, avete provato questo nel vostro servizio web:

X509Certificate2 cert = new X509Certificate2(Context.Request.ClientCertificate.Certificate); 
+0

Funziona! Ora posso finalmente arrivare alla carne del mio problema: capire quale valore posso usare per abbinarlo a un valore memorizzato nel database ... Grazie !! – DaveN59

+1

Thumbprint va bene se si utilizza il database come autorità affidabile, ho lavorato su app che hanno adottato questo approccio. OTOH, se hai bisogno di un modello di fiducia distribuito (usando CA), allora devi fare ulteriori controlli (ad esempio verificando la catena di certificati). – DSO

+0

Penso di aver agito troppo presto. Tentativo di accedere a Thumbprint genera un'eccezione 'System.Security.Cryptography.CryptographicException' - così, anche se il certificato http è ora di tipo X509Certificate2, è davvero lo stesso certificato inviato dal client o ho appena creato un nuovo bestia che sembra un po '? – DaveN59

1

In merito a come legare il certificato a un utente, supponendo che l'identità dell'utente associato alla chiave sia buona (poiché il certificato è stato riconvertito in una radice attendibile e non ha een revocato), quindi è necessario legare l'identità richiesta dal certificato ad un utente. Si può semplicemente usare il modulo di stringa LDAP del DN oggetto e cercarlo (cn = Username, ou = Dipartimento ...) per determinare l'ID locale. Questo è resiliant nel caso in cui l'utente rigenera la propria chiave/certificato a causa di una perdita della carta o della scadenza naturale del certificato. Ciò si basa sul fatto che due CA non emetteranno due certificati con lo stesso nome soggetto per due persone diverse.

Se il certificato è stato emesso da una CA Microsoft, potrebbe avere un UPN in esso che è effettivamente un nome di accesso dominio.

In alternativa, se si desidera associare l'identità dell'utente a un certificato effettivo, il metodo usuale è quello di memorizzare il nome dell'emittente e il numero di serie del certificato. Le CA devono emettere numeri di serie univoci per ciascun certificato. I numeri seriali delle note possono essere grandi a seconda della CA. Non è tuttavia detto che l'utilizzo di questo metodo significhi che i dettagli delle cert nel database devono essere aggiornati ogni volta che il certificato utente viene riemesso.

+1

Ottime informazioni! Se stavo ancora lavorando al progetto, questo è esattamente il tipo di informazioni di cui avrei bisogno per risolvere i problemi che ho creato nella prima implementazione. Tuttavia, dato che ora mi sono trasferito in un'altra azienda con responsabilità diverse, lascerò questo come esercizio per qualcun altro ... Grazie per il feedback! – DaveN59

Problemi correlati