2012-08-23 12 views
13

korrekt vor Ich muss eine SP-initiierte SAML 2.0-Authentifizierungstransaktion mithilfe der HTTP-Redirect-Binding-Methode erstellen. Es stellt sich heraus, dass dies ziemlich einfach ist. Holen Sie sich einfach den IdP-URI und verketten Sie einen einzelnen Abfragezeichenfolgenparameter SAMLRequest. Der Parameter ist ein codierter XML-Block, der die SAML-Anfrage beschreibt. So weit, ist es gut.Wie bereite ich eine 'HTTP Redirect Binding'-SAML-Anfrage mit C#

Das Problem tritt auf, wenn die SAML in den Abfragezeichenfolgenparameter konvertiert wird. Ich glaube, dass dieser Prozess der Vorbereitung sein sollte:

  1. bauen eine SAML Zeichenfolge
  2. Compress diese Zeichenfolge
  3. Base64 die Zeichenfolge
  4. UrlEncode die Zeichenfolge kodieren.

Die SAML Anfrage

<samlp:AuthnRequest 
    xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" 
    xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" 
    ID="{0}" 
    Version="2.0" 
    AssertionConsumerServiceIndex="0" 
    AttributeConsumingServiceIndex="0"> 
    <saml:Issuer>URN:xx-xx-xx</saml:Issuer> 
    <samlp:NameIDPolicy 
     AllowCreate="true" 
     Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"/> 
</samlp:AuthnRequest> 

Der Kodex

private string GetSAMLHttpRedirectUri(string idpUri) 
{ 
    var saml = string.Format(SAMLRequest, Guid.NewGuid()); 
    var bytes = Encoding.UTF8.GetBytes(saml); 
    using (var output = new MemoryStream()) 
    { 
     using (var zip = new DeflaterOutputStream(output)) 
     { 
      zip.Write(bytes, 0, bytes.Length); 
     } 
     var base64 = Convert.ToBase64String(output.ToArray()); 
     var urlEncode = HttpUtility.UrlEncode(base64); 
     return string.Concat(idpUri, "?SAMLRequest=", urlEncode); 
    } 
} 

Ich vermute, dass die Kompression irgendwie schuld ist. Ich benutze die DeflaterOutputStream Klasse von SharpZipLib, die einen Industriestandard Deflate-Algorithmus implementieren soll, also vielleicht gibt es hier einige Einstellungen, die ich falsch gemacht habe?

Der codierte Ausgang kann mit diesem SAML2.0 Debugger (ein nützliches Online-Konvertierungstool) getestet werden. Wenn ich meine Ausgabe mit diesem Werkzeug decodiere, kommt das als Unsinn heraus.

Die Frage ist daher: Wissen Sie, wie Sie eine SAML-Zeichenfolge in den korrekt deflationierten und codierten SAMLRequest-Abfrageparameter konvertieren?

Danke

EDIT 1

Die unten akzeptierte Antwort, die Antwort auf das Problem gibt. Hier ist der endgültige Code, korrigiert durch alle folgenden Kommentare und Antworten.

Encode SAMLRequest - Arbeits-Code

private string GenerateSAMLRequestParam() 
{ 
    var saml = string.Format(SAMLRequest, Guid.NewGuid()); 
    var bytes = Encoding.UTF8.GetBytes(saml); 
    using (var output = new MemoryStream()) 
    { 
     using (var zip = new DeflateStream(output, CompressionMode.Compress)) 
     { 
      zip.Write(bytes, 0, bytes.Length); 
     } 
     var base64 = Convert.ToBase64String(output.ToArray()); 
     return HttpUtility.UrlEncode(base64); 
    } 
} 

Die SAMLRequest Variable enthält die SAML an der Spitze dieser Frage gezeigt.

Decode SAMLResponse - Arbeits-Code

private string DecodeSAMLResponse(string response) 
{ 
    var utf8 = Encoding.UTF8; 
    var bytes = utf8.GetBytes(response); 
    using (var output = new MemoryStream()) 
    { 
     using (new DeflateStream(output, CompressionMode.Decompress)) 
     { 
      output.Write(bytes, 0, bytes.Length); 
     } 
     var base64 = utf8.GetString(output.ToArray()); 
     return utf8.GetString(Convert.FromBase64String(base64)); 
    } 
} 
+0

Ich war nicht in der Lage, Ihre "Decode SAMLResponse - Arbeitscode" so zu verwenden, wie es geschrieben ist. Ich musste die Eingabe von der Ausgabe MemoryStream trennen. Ich musste auch UrlDecode und Convert.FromBase64String (...) vor dem DeflateStream. Ich dachte nur, ich würde mir eine Notiz machen, falls es der nächsten Person hilft. –

+0

Können Sie bitte Ihren Arbeitscode posten? – biofractal

+0

Hey Randall, kannst du den Code für die Änderungen angeben, die du an "SAMLResponse - Arbeitscode decodieren" vorgenommen hast? Das wäre sehr zu begrüßen! Danke – Naner

Antwort

11

