2009-10-29 13 views
11

Próbuję uwierzytelnić się w usłudze WebService za pomocą mojego certyfikatu klienta, ale z pewnych powodów (wyjaśnię), nie chcę ładować certyfikatu ze sklepu, raczej odczytać go z dysku .Używanie certyfikatu klienta spoza magazynu certyfikatów

Poniższy:

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

powraca zawsze 403 - Służba mnie nie dopuszczają. Ale kiedy zapiszę ten certyfikat do CertStore, to działa. (Jak podano w MSDN).

Czy można użyć certyfikatu poza sklepem?

(powodem jest to, że mam usługa Windows (klient) czasami wzywającą usługa (serwer), a moje certyfikaty po bliżej nieokreślonym czasie do „zapomina” usługa i robi zezwolić na serwerze, bez widocznej przyczyny)

Odpowiedz

22

Jaki typ pliku to PathToCertificate? Jeśli jest to plik .cer, nie będzie zawierał klucza prywatnego dla certyfikatu i próba użycia tego certyfikatu dla protokołu SSL/TLS zakończy się niepowodzeniem.

Jeśli jednak masz plik PKCS7 lub PKCS12, który zawiera publiczny i prywatny klucz dla certyfikatu, twój kod będzie działał (może być konieczne użycie przeciążenia, które pobiera hasło, jeśli klucz prywatny go ma).

Aby to przetestować, połączyłem się z http://www.mono-project.com/UsingClientCertificatesWithXSP i utworzyłem plik client.p12 zgodnie z tymi instrukcjami. Stworzyłem również prosty serwer HTTPS przy użyciu HttpListener do testowania.

Potem opracowano następujący program do „Client.exe” i uruchomić jak:

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

gdzie client.p12 jest generowany plik PKCS12 przed i „hasło” to hasło I ustaw dla klucza prywatnego certyfikatu.

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

Daj mi znać, jeśli chcesz, żebym załadował kod serwera i certyfikaty użyte po obu stronach testu.

+0

Czy wiesz jak zrobić taką samą pracę w systemie Windows .NET? Z jakiegoś powodu nie mogę go uruchomić bez rejestracji certyfikatu w sklepie x509 – galets

1

Czy potrzebujesz hasła do certyfikatu? Jeśli tak, w konstruktorze jest dostępne pole.

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

masz potencjał, przez co najmniej dwa problemy ...

First ...

Plik certyfikatu klienta nie może zawierać klucz prywatny, chyba że jest dostępne za pomocą hasła. Powinieneś używać certyfikatu PKCS # 12 (* .pfx) z hasłem, aby twój klient miał dostęp do klucza prywatnego. Kod klienta będzie musiał podać hasło podczas otwierania certyfikatu, jak już napisali inni. Istnieje kilka sposobów, aby stworzyć ten najłatwiej jest użyć następującego wiersza polecenia, aby najpierw wygenerować certyfikat, a następnie użyć menedżera certyfikatów MMC eksportować certyfikatach klucza prywatnego:

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

Second ...

Twoim następnym problemem będzie serwer. Serwer musi zezwolić na ten certyfikat. Masz odpowiednią logikę, ale po niewłaściwej stronie drutu przesuń tę linię do serwera WWW obsługującego żądanie. Jeśli nie możesz, musisz wziąć ".plik cer”zapisane powyżej na serwer i dodać go do listy zaufanych komputerze serwera jest:

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

-1. Serwer nie musi znać klucza prywatnego certyfikatu klienta. Musi tylko zaufać urzędowi certyfikacji, który podpisał certyfikat klienta. Podczas negocjowania protokołu SSL3/TLS serwer wysyła listę z nazwą wyróżniającą wszystkich dozwolonych urzędów certyfikacji. W tym momencie klient powinien zobaczyć, że certyfikat CA klienta. znajduje się na liście i wysyła go. – Gonzalo

+0

Prawidłowo, nigdy nie powiedziałem, że SERVER potrzebuje klucza prywatnego. Musi ufać certyfikatowi lub wyraźnie zezwalać na to za pomocą kodu. Jeśli chodzi o resztę komentarza "... serwer wysyła listę z DN wszystkich urzędów certyfikacji ...", to po prostu nieprawda. –

+1

Nie prawda? Zobacz http://tools.ietf.org/html/rfc5246#page-53, sekcja 7.4.4: certificate_authorities Lista nazw wyróżniających [X501] akceptowalnych certificate_authorities, reprezentowanych w formacie DER. Te wyróżniające nazwy mogą określać pożądaną nazwę wyróżniającą dla głównego urzędu certyfikacji lub podrzędnego urzędu certyfikacji; w ten sposób ten komunikat może być używany do opisywania znanych źródeł, jak również pożądanej przestrzeni autoryzacji. Jeśli lista certyfikatów jest pusta, wówczas klient MOŻE wysłać certyfikat odpowiedniego klienta ClientCertificateType, unles – Gonzalo

2

Potencjalny problem może być buforowanie sesji SSL (Schannel cache). Tylko pierwsze żądanie negocjuje uzgadnianie SSL. Kolejne żądania będą korzystać z tego samego identyfikatora sesji i mam nadzieję, że serwer to zaakceptuje. Jeśli serwer wyczyści SessionId, żądania zakończą się błędem 403. Aby wyłączyć lokalne buforowanie sesji SSL (i zmusić negocjacji SSL dla każdego żądania) trzeba otworzyć okna folderów rejestru:

[HKEY_LOCAL_MACHINE] [System] [CurrentControlSet] [CONTROL] [SecurityProviders] [SCHANNEL]

i dodaj klucz o nazwie ClientCacheTime (DWORD) o wartości 0.

Ten problem jest pokryty tutaj:

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

+0

To jest całkiem olbrzymie. Jeśli tego nie wiesz, możesz spędzić dużo czasu myśląc, że sprowadzasz rozwiązanie "prób i błędów" do jego najprostszej postaci, a jednocześnie niczego nie dziwi. – Chazt3n

Powiązane problemy