2012-10-16 13 views
5

Mam aplikację replikującą dane z usługi katalogowej przy użyciu typowego kodu System.DirectoryServices.DirectoryEntry. Mam teraz obowiązek replikacji z Novell eDirectory za pomocą SSL z samopodpisanym certyfikatem. Podejrzewam, że istniejący kod działałby z ważnym certyfikatem, który można zweryfikować, lub może, jeśli samopodpisany certyfikat zostanie dodany do magazynu kluczy lokalnego komputera. Aby jednak działało to na pewno za pomocą samopodpisanego certyfikatu, jedynym rozwiązaniem, jakie mogę znaleźć, jest użycie przestrzeni nazw System.DirectoryServices.Protocols i klasy LdapConnection, dzięki czemu mogę podłączyć wywołanie zwrotne VerifyServerCertificate. Nie mogę znaleźć żadnego sposobu zastosowania tej samej koncepcji do instancji DirectoryEntry lub połączenia z instancją LdapConnection i "konwertowania" tego do instancji DirectoryEntry. Może to nie jest możliwe, chciałbym to naprawdę potwierdzić. Wszelkie inne przemyślenia mile widziane.Ustawianie wywołania zwrotnego dla System.DirectoryServices.DirectoryEntry w celu obsługi certyfikatu SSL z podpisem własnym?

Jedyne istotne ogniwo Znalazłem to na stronie: http://www.codeproject.com/Articles/19097/eDirectory-Authentication-using-LdapConnection-and

Odpowiedz

8

To fenomenalny pytanie.

Od kilku dni walczę z tym samym problemem, a na koniec otrzymałem ostateczny dowód, dlaczego obiekt DirectoryEntry nie działa w tym scenariuszu.

Ten konkretny serwer Ldap (działający na LDAPS 636) również wydaje własny certyfikat z podpisem własnym. Korzystanie LdapConnection (i monitorowanie ruchu poprzez Wireshark), zauważyłem handshake odbywające że nie występuje podczas korzystania DirectoryEntry:

enter image description here

Pierwsza sekwencja jest z zabezpieczonym serwerze LDAP, druga sekwencja jest od moja maszyna. Kod, który prosi drugą sekwencję jest:

ldapConnection.SessionOptions.VerifyServerCertificate += delegate { return true; }; 

Są inni, sposób na „fałszywy out” callback, ale to co używam.

Niestety, DirectoryEntry nie ma opcji ani metody weryfikacji certyfikatu z podpisem własnym, dlatego akceptacja certyfikatu nigdy się nie dzieje (druga sekwencja), a połączenie nie może się zainicjować.

Jedynym możliwym sposobem osiągnięcia tego celu jest użycie LdapConnection w połączeniu z SearchRequest i SearchResponse. To, co mam do tej pory:

LdapConnection ldapConnection = new LdapConnection("xxx.xxx.xxx:636"); 

var networkCredential = new NetworkCredential("Hey", "There", "Guy"); 
ldapConnection.SessionOptions.SecureSocketLayer = true; 
ldapConnection.SessionOptions.VerifyServerCertificate += delegate { return true; }; 
ldapConnection.AuthType = AuthType.Negotiate; 
ldapConnection.Bind(networkCredential); 

SearchRequest request = new SearchRequest("DC=xxx,DC=xxx,DC=xxx", "(sAMAccountName=3074861)", SearchScope.Subtree); 
SearchResponse response = (SearchResponse)ldapConnection.SendRequest(request); 

if(response.Entries.Count == 1) 
{SearchResultEntry entry = response.Entries[0]; 
string DN = entry.DistinguishedName;} 

Stamtąd można zbierać właściwości AD z SearchResponse, a następnie odpowiednio przetwarzać. Jest to całkowite opóźnienie, ponieważ SearchRequest wydaje się być znacznie wolniejszy niż użycie DirectoryEntry.

Mam nadzieję, że to pomoże!

+0

Cześć. Dzięki za wejście, bardzo docenione. Doszedłem również do wniosku, że cały problem można rozwiązać w bardziej niezawodny sposób, korzystając z przestrzeni nazw protokołów niższego poziomu. Zauważyłem, że gdy certyfikat z podpisem własnym jest poprawnie dodany do lokalnego magazynu certyfikatów komputera, mogę ustawić wywołanie zwrotne VerifyServerCertificate w celu sprawdzenia certyfikatu z powodzeniem, coś w stylu "delegate" (połączenie LdapConnection, certyfikat certyfikatu X509) {return new X509Certificate2 (certyfikat) .Verify()} ", ale nadal nie mam radości z DirectoryEntry. – Andrew

+0

Zauważyłem również, że podczas zdarzeń w usłudze SystemEntry wystąpiły następujące zdarzenia systemowe: "Certyfikat odebrany z serwera zdalnego nie zawiera oczekiwanej nazwy, dlatego nie można ustalić, czy nawiązujemy połączenie z odpowiednim serwerem. Nazwa serwera, którego się spodziewaliśmy, to MYSERVER.MYDOMAIN.CO.UK. Żądanie połączenia SSL nie powiodło się. Załączone dane zawierają certyfikat serwera. " Zajmę się tym teraz, ale myślę, że odpowiedź wciąż jest obszarem nazw protokołów dla maksymalnej pewności i zmniejszonej zależności środowiskowej. – Andrew

+0

Andrew, dzięki za komentarz. Wygląda na to, że pracujemy nad bardzo podobnym celem. Ten błąd, który widzisz w zdarzeniach systemowych, pokrywa się z wieloma informacjami, które przeczytałem w Internecie, dotyczącymi tego, komu wydano certyfikat. Wiele serwerów, na których działa protokół SSL, z którymi próbuję nawiązać interakcję, ma certyfikat wydany dla innej domeny niż ta, do której przyłączam się w LdapConnection ... a to z pewnością powoduje awarię DirectoryEntry. – X3074861X

