Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/xml/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Xml 如何验证HTTP重定向绑定的SAML签名_Xml_Saml 2.0_Xml Signature_Itfoxtec Identity Saml2 - Fatal编程技术网

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

Xml 如何验证HTTP重定向绑定的SAML签名,xml,saml-2.0,xml-signature,itfoxtec-identity-saml2,Xml,Saml 2.0,Xml Signature,Itfoxtec Identity Saml2,我通过HTTP重定向绑定接收SAML请求SAML请求的内容如下所示 {“SigAlg”=>“”, “SAMLRequest”=>"一个中国的QQQQQZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ

我通过HTTP重定向绑定接收SAML请求SAML请求的内容如下所示

{“SigAlg”=>“”, “SAMLRequest”=>"一个中国的QQQQQZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYKWHNAQ66YDNMWY7WijEtMCmysqqo6xOb8Ga+Tbjwye1jtyqfw0ucuCoyWchs3f0krGoaJWtPaijRzJRMU01+Y3+CPt2i+AA==“}

它还有一个签名值

(2)2)2(2)2(2)2(2)2(2)2(2)2(2)2(2)2(2)2(2)2(2)2(2)2(2)2(2)2(2)2)2(2)2)2(2)2)2(2)2(2)2(2)2(2)2)2(2)2)2(2)2)2(2)2)2(2)2)2)2(2)2)2(2)2)2)2(2)2)2(2)2)2)2(2)2)2)2)2(2)2(2)2)2)2)2(2)2)2)2(2)2)2)2)2(2)2)2)2(2)2)2)2)2(2)2)2)2)2)2(2)2)2)2)2)2)2)2)2)2)Y1PZFTTLXDBBFOXP6QBG==

但是我不明白如何验证这个签名是正确的

第3.4.4.1节SAML绑定

我尝试了这个方法,但是失败了

  • 我使用私钥生成的签名与我从SP收到的签名不匹配(如上所述)

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


urn:federation:MicrosoftOnline 4948F6CE-4e3b-4538-b284-1461f9379b48_eafbb730-b590-0134-a918-00d202739c81
SAML身份验证消息是带有嵌入(封装)XMLDSig签名或压缩编码签名的XML文档

信封XMLDSign签名

<samlp:LogoutRequest>
    <...saml message...> 
    <ds:Signature>
         <ds:SignedInfo />
         <ds:SignatureValue /> 
         <ds:KeyInfo /> 
    </ds:Signature> 
</samlp:LogoutRequest>
其中每个值都是url编码的

SAMLRequest=urlencode(base64(<samlp:LogoutRequest> <...saml message...> </samlp:LogoutRequest>))
SAML消息的数字签名

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

如果您担任IdP并希望验证SP的SAML请求,则需要:

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

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

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

大多数编程语言支持XMLDsig签名,但在您的情况下使用的是deflated encoding,这是SAML绑定的一个特定特征,因此如果您的SAML库不支持它,您必须手动验证签名


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

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

final String samlRequest = request.getParameter("SAMLRequest");
final String relayState = request.getParameter("RelayState");
final String sigAlg = request.getParameter("SigAlg");
final String signature = request.getParameter("Signature");

FileInputStream fis = new FileInputStream(new File("path-to-service-provider-x509-certificate"));

CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate cert = cf.generateCertificate(fis);

// ps: java.net.URLEncoder;
String query = "SAMLRequest=" + URLEncoder.encode(samlRequest, "UTF-8");
query += "&RelayState=" +URLEncoder.encode(relayState, "UTF-8");
query += "&SigAlg=" + URLEncoder.encode(sigAlg, "UTF-8");

// ps: org.opensaml.xml.util.Base64
byte[] signatureBytes = Base64.decode(signature);

org.apache.xml.security.Init.init();
Signature sig = Signature.getInstance("SHA1withRSA"); // or other alg (i, e: SHA256WithRSA or others)
sig.initVerify(cert.getPublicKey());
sig.update(query.getBytes());
Boolean valid = sig.verify(signatureBytes);

