如何使用C#正确准备“HTTP重定向绑定”SAML请求

我需要使用HTTP重定向绑定方法创建SP启动的SAML 2.0身份validation事务。 事实certificate这很容易。 只需获取IdP URI并连接单个查询字符串参数SAMLRequest 。 param是xml的编码块,用于描述SAML请求。 到现在为止还挺好。

将SAML转换为查询字符串参数时出现问题。 我相信这个准备过程应该是:

  1. 构建SAML字符串
  2. 压缩此字符串
  3. Base64编码字符串
  4. UrlEncode字符串。

SAML请求

  URN:xx-xx-xx   

代码

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

我怀疑压缩是某种原因。 我正在使用SharpZipLib的DeflaterOutputStream类,它应该实现一个行业标准的deflate算法,所以也许这里有一些设置我错了?

可以使用此SAML2.0调试器 (其有用的在线转换工具)测试编码输出。 当我使用这个工具解码我的输出时,它是无意义的。

因此,问题是:您是否知道如何将SAML字符串转换为正确放气和编码的SAMLRequest查询参数?

谢谢

编辑1

下面接受的答案给出了问题的答案。 以下是所有后续评论和答案所纠正的最终代码。

编码SAMLRequest – 工作代码

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

SAMLRequest变量包含此问题顶部显示的SAML。

解码SAMLResponse – 工作代码

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

我刚刚使用您的示例SAML运行以下代码:

  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; 

测试变量是true 。 这意味着zip / base64 / unbase64 / unzip往返正确执行。 错误必须在以后发生。 也许URLEncoder会毁掉它们? 你能尝试类似的urlencode / decode测试吗? 另外,检查结果的持续时间。 由于其长度,结果URL可能会被截断。

(编辑:我添加了一个StreamReader而不是读取数组。之前我的示例使用bytes.Length来准备缓冲区,这可能会损坏测试。现在读取只使用压缩流中的信息)

编辑:

  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; 

此代码生成一个middle变量,一次是UrlEncoded,正确地通过调试器。 DeflateStream来自标准的.Net的System.IO.Compression命名空间。 我没有任何想法为什么SharpZip的Deflate不被’调试器’站点接受。 不可否认的是,压缩工作正常,因为它设法正确地解压缩数据。它只是必须在算法中有所不同,但我不知道这个deflate和deflate之间有什么区别,d’oh。

顶部的问题包含“解码SAMLResponse – 工作代码”部分,但该代码似乎已损坏。 在尝试了一些事情之后,我发现它试图同时读取和写入同一个流。 我通过分离读取和写入流来重新设计它,这是我的解决方案(为了方便和清晰,我提供了请求部分):

编码SAML身份validation请求:

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

解码SAML身份validation响应:

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