0
votes

I need your help figuring out what I'm doing wrong in terms of validating the hashes. I put the code in a Gist so that it can be viewed without having to do a lot of scrolling.

An example token like this is sent here:

https://gist.github.com/NoMan2000/3d3044e8d653a1d580ac#file-saml-xml

I'm trying to check two fields, the DigestValue and the SignatureValue. As I understand it, the DigestValue is computed by looking at the transforms that are applied and applying a hashing algorithm specified in the DigestMethod.

That hash is a base64 encoded version of the hashing algorithm from raw binary output.

The transforms say "Remove everything in the signature block, convert the string using C14N to canonicalize the output, and calculate the hash based upon the id given in the Reference URI": I'm basing that on what I read here:

http://www.w3.org/TR/xmldsig-core/#sec-TransformAlg

Yet grabbing the algorithm, using the hash, etc. does not work. I always fail that check.

https://gist.github.com/NoMan2000/3d3044e8d653a1d580ac#file-samlcontroller-php-L296

Likewise whenever I try to validate the SignatureValue, my impression is that the SignatureValue is the DigestValue encrypted by the public key using the SSL Algorithm specified in SignatureMethod Algorithm.

So this line of code should work:

$verify = openssl_verify($digestValue, base64_decode($signatureValue), $pubkeyid, $algo);

But verify always returns a 0. As I read the documentation, it's the string by the binary version of the hashing code, plus public key and algorithm.

I've tried a few dozen variants of the code as displayed and never managed to get anywhere with it. Anyone see what I'm doing wrong here?

2

2 Answers

1
votes

You can try using LightSAML. Here's an example of signature validation http://www.lightsaml.com/LightSAML-Core/Cookbook/How-to-verify-signature-of-SAML-message/.

Once you deserialize the SAML Response, the signature validation itself can be done like this

// deserialize from XML ...
$response = new \LightSaml\Model\Protocol\Response();

// load IDP certificate
$key = \LightSaml\Credential\KeyHelper::createPublicKey(
    \LightSaml\Credential\X509Certificate::fromFile('idp.crt')
);

// validate the signature, will throw an Exception if invalid
$response->getSignature()->validate($key);
0
votes

I went with Milos Tomic's answer, but used OneLogin. Code is pretty much the same:

$pubCert = '/path/to/crt/';
$doc = new DOMDocument();
$doc->loadXML($SAMLResponse); // What the server sends over.
$pubkeyid = openssl_x509_read("file://$pubCert");
$validate = OneLogin_Saml2_Utils::validateSign($doc, $pubkeyid);