20

I am a newbie to SSL certificate thing. so a little cautious about what I've done so far. I am creating an application that uses SSO to authenticate the users using PHP 5.4. What I have: a certificate (.pfx) provided by the party. encrypted SAML in POST variable.

The de-crypted xml is almost similar to SAML: Why is the certificate within the Signature?

I need to verify that the response is from verified provider.. I have come to know while googling around that I need .pem instead of .pfx, So I have converted the .pfx file to .pem using ssl commands. I've used the code from http://www.php.net/manual/es/function.openssl-verify.php#62526. Here is my code.

$encxml=$_POST['SAMLResponse'];
$xml = new SimpleXMLElement(base64_decode($encxml)); 
$signature = ((string)$xml->Signature->SignatureValue);
var_dump($signature);


//do I need to do something with this X509Certificate value embedded in xml??
$cert = ((string)$xml->Signature->KeyInfo->X509Data->X509Certificate);
var_dump($cert);

//Or I need
$fp = fopen("xyz.pem", "r");
$priv_key = fread($fp, 8192);
fclose($fp);
print_r($priv_key);
$ok = openssl_verify($xml, $signature, $priv_key);

So should I ignore the X509Certificate embedded in xml or I need to check it as well... will openssl_verify suffice? and am I on the right path? please any guidance will be appreciated.

3
  • This question is old... But still alive... Any expert opinion will be greatly appreciated.
    – MJ Khan
    Commented May 29, 2014 at 17:45
  • don't try to implement a SAML stack yourself, instead use an existing library/toolkit/product
    – Hans Z.
    Commented Sep 22, 2015 at 13:42
  • 1
    Like simplesamlphp.org
    – tom
    Commented Sep 28, 2015 at 18:03

2 Answers 2

20
+25

An XML signed using xmldsig syntax has 3 important parts:

  1. Signature -> KeyInfo contains information about the public key derived from the private key used to sign the data
  2. Signature -> SignedInfo contains the data which is gonna be signed using the private key mentioned above; the data contains information about how the verification should be computed, such as: CanonicalizationMethod, SignatureMethod, Reference
  3. Signature -> SignatureValue contains the value of the signature generated by signing Signature -> SignedInfo with the private key

Theoretically this is how the code should look for an rsa-sha1 algorithm(specified by Signature -> SignedInfo -> SignatureMethod), having the following canonicalization method: Exclusive XML Canonicalization 1.0 (omits comments), and the x509 certificate provided:

$xmlDoc = new DOMDocument();
$xmlDoc->loadXML($xmlString);

$xpath = new DOMXPath($xmlDoc);
$xpath->registerNamespace('secdsig', 'http://www.w3.org/2000/09/xmldsig#');

// fetch Signature node from XML
$query = ".//secdsig:Signature";
$nodeset = $xpath->query($query, $xmlDoc);
$signatureNode = $nodeset->item(0);

// fetch SignedInfo node from XML
$query = "./secdsig:SignedInfo";
$nodeset = $xpath->query($query, $signatureNode);
$signedInfoNode = $nodeset->item(0);

// canonicalize SignedInfo using the method descried in
// ./secdsig:SignedInfo/secdsig:CanonicalizationMethod/@Algorithm
$signedInfoNodeCanonicalized = $signedInfoNode->C14N(true, false);

// fetch the x509 certificate from XML
$query = 'string(./secdsig:KeyInfo/secdsig:X509Data/secdsig:X509Certificate)';
$x509cert = $xpath->evaluate($query, $signatureNode);
// we have to re-wrap the certificate from XML to respect the PEM standard
$x509cert = "-----BEGIN CERTIFICATE-----\n"
    . $x509cert . "\n"
    . "-----END CERTIFICATE-----";
// fetch public key from x509 certificate
$publicKey = openssl_get_publickey($x509cert);

// fetch the signature from XML
$query = 'string(./secdsig:SignatureValue)';
$signature = base64_decode($xpath->evaluate($query, $signatureNode));

// verify the signature
$ok = openssl_verify($signedInfoNodeCanonicalized, $signature, $publicKey);    

This lib does a good job at implementing xmldsig in php: xmlseclibs; an example of how to verify an xmldsig can be found here: https://github.com/robrichards/xmlseclibs/blob/master/tests/xmlsec-verify.phpt. This library also validates the digest value from Signature -> SignedInfo -> Reference, a step which I omitted above.

3
  • 2
    At last an answer :) .. but I am no more working on this project .. but it may be helpful to others.. I am not sure If any one else can mark this as answer, if it works.
    – MJ Khan
    Commented Sep 27, 2015 at 15:16
  • 3
    This code worked for me -- but you will likely need to set the 4th parameter of openssl_verify() to match the encryption method defined in the SignatureMethod algorithm tag within the SAML response document.
    – RLI123
    Commented Apr 23, 2016 at 3:22
  • $query = './secdsig:SignatureMethod'; $signatureMethod = $xpath->evaluate($query, $signedInfoNode)->item(0)->getAttribute('Algorithm'); $spos = strpos($signatureMethod, '#'); if (!empty($signatureMethod) && $spos !== false) { $algo = strtoupper( substr($signatureMethod, $spos + 1) ); } else { $algo = 'RSA-SHA1'; // fallback? } var_dump($algo); Commented May 18, 2018 at 12:10
1

I would suggest you use https://github.com/lightSAML/lightSAML. It's using xmlseclibs and implements full SAML SSO SP profile. Simple receiving of SAML response from the HTTP POST and verifying its signature samples are given in LightSAML cookbook http://www.lightsaml.com/LightSAML-Core/Cookbook/How-to-receive-SAML-message/ and http://www.lightsaml.com/LightSAML-Core/Cookbook/How-to-verify-signature-of-SAML-message/ and the whole code would look like this

$request = \Symfony\Component\HttpFoundation\Request::createFromGlobals();

$bindingFactory = new \LightSaml\Binding\BindingFactory();
$binding = $bindingFactory->getBindingByRequest($request);

$messageContext = new \LightSaml\Context\Profile\MessageContext();
/** @var \LightSaml\Model\Protocol\Response $response */
$response = $binding->receive($request, $messageContext);

$key = \LightSaml\Credential\KeyHelper::createPublicKey(
    \LightSaml\Credential\X509Certificate::fromFile(__DIR__.'/../web/sp/saml.crt')
);

/** @var \LightSaml\Model\XmlDSig\SignatureXmlReader $signatureReader */
$signatureReader = $authnRequest->getSignature();

try {
    $ok = $signatureReader->validate($key);

    if ($ok) {
        print "Signaure OK\n";
    } else {
        print "Signature not validated";
    }
} catch (\Exception $ex) {
    print "Signature validation failed\n";
}

Handling of the Response by full SAML SSO profile specificaction is a bit more than that, for those details, you can check the sample in https://github.com/lightSAML/lightSAML/blob/master/web/sp/acs.php or if you're using Symfony https://github.com/lightSAML/SpBundle

Not the answer you're looking for? Browse other questions tagged or ask your own question.