Ich habe nur den folgenden Code mit Ihrem Beispiel SAML laufen:

 var saml = string.Format(sample, Guid.NewGuid()); 
     var bytes = Encoding.UTF8.GetBytes(saml); 

     string middle; 
     using (var output = new MemoryStream()) 
     { 
      using (var zip = new DeflaterOutputStream(output)) 
       zip.Write(bytes, 0, bytes.Length); 

      middle = Convert.ToBase64String(output.ToArray()); 
     } 

     string decoded; 
     using (var input = new MemoryStream(Convert.FromBase64String(middle))) 
     using (var unzip = new InflaterInputStream(input)) 
     using (var reader = new StreamReader(unzip, Encoding.UTF8)) 
      decoded = reader.ReadToEnd(); 

     bool test = decoded == saml; 

Die Testvariable true ist. Dies bedeutet, dass der zip/base64/unbase64/unzip-roundtrip korrekt ausgeführt wird. Der Fehler muss später auftreten. Vielleicht zerstört der URLEncoder sie? Könnten Sie einen ähnlichen Urlencode/Decode Test ausprobieren? Überprüfen Sie auch, wie lange das Ergebnis ist. Möglicherweise ist die resultierende URL aufgrund ihrer Länge abgeschnitten.

(bearbeiten: Ich habe einen StreamReader anstelle von Arrays hinzugefügt. Früher mein Beispiel verwendet Bytes.Length, um den Puffer vorzubereiten und das könnte den Test beschädigen. Jetzt verwendet das Lesen nur die Informationen aus dem komprimierten Stream)

edit:

 var saml = string.Format(sample, Guid.NewGuid()); 
     var bytes = Encoding.UTF8.GetBytes(saml); 

     string middle; 
     using (var output = new MemoryStream()) 
     { 
      using (var zip = new DeflateStream(output, CompressionMode.Compress)) 
       zip.Write(bytes, 0, bytes.Length); 

      middle = Convert.ToBase64String(output.ToArray()); 
     } 

     // MIDDLE is the thing that should be now UrlEncode'd 

     string decoded; 
     using (var input = new MemoryStream(Convert.FromBase64String(middle))) 
     using (var unzip = new DeflateStream(input, CompressionMode.Decompress)) 
     using (var reader = new StreamReader(unzip, Encoding.UTF8)) 
      decoded = reader.ReadToEnd(); 

     bool test = decoded == saml; 

dieser Code erzeugt eine middle Variable, die einst urlEncoded ist, richtig Debugger durchläuft. DeflateStream stammt aus dem Standard .Net System.IO.Compression Namensraum. Ich habe nicht die geringste Ahnung, warum die Deflate von SharpZip von der 'Debugger'-Seite nicht akzeptiert wird. Es ist unbestreitbar, dass die Komprimierung funktioniert, da es die Daten richtig dekomprimiert. Es muss nur ein Unterschied in den Algorithmen sein, aber ich kann nicht sagen, was der Unterschied zwischen diesem Deflate und dem Deflate ist, d'oh.

+0

Die Debugger-Site, die Sie bereitgestellt haben, zeigt ein Beispiel für eine codierte Nachricht an. Ich habe versucht, es mit Deflate und GZip zu dekomprimieren - und es war nicht möglich. Nicht einmal der GZipStream von .Net schafft es, das zu dekomprimieren. Welchen Komprimierungsalgorithmus benutzt es? – quetzalcoatl

+0

Ich habe einen Compressor gefunden, der mit der Beispielseite funktioniert, und ich habe die Antwort noch einmal aktualisiert. Bitte lesen Sie den letzten Teil erneut :) – quetzalcoatl

+0

Tolle Arbeit. Dein Code hat wunderbar funktioniert. Vielen Dank :-) – biofractal

6

Die Frage an der Spitze enthält einen Abschnitt "Decoding SAMLResponse - Arbeitscode", aber dieser Code schien gebrochen. Nachdem ich ein paar Dinge ausprobiert hatte, entdeckte ich, dass es gleichzeitig versuchte, in denselben Stream zu lesen und zu schreiben. Ich überarbeitet sie durch die Lese Trennung und Bäche schreiben und hier meine Lösung (ich die Anfrage Abschnitt zur Vereinfachung und Klarheit Providing):

Encode SAML-Authentifizierung anfordern:

public static string EncodeSamlAuthnRequest(this string authnRequest) { 
    var bytes = Encoding.UTF8.GetBytes(authnRequest); 
    using (var output = new MemoryStream()) { 
     using (var zip = new DeflateStream(output, CompressionMode.Compress)) { 
     zip.Write(bytes, 0, bytes.Length); 
     } 
     var base64 = Convert.ToBase64String(output.ToArray()); 
     return HttpUtility.UrlEncode(base64); 
    } 
    } 

Decode SAML-Authentifizierung Antwort:

public static string DecodeSamlAuthnRequest(this string encodedAuthnRequest) { 
    var utf8 = Encoding.UTF8; 
    var bytes = Convert.FromBase64String(HttpUtility.UrlDecode(encodedAuthnRequest)); 
    using (var output = new MemoryStream()) { 
    using (var input = new MemoryStream(bytes)) { 
     using (var unzip = new DeflateStream(input, CompressionMode.Decompress)) { 
     unzip.CopyTo(output, bytes.Length); 
     unzip.Close(); 
     } 
     return utf8.GetString(output.ToArray()); 
    } 
    } 
}