如何使用C#正确准备“HTTP重定向绑定”SAML请求
我需要使用HTTP重定向绑定方法创建SP启动的SAML 2.0身份validation事务。 事实certificate这很容易。 只需获取IdP URI并连接单个查询字符串参数SAMLRequest
。 param是xml的编码块,用于描述SAML请求。 到现在为止还挺好。
将SAML转换为查询字符串参数时出现问题。 我相信这个准备过程应该是:
- 构建SAML字符串
- 压缩此字符串
- Base64编码字符串
- 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()); } } }