Archiv für den Monat Februar 2012

SSL einstellen für IIS 7 mit C# und dem ServerManager


Wie ich bereits gezeigt habe gibt es die unterschiedlichsten Wege um für eine Webseite SSL einzustellen. Ich habe das ganze bereits hier gezeigt, in dem ich SSL manuell eingestellt habe und hier in dem ich SSL mit Hilfe von C# und DirectoryEntry gesetzt habe. Jetzt kommt noch das setzten von SSL mit Hilfe des ServerManagers hinzu, was aber nur für den IIS größer der Version 7 geht. Auch hier gehe ich nur auf Details ein die das Einstellen von SSL beinhalten.

Dazu muss gesagt werden das jede Webseite eine ID besitzt im IIS, welche in der ID-Spalte der Webseiten Auflistung des IIS zu finden ist.

image

In unserem Falle handelt es sich um die Webseite mit der ID = 1.

Das Folgende Beispiel Zeigt wie man die passende Webseite findet und ein Binding für ein passendes SSL Zertifikat setzt für Port 443. Hier wird nicht geprüft ob bereits ein Binding existiert, ….

 ServerManager iisManager = new ServerManager();
 Site baseWebsite = null;

 foreach (Site eSite in iisManager.Sites)
 {
     //Es handelt sich um die Basiswebseite diese hat immer die 
     //ID 1 (Sei denn sie wurde evtl. vom Admin gelöscht und neu angelegt)
     if (eSite.Id == 1)
     {
         baseWebsite = eSite;
         break;
     }
 }
 
 baseWebsite.Bindings.Add("*:443:",
         CertificateFunctions.GetCertificateByHashString("61a669c54ab30b5e72209ff88b3c0785df927228").GetCertHash(),
         CertificateFunctions.GetLocalCertificateStore().Name);
 iisManager.CommitChanges();

So einfach kann der SSL Port für eine Webseite mit dem ServerManager angelegt werden. Wenn Sie dann den IIS öffnen dann sehen Sie wie oben im Screenshot bereits zu sehen ist auch den Port 443 mit https in den Bindungen für Ihre Standard Webseite wieder.

So öffnen Sie den lokalen Zertifikatsstore um an den Storename zu kommen für das Zertifikat.

/// 
/// Gibt den Zertiofikat Store zurück der benutzt wird.
/// 
public static X509Store GetLocalCertificateStore()
{
    X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
    store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite);
    return store;
}

Das passende Zertifikat anhand des übergebenen Hashes ermitteln.

/// 
/// Sucht aus dem lokalen Zertifikaten das mit dem übergebenen
/// Hash heraus und gibt das passende Zertifikat zurück
/// 
/// der Hashstring nach dem gesucht werden soll in den Zertifikaten
/// das passende X509Certificate2 | wenns nicht gefunden wird NULL
public static X509Certificate2 GetCertificateByHashString(string hashString)
{
    //Öffnen des lokalen Zertifikatstores
    X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
    store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite);

    //Alle lokalen Zertifikate durchgehen.
    foreach (X509Certificate2 x509Certificate2 in store.Certificates)
    {
        //Wenn das Zertifikat mit dem Hashcode gefunden wurde das Zertifikat zurückgeben.
        if (hashString.ToLower() == x509Certificate2.GetCertHashString().ToLower())
        {
            return x509Certificate2;
        }
    }

    //Das Zertifikat konnte nicht gefunden werden.
    return null;
}

SSL einstellen für IIS mit C# und DirectoryEntry


Es gibt die unterschiedlichsten Wege um für eine Webseite SSL einzustellen, wie .z.B. das manuelle Hinzufügen einer https Verbindung mit dem IIS-Manager hier.

Dann kann man noch für den IIS 6 mit Hilfe von C# und DirectoryEntry SSL setzten. Im Folgenden erläutere ich welche Einstellungen im DirectoryEntry gesetzt werden müssen. Da ich leider keine “genaue” Anleitung gefunden habe, sondern alles mit Hilfe von Probieren und einem guten Metabase Explorer aus dem Resource Toolkit für den IIS 6 “herausgefunden” habe, kann es gut sein das es auch andere und evtl. einfachere Wege gibt das ganze einzustellen, wenn ja würde ich mich über einen Kommentar sehr freuen.

Als erstes müssen wir uns einen passenden DirectoryEntry heraussuchen für den wir SSL setzten wollen. Da man SSL für eine komplette Site setzt, suchen wir uns in meinem Beispiel die “Default Web Site” heraus und setzten hier den Standard SSL Port 443.

Ich setzte voraus das man im ISS bereits ein SSL Zertifikat angelegt hat, wie z.B. hier am Anfang erläutert wird, das Zertifikat mit dem Namen “squadwuschelscert”.

Eine Liste der installierten Zertifikate auf dem lokalen Rechner erhält man z.B. mit der folgenden Funktion:

