SSL/TLS Fehler in Powershell, bzw. wie man Zertifikatsprobleme unter Windows analysieren kann


Beim Herumspielen mit Chocolatey, dazu ein anderes Mal mehr, gab es Probleme auf einem Windows XP-Rechner. Der Windows XP Rechner war für Chocolatey noch nicht vorbereitet, d. h. er hatte zwar .Net 1, 2 und 3.5 bereits aber noch kein .Net Framework 4. Powershell 2 war bereits installiert.

Ausgangslage
Der Aufruf von

@powershell -NoProfile -ExecutionPolicy unrestricted -Command "iex ((new-object net.webclient).DownloadString(‚https://chocolatey.org/install.ps1‘))" && SET PATH=%PATH%;%systemdrive%\chocolatey\bin

für die Installation, führte zu folgender Fehlermeldung:

Exception calling "DownloadString" with "1" argument(s): "The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel."
At line:1 char:47
+ iex ((new-object net.webclient).DownloadString <<<< (‚https://chocolatey.org/
install.ps1′))
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

Der entscheidende Punkt ist oben rot markiert. Aber was macht man jetzt? Die Meldung ist leider doch zu allgemein.

Erster Versuch mehr Infos zum Fehler zu bekommen
Powershell kennt das $error Objekt. In diesem ist immer der letzte Fehler enthalten. Aber gleichzeitig sind bestimmte Randbedingungen und tiefergehende Infos zum Fehler abrufbar. Wichtig: Wenn man beim Rumspielen einen neuen Fehler verursacht, dann gehts schnell durcheinander und man untersucht nicht den eigentlichen Fehler, sondern einen nachfolgenden! Also geht man zuerst her und sichert den Fehler:

$myE = $error[0]

Damit kann nun nichts schiefgehen und mittels verschiedenen Abfragen kommt man dann irgendwann dazu:

((($myE).Exception).InnerException).Innerexception
The remote certificate is invalid according to the validation procedure.

Aha, wir haben also ein Zertifikatsproblem.

Prüfung mittels Browser
Aber welches Zertifikat macht nun die Probleme? Wie kann man dies überprüfen? Ein einfacher Versuch ist mittels Browser, in diesem gibt man einfach im aktuellen Fall https://chocolatey.org ein und schaut was passiert. Wer jetzt Chrome benutzt, der ist fein raus und bleibt verdutzt zurück, wer es aber mit dem Internet Explorer probiert, welcher den gleichen Weg einschlägt wie eben Powershell, bekommt diese Meldung:

Es besteht ein Problem mit dem Sicherheitszertifikat der Website.

Das Sicherheitszertifikat dieser Website wurde für eine andere Adresse der Website ausgestellt.

Die Sicherheitszertifikatprobleme deuten eventuell auf den Versuch hin, Sie auszutricksen bzw. Daten die Sie an den Server gesendet haben abzufangen.

Es wird empfohlen, dass Sie die Webseite schließen und nicht zu dieser Website wechseln.

Klicken Sie hier, um diese Webseite zu schließen.

Laden dieser Website fortsetzen (nicht empfohlen).

Weitere Informationen

Ja das kennen wir schon. Wenn man aber “Laden dieser Website fortsetzen (nicht empfohlen)” anklickt, bekommt man den bekannten roten Balken des IE mit dem Zertifikatsfehler. Dort kann man nun auf Zertifikatsfehler klicken und sich die Sache etwas genauer anschauen. Blöd nur das, dass es nicht offensichtlich ist, wo das Problem wieder genau liegt. Auf jeden Fall bekommt man raus, dass es sich in diesem Fall um Geotrust-Zertfikate handelt. Übrigens bekommt man diese Info auch von Chrome geliefert, wenn man auf das Schloss in der Adresszeile klickt und das Register Verbindung auswählt. Aber Hallo! Hier wird ein Go Daddy Zertifikat für chocolatey.org genannt! Ja was denn nun? GeoTrust oder Go Daddy? Des Rätsel Lösung nennt sich SNI und wird am Ende aufgelöst.

