如何验证HTTP重定向绑定的SAML签名

时间:2017-01-04 14:47:58

标签: xml saml-2.0 xml-signature itfoxtec-identity-saml2

我通过HTTP-redirect绑定收到SAML请求SAML请求的内容如下所示

  

{ “SigAlg”=> “中http://www.w3.org/2000/09/xmldsig#rsa-sha1”,   “SAMLRequest”=> “中lVLLaoQwFP0VyT5jEqPG4AiFoSDMtNApXXQzxDxaQRObRDqfX3XoolAKXd7DPQ / uuXUQ4zDxo3tzc3zSH7MOMWkPe3DpcixzVVVQl4RBqoiCncEYEmkoY7k00hCQvGgfemf3gOwQSNoQZt3aEIWNC4RwCRGGiD6jkmPMs2KHUPYKksPi0lsRN + Z7jFPgafqpvejtbtQpSK7jYAPfsu3B7C13IvSBWzHqwKPk57vTkS + WfPIuOukG0NSbub9R / yaJELRfzUGzrhmtFut15qdeeheciY926K2u05toUz8sIu0huXd + FPFv9RXpFTTbKp / WA4WobQT / jEYrykwhNaQ66yDNMwY7wijEtMCmysqqo6xOb8Ga + tbjWYe1jtYqfW0uCucoYwWCHS3F0kRGoajWTpAiiJRZJRmu01 + Y3 + CPt2i + AA ==”}

它还具有签名值

  

WkDaGzC6vPTlzh + EnFA5 / 8IMmV7LviyRh2DA5EHF0K0nl + xzBlKfNCYRnunpwoEvGhereGdI5xBpv + mc9IguiCaLZSZjDh6lIDdpvctCnmSNzORqzWQwQGeZ9vjgtCLjUn35VZLNs3WgEqbi2cL + ObrUDS2gV1XvBA3Q3RRhoDmi + XE89Ztnd1cNpR3XdA + EL2ENbMI2XAD9qSgMufUJY / 3GBBpT7Vg1ODtPxBudq + sXrgPh / + + WtUUitLkkfC8tdRTCS1EZPv h27I5g / VNza23Xl8w2HdAuYP0F2FjREo8VV2aUtaOUd / jAF9 + bfkGV93y1PzFttLxdBbFoxp6qBg ==

但我不明白如何验证此签名是否正确。

关于SAML绑定的第3.4.4.1节https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf

To construct the signature, a string consisting of the concatenation of the RelayState (if present),
SigAlg, and SAMLRequest (or SAMLResponse) query string parameters (each one URLencoded)
is constructed in one of the following ways (ordered as below):
SAMLRequest=value&RelayState=value&SigAlg=value
SAMLResponse=value&RelayState=value&SigAlg=value

我试过这个方法,但是

  • 我使用私钥生成的签名与我从SP收到的签名不匹配。 (上面发布)

  • 此外,我无法使用私钥解密签名的邮件(我假设签名是使用我与之联合的公共创建的。)

<samlp:LogoutRequest ID="_36167d94-d868-4c04-aee3-8bbd4ed91317" Version="2.0" IssueInstant="2017-01-05T16:21:55.704Z" Destination="https://werain.me/" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"><Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">urn:federation:MicrosoftOnline</Issuer><NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" xmlns="urn:oasis:names:tc:SAML:2.0:assertion">4948f6ce-4e3b-4538-b284-1461f9379b48</NameID><samlp:SessionIndex>_eafbb730-b590-0134-a918-00d202739c81</samlp:SessionIndex></samlp:LogoutRequest>

这里有任何帮助。

6 个答案:

答案 0 :(得分:7)

SAML身份验证消息是带有嵌入式(封装)XMLDSig签名或缩减编码签名的XML文档

已开发的XMLDSign签名

<samlp:LogoutRequest>
    <...saml message...> 
    <ds:Signature>
         <ds:SignedInfo />
         <ds:SignatureValue /> 
         <ds:KeyInfo /> 
    </ds:Signature> 
</samlp:LogoutRequest>

<ds:SignatureValue>包含签名,<ds:SignedInfo>签名数据和对邮件的引用,<ds:KeyInfo>通常包含带有签名者身份的X509Certificate,或者对该证书的引用< / p>

网址中的违规编码

SAMLRequest=value&RelayState=value&SigAlg=value&Signature=value

每个值都是url编码的

SAMLRequest=urlencode(base64(<samlp:LogoutRequest> <...saml message...> </samlp:LogoutRequest>))

签名是使用算法SigAlg

在查询字符串算法的串联上完成的
Signature = urlencode( base64 ( SigAlg ("SAMLRequest=value&RelayState=value&SigAlg=value")))

SAML消息的数字签名

SAML邮件使用颁发者(SP)的私钥进行数字签名(未加密),并且可以使用SP的公钥进行验证。必须使用身份提供商(IdP)的私钥对SAML响应进行签名,并且SP可以使用IdP的公钥验证消息。

如果您充当IdP并且想要验证SP的SAML请求,则需要:

  • 验证数字签名:使用SP的公钥验证签名与签名邮件的匹配,以确保签名者的身份并且邮件未被更改

  • 授权请求:验证签名者的身份是否可以执行请求的操作。通常,您必须将序列号或证书主题与预先存在的列表进行匹配,或者验证证书是否由受信任的证书颁发机构颁发

  • 生成SAML响应:使用SAML数据生成XML消息,并使用您的私钥对其进行签名以发送给SP

大多数编程语言都支持XMLDsig签名,但在您的情况下使用 deflated encodin g这是SAML绑定的特定特征,因此如果您的SAML库不支持它,你有手动验证签名。根据{{​​3}}

,这些或多或少是要遵循的步骤
 //get params from query string 
String samlrequest = getQueryParam("SAMLRequest");
String relaystate = getQueryParam("RelayState");
String sigalg = getQueryParam("SigAlg");
String signature = getQueryParam("Signature");


//The signature
byte signature[] = URLDecoder.decode(Base64.getDecoder().decode(signature ), "UTF-8");

//The signed data. build the following string checking if RelayState is null
//SAMLRequest=samlrequest&RelayState=relaystate&SigAlg=sigalg
byte signedData[] = concat(samlrequest,relaystate,sigalg);

//The signature algorithm could be "SHA1WithRSA" or "SHA1withDSA" depending on sigalg is http://www.w3.org/2000/09/xmldsig#rsa-sha1 or http://www.w3.org/2000/09/xmldsig#dsa-sha1 
String signatureAlgorithm = extractSignatureAlgorithm(sigalg);

//get the public key of the SP. It must be registered before this process
PublicKey publicKey = ...

//Verify the signature
Signature sig = Signature.getInstance(signatureAlgorithm);
sig.initVerify(publicKey);
sig.update(signedData); 
boolean verifies = sig.verify(signature);  

答案 1 :(得分:2)

根据绑定(POST或重定向)对SAML 2.0签名进行不同的验证。如果使用POST绑定,则在SAML XML中验证签名。如果使用了重定向绑定,则查询字符串将使用签名进行验证。

此LogoutRequest与重定向绑定一起发送。 以下C#示例代码是从ITfoxtec.Identity.Saml2组件中复制的,并显示了如何验证签名。

var queryString = request.QueryString;
var signatureValue = Convert.FromBase64String(request.Query["Signature"]);

var messageName = "SAMLRequest";
var signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
var signatureValidationCertificate = new X509Certificate2("path-to-service-provider-x509-certificate");

var saml2Sign = new Saml2SignedText(signatureValidationCertificate, signatureAlgorithm);
if (saml2Sign.CheckSignature(Encoding.UTF8.GetBytes(new RawSaml2QueryString(queryString, messageName).SignedQueryString), signatureValue))
{
    // Signature is valid.
}
else
{
    throw new InvalidSignatureException("Signature is invalid.");
}

答案 2 :(得分:1)

我正在尝试使用上述答案,但没有成功。

然后,阅读文档和一点时间,我已成功用Java验证签名,快速答案是:

this

答案 3 :(得分:1)

对于仍然被困的人,这里是完整的方法

public static void verifySignature(boolean isResponse, String samlQueryString, String relayStateString, String sigAlgString, String signature, X509Certificate cert) throws Exception {
    String type = isResponse ? "SAMLResponse" : "SAMLRequest";

    String query = type + "=" + URLEncoder.encode(samlQueryString, "UTF-8");
        query += relayStateString == null ? "" : "&RelayState=" + URLEncoder.encode(relayStateString, "UTF-8");
        query += "&SigAlg=" + URLEncoder.encode(sigAlgString, "UTF-8");

    String javaSigAlgName = null;

    if(sigAlgString.equals("http://www.w3.org/2000/09/xmldsig#rsa-sha1")) {
        javaSigAlgName = "SHA1withRSA";
    } else if(sigAlgString.equals("http://www.w3.org/2000/09/xmldsig#rsa-sha256")) {
        javaSigAlgName = "SHA256withRSA";
    } else {
        throw new Exception("signature: " + sigAlgString + " not supported by SP/IDP");
    }

    byte[] signatureBytes = Base64.getDecoder().decode(signature);

    Signature sig = Signature.getInstance(javaSigAlgName);
    sig.initVerify(cert.getPublicKey());
    sig.update(query.getBytes());

    Boolean valid = sig.verify(signatureBytes);
    System.out.println("is valid: " + valid);
}

答案 4 :(得分:0)

我想在上述答案中补充一点:URL编码/解码是非规范的,这意味着每个框架/语言可能实际上都有不同的处理方式。我花了很多天来验证HTTP重定向绑定,事实证明,我们使用URL的Java Play 1.x框架以不同于SAML框架预期的方式解码内容。

我们解决了此问题,方法是直接从查询字符串中删除查询参数,而不是让Play框架为我们解码(仅需要我们重新编码回去)。因此,如果您的代码与Alexandre的代码匹配,但SAML框架指出签名无效,请确保您将从URL GET参数中直接直接输入的字符串输入算法中。

答案 5 :(得分:0)

我们可以使用one login saml library来验证auth-request签名。它们为SAML提供了很多包装方法。这是它的ruby实现。 `

  Select 
    u.*,
    (
        Select top 1
            COUNT(cDays) as c,
            SUM(un) as un,
            SUM(vcs) as vcs
        From
        (
            Select
                s.DateSales as cDays,
                SUM(s.Un) as Un,
                SUM(s.VCS) as vcs
            From
                dbSales.dbo.Sales s
            Where
                s.IDX = u.IDX And s.DateSales Between DATEADD(dd,-8, u.DateS) And DATEADD(dd,-1, u.DateS)
            Group by
                s.DateSales
        ) as sct
    )

From 
    unikat u
)

`