/// <summary>
/// Gibt eine Liste mit den installierten Zertifikaten zurück
/// </summary>
public static SortedList<string, string> GetInstalledCertifcates()
{
    //SortedList(HashAlsString,CertificateFileName)
    SortedList<string, string> certificates = new SortedList<string, string>();
    bool edoCertIsInstalled = false;

    //Den lokalen Zertifikatsstore auf dem Rechner öffnen.
    X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
    store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite);

    //alle Zertifikate auf dem lokalen Rechner durchgehen.
    foreach (X509Certificate2 eCertificate in store.Certificates)
    {
        //Die Zertifikate unserer Liste hinzufügen, der Hash wird als Key verwendet, 
        //da dieser im Regelfall nur einmal im lokalen Store installiert sein sollte.
        if (!certificates.ContainsKey(eCertificate.GetCertHashString()))
        {
            if (eCertificate.FriendlyName.Trim().Length == 0)
            {
                certificates.Add(eCertificate.GetCertHashString(), eCertificate.SubjectName.Name);
            }
            else
            {
                certificates.Add(eCertificate.GetCertHashString(), eCertificate.FriendlyName);
            }
        }
    }

    return certificates;
}

Wir benötigen den passenden Zertifikatshash, den wir dann im DirectoryEntry eintragen (das Zertifikat muss auf dem Lokalen System installiert sein zu dem der Hash gehört!).

Der Normale HashString der vom Store für die einzelnen Zertifikate zurück gegeben wird, kann nicht im DirectoryEntry hinzugefügt werden, daher muss dieser “modifiziert” werden mit der folgenden Funktion, wird ein Hash der z.B: “61a669c54ab30b5e72209ff88b3c0785df927228” aussieht an die Funktion übergeben.

/// <summary>
/// Der normale HashString, der vom Zertifikat zurück gegeben wird, kann so nicht verwendet werden um ihn
/// dem directoryEntry hinzufügen zu können. Dieser wird hier bearbeitet und e
/// </summary>
/// <param name="certHashString">der HashString der in ein object array umgewandelt werden soll.</param>
public static object[] ConvertCertHashString(string certHashString)
{
    //Der String vom Certifikathash muss speziell "aufgearbeitet" werden, damit dieser unserem SSLCertHash im IIS hinzugefügt werden kann.
    string newHash = string.Empty;
    string tmp = string.Empty;
    for (int i = 1; i <= certHashString.Length; i++)
    {
        //BeispielString der erzeugt wird: "a7 1f db 98 92 7e 71 c0 7b 1e f7 0d 74 1d c4 5a 39 a8 22 f5"
        //damit eine Split methode ausgeführt werden kann.
        tmp += certHashString[i - 1];

        if (i % 2 == 0)
        {
            tmp += ' ';
            newHash += tmp.ToLower();
            tmp = string.Empty;
        }
    }

    //Damit der Eintrag dann dem DirectoryEntry hinzugefügt werden kann wird ein Object Array benötigt.
    string[] newValueSplit = newHash.Trim().Split(new Char[] { ' ' });
    object[] newObj = new object[newValueSplit.Length];
    newValueSplit.CopyTo(newObj, 0);

    return newObj;
}

Die Funktion gibt dann ein Object Array zurück, welches dem DirectoryEntry erfolgreich hinzugefügt werden kann.

Mit dem folgenden Code Ausschnitt suchen wir unsere Website heraus und setzten die DirectoryEntries. Wir müssen hier nur zwei Einträge setzten. Dazu gehört “SSLCertHash” und “SecureBindings”.

Der “SSLCertHash” Eintrag wird benötigt, damit der IIS weiß welches Zertifikat verwendet werden soll und mit Hilfe von “SecureBindings” wird der Port angegeben für den SSL Zugang. Wenn wir diese Werte dem DirectoryEntry hinzugefügt haben, dann werden automatisch noch weitere Werte angelegt, was man sehr gut in einem Metabase Explorer sehen kann wie z.B. “SLLCertStoreName”.

//Die Basiswebseite (ROOT) ermitteln.
DirectoryEntry w3svc = new DirectoryEntry("IIS://localhost/W3SVC/1");

//Der Hash kann immer wieder neu gesetzt werden, 
//d.h. wenn jmd ein anderes Zertifikat möchte muss man das alte erst entfernen
PropertyValueCollection propValues = w3svc.Properties["SSLCertHash"];
propValues.Clear();
w3svc.CommitChanges();

//Einen passenden SSL Cert Hash heraussuchen
propValues.Add(CertificateFunctions.ConvertCertHashString("61a669c54ab30b5e72209ff88b3c0785df927228"));
w3svc.CommitChanges();

//Wenn noch keine Bindings für SSL gemacht wurden, wird hier ein Standard Binding für SSL angelegt, sonst
//wird das bereits gesetzte Binding verwendet, 
//um keine Änderungen an der bestehenden Basiswebseite zu machen die nicht notwendig sind.
if (w3svc.Properties["SecureBindings"].Count == 0)
{
    propValues = w3svc.Properties["SecureBindings"];
    //Den neuen Ports für SSL - Wenn dieser Eintrag hinzugefügt wird, werden automatisch weitere Einträge vom 
    //System erstellt wie SLLCertStoreName = "MY" muss nicht von uns erstellt werden.
    //Gut zu sehen mit einem Metabase explorer
    propValues.Add("*:443:");
    w3svc.CommitChanges();
}

Wenn man jetzt die Bindings der Default Web Site anschaut, ist dort auch ein Binding für SSL und dem Port 443 angegeben.

Das ganze geht auch noch einfacher, aber nur für den IIS größer Version 7 mit dem ServerManager.