Tiefergehende Suche mittels Powershell
Wie kann man die Geschichte aber mittels Powershell besser in Griff bekommen? Da zu unterst immer irgendwo eine TCP-Verbindung aufgebaut werden muss, kann nur der direkte Aufruf über TCPClient etwas bringen.

Die einfachste Variante wäre nun:

$host = "chocolatey.org"
$port = 443
$conn = New-Object System.Net.Sockets.TcpClient ($host, $port)
$stream = New-Object System.Net.Security.SslStream($conn.GetStream())
$result=$stream.AuthenticateAsClient($host)
$conn.Close()

aber hier kommt nur wieder die Fehlermeldung wie oben bei der doppelten InnerException. Also was tun?

Der Konstruktor von SslStream kennt noch weitere Parameter. Unter anderem den RemoteCertificateValidationCallback Delegaten. http://msdn.microsoft.com/en-us/library/system.net.security.remotecertificatevalidationcallback(v=vs.110).aspx. Dieser wird üblicherweise benutzt, um bestimmte Fehlermeldungen in Verbindung mit Zertifikaten zu unterdrücken. Ab Powershell 2.0 ist dies ohne weiteres möglich, wie dieser Artikel hier schön beschreibt: http://www.nivot.org/blog/post/2009/07/18/PowerShell20RCWorking
WithNETCallbacksPart1Synchronous
. Der Callback kennt aber alle relevanten Informationen, so dass man diesen nun so nutzen kann:

$conn = New-Object System.Net.Sockets.TcpClient($host,$port)
$stream = New-Object System.Net.Security.SslStream($conn.GetStream(), $null, {
                    Write-Host "Sender: $($args[0])";
                    Write-Host "Certificate: $(($args[1]).gettype())";
                    Write-Host "CertificateChain: $($args[2])";
                    Write-Host "PolicyErrors: $($args[3])"; 
         })
$result = $stream.AuthenticateAsClient($host)
$conn.Close()

nun bekommt man als Ergebnis dies geliefert:

Sender: System.Net.Security.SslStream
Certificate: System.Security.Cryptography.X509Certificates.X509Certificate2
CertificateChain: System.Security.Cryptography.X509Certificates.X509Chain
PolicyErrors: RemoteCertificateNameMismatch

Also genau was auf MSDN als Parameter dokumentiert steht. Vor allem interessant ist nun die Aussage mit RemoteCertificateNameMismatch. Jetzt wird die Sache schon etwas klarer. Sollte chocolatey.org nicht im Zertifikat genannt sein?

Hier nun das Script welches die richtigen Infos ausgibt:

$conn = New-Object system.net.sockets.tcpclient($host, $port)
$stream = New-Object system.net.security.sslstream($conn.getstream(), $null, {
                    Write-Host $args[2].ChainElements[0].Certificate.Subject;
                    Write-Host "PolicyErrors: $($args[3])";
         })
$result = $stream.authenticateasclient("chocolatey.org")
$conn.Close()

Als Ausgabe erhält man:

CN=*.apphb.com, O=AppHarbor Inc, L=San Francisco, S=California, C=US, SERIALNUMBER=MyGg//QgdanmdPPqqfNR5JjsvbrKA/uJ
PolicyErrors: RemoteCertificateNameMismatch

Aha, nun wird es klarer. Wir wollen auf die Seite chocolatey.org bekommen aber ein Zertifikat vom Server für AppHarbor. Findet hier eine Attacke statt? Oh Gott, alle Schotten dicht!

Zusammenfassung
Ich wollte nur mal schnell Chocolatey installieren und bin dann wegen den Sicherheitsanforderungen von Chocolatey bei einer Attacke gelandet, wo mir irgendjemand falsche Zertifikate unterjubeln möchte. Oder ist doch alles ganz anders?

