2009-05-21 15 views
9

Podobno zadałem niewłaściwe pytanie w moim poprzednim poście. Mam usługę internetową zabezpieczoną certyfikatem X.509, działającą jako bezpieczna witryna internetowa (https://...). Chcę użyć certyfikatu komputera klienta (również X.509) wydanego przez główny urząd certyfikacji przedsiębiorstwa, aby zweryfikować na serwerze, że komputer klienta jest uprawniony do korzystania z usługi. Aby to zrobić, muszę sprawdzić certyfikat i wyszukać jakąś funkcję identyfikującą i dopasować ją do wartości przechowywanej w bazie danych (być może Thumbprint?).Jak uzyskać certyfikat X509 wysłany od klienta w serwisie WWW?

Oto kod używam, aby uzyskać certyfikat z lokalnego magazynu certyfikatów (podnoszone prosto z 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(); } 
    } 

Potem dołączyć zaświadczenie do strumienia wychodzącego wygląda następująco:

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

Moje pytanie brzmi: jak "uzyskać" certyfikat w kodzie usługi sieciowej? Większość przykładowych fragmentów kodu, na które natknąłem się, w jaki sposób przeprowadzić niestandardową walidację, ma wywołanie GetCertificate(), zakładając, że ta część jest tak łatwa, że ​​każdy powinien wiedzieć, jak to zrobić?

Moja główna klasa dziedziczy z WebService, więc mogę użyć certyfikatu Context.Request.ClientCertificate, aby uzyskać certyfikat, ale jest to certyfikat HttpClientCertificate, a nie X509Certificate2. HttpContext daje mi taki sam wynik. Inne podejścia wykorzystują kod konfiguracji sieciowej do wywoływania wstępnie zdefiniowanego kodu weryfikacyjnego, bez wskazówki, jak wywołać niestandardową metodę C# do przeprowadzenia weryfikacji.

Jestem pewien, że to kolejne "głupie pytanie", że ktoś wróci i powie: "spójrz, po prostu zrób to i działa", ale to jest w porządku. Spędziłem wiele godzin próbując zmusić to do działania, a moja duma prawie nie istnieje w tym momencie. Czy ktoś może mi pokazać błąd moich działań?

Dzięki, Dave

+0

Jeśli nie masz HttpContext, zobacz odpowiedź na http: // stackoverflow.com/questions/7528455/how-to-get-the-x509 certificate-from-a-client-request? lq = 1 –

Odpowiedz

12

Pamiętam robi coś podobnego, jego zostały chwilę, ale próbowałeś to w usłudze internetowej:

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

To działa! Teraz mogę w końcu dostać się do mięsa mojego problemu - zastanawiając się, jaką wartość mogę użyć, aby dopasować do wartości przechowywanej w bazie danych ... Dzięki! – DaveN59

+1

Odcisk palca jest odpowiedni do użycia, jeśli traktujesz bazę danych jako zaufany organ, pracowałem nad aplikacjami, które przyjęły to podejście. OTOH, jeśli potrzebujesz modelu rozproszonego zaufania (przy użyciu urzędów certyfikacji), musisz wykonać dodatkowe kontrole (np. Weryfikacja łańcucha certyfikatów). – DSO

+0

Myślę, że mogłem działać zbyt szybko. Próba uzyskania dostępu do funkcji Thumbprint powoduje zgłoszenie wyjątku "System.Security.Cryptography.CryptographicException" - mimo że certyfikat http jest teraz typu X509Certificate2, czy to naprawdę ten sam certyfikat wysłany przez klienta, czy też po prostu utworzyłem nowy bestia, która tak po prostu wygląda? – DaveN59

1

Na temat sposobu powiązania certyfikatu z powrotem z użytkownikiem, więc założenie, że tożsamość użytkownika powiązanego z kluczem jest dobra (ponieważ certyfikat został zweryfikowany z powrotem do zaufanego katalogu głównego i nie ma b) een unieważniony), następnie musisz powiązać tożsamość zgłoszoną przez certyfikat z użytkownikiem. Możesz po prostu użyć ciągu znaków LDAP podmiotu DN i sprawdzić to (cn = Username, ou = Department ...), aby określić lokalny identyfikator. Jest to nieszkodliwe w przypadku, gdy użytkownik ponownie wygeneruje swój klucz/certyfikat z powodu utraty karty lub naturalnego wygaśnięcia certyfikatu. Wynika to z faktu, że dwa CA nie wydadzą dwóch certyfikatów o tej samej nazwie podmiotu dwóm różnym osobom.

Jeśli certyfikat został wydany przez urząd certyfikacji MS, może to być nazwa UPN, która jest faktycznie nazwą logowania domeny.

Ewentualnie, jeśli chcesz powiązać tożsamość użytkownika z faktycznym certyfikatem, zwykłą metodą jest przechowywanie nazwy emitenta i numeru seryjnego certyfikatu. Urzędy certyfikacji muszą wystawiać unikalne numery seryjne dla każdego certyfikatu. Uwaga: numery seryjne mogą być duże w zależności od urzędu certyfikacji. Nie oznacza to jednak, że szczegóły certyfikatu w bazie danych muszą być aktualizowane za każdym razem, gdy ponownie wydany zostanie certyfikat użytkownika.

+1

Doskonała informacja! Gdybym nadal pracował nad projektem, jest to dokładnie ten rodzaj informacji, jaki byłby potrzebny, aby rozwiązać problemy, które stworzyłem w pierwszej implementacji. Ponieważ jednak przeniosłem się do innej firmy o różnych obowiązkach, zostawię to jako ćwiczenie dla kogoś innego ... Dzięki za informację zwrotną! – DaveN59