我需要使用HTTP重定向绑定方法创建SP启动的SAML 2.0身份验证事务。事实证明这很容易。只需获取IdP URI并连接单个查询字符串参数SAMLRequest
。 param是xml的编码块,用于描述SAML请求。到目前为止一切都很好。
将SAML转换为查询字符串参数时出现问题。我相信这个准备过程应该是:
SAML请求
<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>
守则
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 Debugger(一种有用的在线转换工具)测试编码输出。当我使用这个工具解码我的输出时,它就是废话。
因此,问题是:您是否知道如何将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));
}
}
答案 0 :(得分:13)
我刚刚使用您的示例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。
答案 1 :(得分:9)
顶部的问题包含“解码SAMLResponse - 工作代码”部分,但该代码似乎已损坏。在尝试了一些事情之后,我发现它正在尝试同时读取和写入同一个流。我通过分离读取和写入流来重新设计它,这是我的解决方案(为了方便和清晰,我提供了请求部分):
对SAML身份验证请求进行编码:
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身份验证响应:
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());
}
}
}