2009-05-21 8 views
9

Apparemment, je posais la mauvaise question dans mon post précédent. J'ai un service web sécurisé avec un certificat X.509, fonctionnant comme un site web sécurisé (https://...). Je souhaite utiliser le certificat d'ordinateur du client (également X.509) émis par l'autorité de certification racine de l'entreprise pour vérifier auprès du serveur que l'ordinateur client est autorisé à utiliser le service. Pour ce faire, je dois inspecter le certificat et rechercher une fonction d'identification et faire correspondre celle-ci à une valeur stockée dans une base de données (peut-être le Thumbprint?).Comment puis-je obtenir le certificat X509 envoyé par le client dans le service Web?

Voici le code que j'utilise pour obtenir le certificat du magasin de certificats local (levé tout droit de 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(); } 
    } 

Ensuite, je joins le certificat au flux sortant ainsi:

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... 
    } 
} 

Ma question est la suivante: comment obtenir le certificat dans le code du service Web? La plupart des exemples de code que j'ai rencontré et qui traitent de la validation personnalisée ont un appel GetCertificate(), supposant apparemment que cette partie est si facile que tout le monde devrait savoir comment le faire?

Ma classe principale hérite de WebService, donc je peux utiliser Context.Request.ClientCertificate pour obtenir un certificat, mais c'est un HttpClientCertificate, pas un X509Certificate2. HttpContext me donne le même résultat. D'autres approches utilisent toutes le code de configuration Web pour appeler le code de vérification prédéfini, sans aucune indication sur la façon d'appeler une méthode C# personnalisée pour effectuer la vérification. Je suis sûr que c'est une autre "question stupide" que quelqu'un va revenir et dire "regarde, fais ça et ça marche", mais c'est OK. J'ai passé de nombreuses heures à essayer de le faire fonctionner, et ma fierté est presque inexistante à ce stade. Quelqu'un peut-il me montrer l'erreur de mes manières?

Merci, Dave

+0

Si vous n'avez pas HttpContext, voyez la réponse pour http: // stackoverflow.com/questions/7528455/how-to-get-the-x509certificate-from-a-request-client? lq = 1 –

Répondre

12

Je me souviens de faire quelque chose de semblable, son été pendant quelque temps, mais, avez-vous essayé dans votre service Web:

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

Ça marche! Maintenant, je peux enfin arriver à la viande de mon problème - déterminer quelle valeur je peux utiliser pour correspondre à une valeur stockée dans la base de données ... Merci !! – DaveN59

+1

Empreinte est bon à utiliser si vous traitez votre base de données comme autorité de confiance, j'ai travaillé sur des applications qui ont adopté cette approche. OTOH, si vous avez besoin d'un modèle de confiance distribué (en utilisant des autorités de certification), vous devez effectuer des vérifications supplémentaires (par exemple vérifier la chaîne de certificats). – DSO

+0

Je pense que j'ai peut-être agi trop tôt. Essayer d'accéder à l'Empreinte génère une exception 'System.Security.Cryptography.CryptographicException' - donc, même si le certificat http est maintenant de type X509Certificate2, est-ce vraiment le même certificat envoyé par le client, ou est-ce que je viens de créer un nouveau Bête que juste un peu ressemble à ça? – DaveN59

1

Sur la façon de lier le certificat à un utilisateur, en supposant que l'identité de l'utilisateur associé à la clé est bonne (le certificat a été vérifié à une racine approuvée et n'a pas een révoqué), vous devez lier l'identité revendiquée par le certificat à un utilisateur. Vous pouvez simplement utiliser la forme de chaîne LDAP du DN du sujet et chercher cela (cn = Nom d'utilisateur, ou = Département ...) pour déterminer l'ID local. Ceci est résilient dans le cas où l'utilisateur re-génère leur clé/certificat en raison d'une perte de carte ou de l'expiration naturelle du certificat. Cela repose sur le fait que deux CA ne délivreront pas deux certificats avec le même sujet à deux personnes différentes.

Si le certificat a été émis par une autorité de certification MS, il peut contenir un nom UPN qui est effectivement un nom d'ouverture de session de domaine.

Alternativement, si vous souhaitez lier l'identité de l'utilisateur à un certificat réel, la méthode habituelle consiste à stocker le nom de l'émetteur et le numéro de série du certificat. Les autorités de certification doivent émettre des numéros de série uniques pour chaque certificat. Notez que les numéros de série peuvent être importants en fonction de l'autorité de certification. Cependant, cela ne signifie pas que l'utilisation de cette méthode signifie que les informations sur les cert dans la base de données doivent être mises à jour chaque fois que le certificat utilisateur est réémis.

+1

Excellente information! Si je travaillais encore sur le projet, c'est exactement le type d'informations dont j'ai besoin pour résoudre les problèmes que j'ai créés lors de la première implémentation. Cependant, étant donné que j'ai maintenant déménagé dans une entreprise différente avec des responsabilités différentes, je vais laisser cela comme un exercice pour quelqu'un d'autre ... Merci pour les commentaires! – DaveN59

Questions connexes