我想在上面的回答中补充一点:URL编码/解码是不规范的,这意味着每种框架/语言实际上可能有不同的方式来实现它。我花了很多天的时间验证HTTP重定向绑定,结果证明我们使用的Java Play 1.x框架以不同的方式对URL进行解码这并不是SAML框架所期望的


我们解决了这个问题,取而代之的是直接从查询字符串中取出查询参数,而不是让Play framework为我们解码(只让我们需要重新编码)。因此,如果您的代码与Alexandre的匹配,但SAML框架表示签名无效,请确保将直接从URL GET参数中提取的字符串输入到算法中。

SAML 2.0签名的验证方式因绑定(POST或重定向)而异。如果使用POST绑定,则在SAML XML中验证签名。如果使用重定向绑定,则使用签名验证查询字符串

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.");
}
此LogoutRequest通过重定向绑定发送。 下面的C#示例代码是从组件复制的,并显示如何验证签名

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.");
}
  • 从复制的代码

对于那些仍然被卡住的人,这里是完整的方法

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);
}
我们可以使用验证身份验证请求签名。它们为SAML提供了许多包装方法。这是它的ruby实现。 `


`

两件事。首先是一个
LogoutRequest
而不是SAML
AuthRequest
(我想我忘了提到这一点)。第二个绑定是HTTP重定向,而不是HTTP-POST请参见SAML绑定的第3.4.4.1节。如果我错了,也请纠正我,但SAML签名和验证的工作方式是正确的。IDP(有自己的公共/私有对)SP(拥有自己的公钥/私钥对)。如果IDP必须对数据进行签名,则使用自己的私钥对其进行签名。然后,SP可以使用IDP的公钥/证书(通过IDP的元数据共享)对签名数据进行解密。同样,如果SP必须对数据进行签名,则使用自己的私钥对其进行签名,然后IDP使用SP的公钥/证书对其进行解密(可通过SP元数据访问)
AuthRequest
只是一个示例。IdP和SP的签名模型是正确的。只有术语
decrypt
不适用。消息是经过签名的,未加密(内容未隐藏),如果我没有弄错,则使用公钥进行检查的操作是
验证
final String samlRequest = request.getParameter("SAMLRequest");
final String relayState = request.getParameter("RelayState");
final String sigAlg = request.getParameter("SigAlg");
final String signature = request.getParameter("Signature");

FileInputStream fis = new FileInputStream(new File("path-to-service-provider-x509-certificate"));

CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate cert = cf.generateCertificate(fis);

// ps: java.net.URLEncoder;
String query = "SAMLRequest=" + URLEncoder.encode(samlRequest, "UTF-8");
query += "&RelayState=" +URLEncoder.encode(relayState, "UTF-8");
query += "&SigAlg=" + URLEncoder.encode(sigAlg, "UTF-8");

// ps: org.opensaml.xml.util.Base64
byte[] signatureBytes = Base64.decode(signature);

org.apache.xml.security.Init.init();
Signature sig = Signature.getInstance("SHA1withRSA"); // or other alg (i, e: SHA256WithRSA or others)
sig.initVerify(cert.getPublicKey());
sig.update(query.getBytes());
Boolean valid = sig.verify(signatureBytes);
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.");
}
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);
}
def verify_signature(params)
    saml_request = URI.decode(params[:SAMLRequest])
    relay_state_string = URI.decode(params[:RelayState])
    signature = URI.decode(params[:Signature])
    sign_alg = URI.decode(params[:SigAlg])
    query_params,sig_params={},{}
    query_params[:type] = "SAMLRequest"
    query_params[:data] = saml_request
    query_params[:relay_state] = relay_state_string
    query_params[:sig_alg] = sign_alg
    query = OneLogin::RubySaml::Utils.build_query(query_params)
    sig_params[:cert] = getPublicKeyFromCertificate
    sig_params[:sig_alg] = sign_alg
    sig_params[:signature] = signature
    sig_params[:query_string] = query
    OneLogin::RubySaml::Utils.verify_signature(sig_params)
end