2

Obiecuję, to będzie mój ostatni wpis na to konkretne pytanie. :)

Po kolejnym tygodniu badań i rozwoju, mam na to dobre rozwiązanie, które jak dotąd sprawdziło się bardzo dobrze.

Podejście różni się nieco od mojej pierwszej odpowiedzi, ale generalnie jest to ta sama koncepcja; użycie LdapConnection do wymuszenia sprawdzenia poprawności certyfikatu.

//I set my Domain, Filter, and Root-AutoDiscovery variables from the config file 
string Domain = config.LdapAuth.LdapDomain; 
string Filter = config.LdapAuth.LdapFilter; 
bool AutoRootDiscovery = Convert.ToBoolean(config.LdapAuth.LdapAutoRootDiscovery); 

//I start off by defining a string array for the attributes I want 
//to retrieve for the user, this is also defined in a config file. 
string[] AttributeList = config.LdapAuth.LdapPropertyList.Split('|'); 

//Delcare your Network Credential with Username, Password, and the Domain 
var credentials = new NetworkCredential(Username, Password, Domain); 

//Here I create my directory identifier and connection, since I'm working 
//with a host address, I set the 3rd parameter (IsFQDNS) to false 
var ldapidentifier = new LdapDirectoryIdentifier(ServerName, Port, false, false); 
var ldapconn = new LdapConnection(ldapidentifier, credentials); 

//This is still very important if the server has a self signed cert, a certificate 
//that has an invalid cert path, or hasn't been issued by a root certificate authority. 
ldapconn.SessionOptions.VerifyServerCertificate += delegate { return true; }; 

//I use a boolean to toggle weather or not I want to automatically find and query the absolute root. 
//If not, I'll just use the Domain value we already have from the config. 
if (AutoRootDiscovery) 
{ 
    var getRootRequest = new SearchRequest(string.Empty, "objectClass=*", SearchScope.Base, "rootDomainNamingContext"); 
    var rootResponse = (SearchResponse)ldapconn.SendRequest(getRootRequest); 
    Domain = rootResponse.Entries[0].Attributes["rootDomainNamingContext"][0].ToString(); 
} 

//This is the filter I've been using : (&(objectCategory=person)(objectClass=user)(&(sAMAccountName={{UserName}}))) 
string ldapFilter = Filter.Replace("{{UserName}}", UserName); 

//Now we can start building our search request 
var getUserRequest = new SearchRequest(Domain, ldapFilter, SearchScope.Subtree, AttributeList); 

//I only want one entry, so I set the size limit to one 
getUserRequest.SizeLimit = 1; 

//This is absolutely crucial in getting the request speed we need (milliseconds), as 
//setting the DomainScope will suppress any refferal creation from happening during the search 
SearchOptionsControl SearchControl = new SearchOptionsControl(SearchOption.DomainScope); 
getUserRequest.Controls.Add(SearchControl); 

//This happens incredibly fast, even with massive Active Directory structures 
var userResponse = (SearchResponse)ldapconn.SendRequest(getUserRequest); 

//Now, I have an object that operates very similarly to DirectoryEntry, mission accomplished 
SearchResultEntry ResultEntry = userResponse.Entries[0]; 

Drugą rzeczą, jaką chciałem zwrócić uwagę jest to, że SearchResultEntry powróci użytkownika „atrybuty” zamiast „Właściwości”.

Atrybuty są zwracane jako tablice bajtów, więc musisz je kodować, aby uzyskać reprezentację ciągów. Na szczęście System.Text.Encoding zawiera natywną klasę ASCIIEncoding, która może sobie z tym poradzić bardzo łatwo.

string PropValue = ASCIIEncoding.ASCII.GetString(PropertyValueByteArray); 

I o to chodzi! Bardzo szczęśliwy, że w końcu udało się to ustalić.

Pozdrawiam!

+0

Dziękuję za to, postanowiłem również przebudować, aby w końcu użyć zestawu System.DirectoryServices.Protocols. To prawda, że ​​jest to trochę proceduralne, ale po pracy z bibliotekami Win32 LDAP, zanim poczuje się dużo bliżej metalu. I czuję się pewniej, że kod będzie można dostosować do przyszłych wymagań niż kod przy użyciu usług System.Directory opartych na ADSI. – Andrew

0

Użyłem poniższego kodu do łączenia się z ldaps przy użyciu DirectoryEntry.

Co mam rozumieć w moim scenerio jest DirectoryEntry nie działa, gdy LDAPS jest określony w ścieżce serwera lub typ uwierzytelniania jest wymieniony jako „AuthenticationTypes.SecureSocketsLayer”, ale jeśli tylko LDAPS port jest wymienione na końcu nazwa serwera pracy it . Po obejrzeniu logu Wiresharka widzę, jak odbywa się uścisk dłoni, jak wspomniano w powyższym poście.

Handshake: enter image description here

Kod:

public static SearchResultCollection GetADUsers() 
    { 
     try 
     { 
      List<Users> lstADUsers = new List<Users>(); 
      DirectoryEntry searchRoot = new DirectoryEntry("LDAP://adserver.local:636", "username", "password"); 
      DirectorySearcher search = new DirectorySearcher(searchRoot); 
      search.PropertiesToLoad.Add("samaccountname"); 
      SearchResult result; 
      SearchResultCollection resultCol = search.FindAll(); 
      Console.WriteLine("Record count " + resultCol.Count); 
      return resultCol; 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine("exception" + ex.Message); 
      return null; 
     } 
    } 
Powiązane problemy