Aufklärung
Die Lösung des Dilemmas lautet SNI “Server Name Indication”. Ein schöner Artikel dazu findet man auf der deutschen Wikipedia unter http://de.wikipedia.org/wiki/Server_Name_Indication. Dort wird im Zusammenhang von SNI darauf hingewiesen, dass Windows XP kein Server Name Indication unterstützt. Warum ist dies wichtig? Weil Powershell mittels der Systembibliotheken von Windows XP seine HTTPS Verbindung aufbauen möchte und ebenso wie der IE8 Probleme bekommt. Hintergrund dafür ist, dass chocolatey.org bei HTTPS-Verbindungen auf SNI angewiesen ist, wie dieser Test zeigt: https://www.ssllabs.com/ssltest/analyze.html?d=chocolatey.org.

Dort ist dann zu lesen:

This site works only in browsers with SNI support.

Dies erklärt nun auch die unterschiedlichen Zertifikate. Denn IE8 auf Windows XP kann kein SNI, Chrome jedoch kann es, obwohl Windows XP zum Einsatz kommt. Der Grund dafür ist, dass Chrome seine eigene Crypto-Library mitbringt.

Beim Versuch also die Verbindung ohne SNI aufzubauen, landet man nicht auf dem gewünschten Server sondern bekommt ein allgemeineres Zertifikat des Hosters zurück. Dies erklärt nun den Punkt mit den unterschiedlichen Zertifikaten von GeoTrust und Go Daddy.

Man kann die Sache sehr schön nachverfolgen, wenn man Fiddler einsetzt. http://fiddler2.com/. Damit kann man, wenn der HTTPS-Mitschnitt aktiviert ist, sehr schön sehen, dass Powershell 2.0 diesen Aufruf generiert:

Version: 3.1 (TLS/1.0)

Extensions:
renegotiation_info 00
Ciphers:
[0004] SSL_RSA_WITH_RC4_128_MD5
[0005] SSL_RSA_WITH_RC4_128_SHA
[000A] SSL_RSA_WITH_3DES_EDE_SHA
[0009] SSL_RSA_WITH_DES_SHA
[0064] TLS_RSA_EXPORT1024_WITH_RC4_56_SHA
[0062] TLS_RSA_EXPORT1024_WITH_DES_SHA
[0003] SSL_RSA_EXPORT_WITH_RC4_40_MD5
[0006] SSL_RSA_EXPORT_WITH_RC2_40_MD5
[0013] SSL_DHE_DSS_WITH_3DES_EDE_SHA
[0012] SSL_DHE_DSS_WITH_DES_SHA
[0063] TLS_DHE_DSS_EXPORT1024_WITH_DES_SHA

Compression:
[00] NO_COMPRESSION

Während Chrome dies zustande bringt:

Version: 3.1 (TLS/1.0)

Extensions:
 server_name chocolatey.org
renegotiation_info 00
elliptic_curves secp256r1 [0x17], secp384r1 [0x18], secp521r1 [0x19]
ec_point_formats uncompressed [0x0]
SessionTicket TLS empty
NextProtocolNegotiation empty
ALPN  spdy/2; spdy/3; spdy/3.1; http/1.1;
channel_id empty
status_request 01 00 00 00 00
Ciphers:
[C02F] TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
[009E] TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
[009C] TLS_RSA_WITH_AES_128_GCM_SHA256
[C014] TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA
[0039] TLS_DHE_RSA_WITH_AES_256_SHA
[0035] TLS_RSA_AES_256_SHA
[C011] TLS_ECDHE_RSA_WITH_RC4_128_SHA
[C013] TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA
[0033] TLS_DHE_RSA_WITH_AES_128_SHA
[0032] TLS_DHE_DSS_WITH_AES_128_SHA
[0005] SSL_RSA_WITH_RC4_128_SHA
[0004] SSL_RSA_WITH_RC4_128_MD5
[002F] TLS_RSA_AES_128_SHA
[000A] SSL_RSA_WITH_3DES_EDE_SHA

Compression:
[00] NO_COMPRESSION

