How to verify a SAML signature for HTTP-redirect binding

前端 未结 6 1439
生来不讨喜
生来不讨喜 2020-12-30 09:40

I\'m receiving a SAML request via HTTP-redirect binding the content of the SAML request look like this

{\"SigAlg\"=>\"http://www.w3.org/2000/09/xmldsi

相关标签:
6条回答
  • 2020-12-30 10:18

    A SAML authentication message is a XML document with an embedded (enveloped) XMLDSig signature or a deflated encoding signature

    Enveloped XMLDSign signature

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

    <ds:SignatureValue> contains the signature, <ds:SignedInfo> the signed data and a reference to the message and <ds:KeyInfo> usually contains the X509Certificate with the identity of the signer, or a reference to that certicate

    Deflated encoding in URL

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

    Where each value is url encoded

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

    And the signature is done on a concatenation of query string algorithm using the algorithm SigAlg

    Signature = urlencode( base64 ( SigAlg ("SAMLRequest=value&RelayState=value&SigAlg=value")))
    

    Digital signature of SAML messages

    SAML message is digitally signed (not encrypted) with the private key of the issuer (SP), and can be verified with the public key of the SP. A SAML response must be signed with the private key of the identity provider (IdP), and the SP can verify the message with the public key of the IdP.

    If you act as IdP and you want to verify a SAML request of the SP, you need:

    • Verify the digital signature: Verify using the public key of the SP that the signature match with the signed message to ensure the identity of the signer and the message has not been altered

    • Authorize the request: Verify that the identity of the signer can perform the requested operation. Usually you have to match the serial number or the subject of the certificate with a pre-existent list, or verify that the certificate has been issued by a trusted certificate authority

    • Generate the SAML response: Generate a XML message with the SAML data and sign it with your private key to send to SP

    Most programming languages support XMLDsig signatures but in your case is used the deflated encoding that is a specific characteristic of SAML binding, so if your SAML library does not support it, you have to verify the signature manually. These are more or less the steps to follow according to specification

     //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);  
    
    0 讨论(0)
  • 2020-12-30 10:24

    For those still stuck, here is the complete method

    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);
    }
    
    0 讨论(0)
  • 2020-12-30 10:25

    We can use the one login saml library to verify auth-request signature.They provide a lot of wrapper methods for SAML.This is a ruby implementation of it. `

    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
    

    `

    0 讨论(0)
  • 2020-12-30 10:27

    I'am trying using the above answer but don't success.

    Then, read the documentation and a little time, i have sucess to validate signature with Java and the fast answer is:

    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);
    
    0 讨论(0)
  • 2020-12-30 10:34

    One point I would like to add to the above answers: URL encoding/decoding is non-canonical, meaning that every framework/language may in fact have a different way of doing it. I was stuck on verifying an HTTP-Redirect binding for many days, turns out that the Java Play 1.x framework we are using URL decodes things in a different way than the SAML framework expects.

    We resolved this issue by instead taking the query parameters directly out of the query string, rather than letting Play framework decode it for us (only for us to need to re-encode it back). So if your code matches Alexandre's but the SAML framework says the signature is invalid, make sure that you're feeding into the algorithm the strings that are directly taken from the URL GET parameters.

    0 讨论(0)
  • 2020-12-30 10:40

    A SAML 2.0 signature is validated differently depending on the binding (POST or Redirect). If a POST binding is used the signature is validated in the SAML XML. If a Redirect binding is used the query string is validated with the signature.

    This LogoutRequest is send with a redirect binding. The following C# sample code is copied from the ITfoxtec.Identity.Saml2 component and show how to validate the signature.

    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.");
    }
    
    • Code copied from Saml2RedirectBinding
    • RawSaml2QueryString
    • Saml2SignedText
    0 讨论(0)
提交回复
热议问题