如何正确准备的“HTTP重定向绑定”使用C#SAML请求(How do I correctly pr

2019-06-27 07:31发布

我需要创建一个SP使用HTTP重定向绑定方法来发起SAML 2.0身份验证交易。 原来,这是很容易的。 刚刚拿到的IdP URI和串联单查询字符串PARAM SAMLRequest 。 该参数是XML的编码块,描述SAML请求。 到现在为止还挺好。

转换成SAML查询字符串PARAM时,问题就来了。 我相信,准备的过程应该是:

  1. 建立一个SAML字符串
  2. 压缩此字符串
  3. Base64编码字符串
  4. URLEncode的字符串。

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

我怀疑压缩在某种程度上难辞其咎。 我现在用的是DeflaterOutputStream从类SharpZipLib这是应该这样也许有一些设置,在这里我有错实施的行业标准放气的算法?

编码的输出可以使用该被测试SAML2.0调试器 (它的一个有用的在线转换工具)。 当我解码使用这个工具我的输出它出来无稽之谈。

因此,问题是:你知不知道如何将SAML字符串转换成正确的放气和编码SAMLRequest查询PARAM?

谢谢

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

Answer 1:

我刚刚运行你的榜样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 。 这意味着,拉链/ BASE64 / unbase64 /解压缩正确执行往返。 以后一定会出现错误。 也许URLEncoder的破坏呢? 你可以尝试类似用urlencode /解码测试? 此外,检查结果是多久。 有可能的是,所得的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的放气不会被“调试”现场接受了丝毫的想法。 不可否认的是,压缩工作,因为它设法妥善解压缩数据..它只是要在算法差一些,但我不能告诉是什么这个放气和放气的区别,德哦。



Answer 2:

在上面的问题中包含一个“解码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());
    }
  }
}


文章来源: How do I correctly prepare an 'HTTP Redirect Binding' SAML Request using C#