Der Extensions-Eintrag server_name chocolatey.org ist genau der Punkt, welcher beim Powershell-Aufruf fehlt und die Sache zum Scheitern bringt. Gleichwohl auch Chrome innerhalb von Fiddler Probleme mit dem Aufruf bekommt, denn Fiddler2 basiert auf .Net Framework 2.0 und damit ist der weitere Fortgang zum Scheitern verurteilt und man bekommt am Ende nicht das Go Daddy Zertifikat sondern wieder das falsche GeoTrust Zertifikat.

Übrigens geht genau dieser Artikel auf das aktuelle Problem ein und bestätigt nochmal alles: https://groups.google.com/forum/#!searchin/chocolatey/geotrust$20/chocolatey/r9EdhJdPjWM/FavaGm07VtoJ

Zu guter Letzt noch die Bestätigung, dass Powershell 4.0 unter Windows 8.1 problemlos damit umgehen kann:

A SSLv3-compatible ClientHello handshake was found. Fiddler extracted the parameters below.

Version: 3.1 (TLS/1.0)

Extensions:
renegotiation_info 00
 server_name chocolatey.org
elliptic_curves secp256r1 [0x17], secp384r1 [0x18]
ec_point_formats uncompressed [0x0]
SessionTicket TLS empty
Ciphers:
[002F] TLS_RSA_AES_128_SHA
[0035] TLS_RSA_AES_256_SHA
[0005] SSL_RSA_WITH_RC4_128_SHA
[000A] SSL_RSA_WITH_3DES_EDE_SHA
[C013] TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA
[C014] TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA
[C009] TLS1_CK_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
[C00A] TLS1_CK_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
[0032] TLS_DHE_DSS_WITH_AES_128_SHA
[0038] TLS_DHE_DSS_WITH_AES_256_SHA
[0013] SSL_DHE_DSS_WITH_3DES_EDE_SHA
[0004] SSL_RSA_WITH_RC4_128_MD5

Compression:
[00] NO_COMPRESSION

Also alles halb so wild, wenn man weiß, wie es zusammenhängt!

5 Antworten to “SSL/TLS Fehler in Powershell, bzw. wie man Zertifikatsprobleme unter Windows analysieren kann”

  1. Quirel Says:

    Als Ergänzung um CryptoApi2-Logging unter Windows >Vista nutzen zu können: http://technet.microsoft.com/en-us/library/cc749296(v=ws.10).aspx

  2. Quirel Says:

    Hier ein wichtiges Update zu Microsoft TLS 1.2 und PFS: http://support.microsoft.com/kb/2929781/en-us. Es wird auch die neue Defaultreihenfolge der Ciphersuits aufgeführt, aus der man wählen kann.

    Dieses Update wird mit dem berühmten Windows 8.1 Update-Update bzw. beim Server 2012 R2 Update eingespielt.

  3. Bei Powershell SSL/TLS-Zertifikate-Prüfung einfach ignorieren | Das nie endende Chaos! Says:

    […] Weitere Infos zu Powershell und SSL/TLS-Zertifikate und wie man bei Problemen weitere Infos erhält, findet man hier: https://newyear2006.wordpress.com/2014/01/04/ssltls-fehler-in-powershell-bzw-wie-man-zertifikatsprobl&#8230; […]

  4. Probleme mit Transportverschlüsselung, Zertifikaten oder Passwörtern bei SMTP-Servern mittels Powershell überprüfen | Das nie endende Chaos! Says:

    […] Falls beim TLS-Handshake etwas schief gehen sollte, dann hat dies meist mit den Zertifikaten zu tun. Wie man diesem Problem auf den Grund gehen kann, habe ich in einem früheren Blogartikel bereits beschrieben: https://newyear2006.wordpress.com/2014/01/04/ssltls-fehler-in-powershell-bzw-wie-man-zertifikatsprob&#8230;. […]

  5. Quirel Says:

    und noch was zu SNI, falls man Clients unterstützen muss, welche kein SNI sprechen: http://blogs.technet.com/b/applicationproxyblog/archive/2014/06/19/how-to-support-non-sni-capable-clients-with-web-application-proxy-and-ad-fs-2012-r2.aspx

Schreibe einen Kommentar

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s


%d Bloggern gefällt das: