.
*/
include_once 'xmlseclibs.php';
use \RobRichards\XMLSecLibs\MoXMLSecurityKey;
use \RobRichards\XMLSecLibs\MoXMLSecurityDSig;
use \RobRichards\XMLSecLibs\MoXMLSecEnc;
class Utilities {
public static function generateID() {
return '_' . self::stringToHex(self::generateRandomBytes(21));
}
public static function stringToHex($bytes) {
$ret = '';
for($i = 0; $i < strlen($bytes); $i++) {
$ret .= sprintf('%02x', ord($bytes[$i]));
}
return $ret;
}
public static function generateRandomBytes($length, $fallback = TRUE) {
return openssl_random_pseudo_bytes($length);
}
public static function createAuthnRequest($acsUrl, $issuer, $force_authn = 'false') {
$requestXmlStr = '' .
'
";
}
$ret = array(
'Signature' => $objXMLSecDSig,
'Certificates' => $certificates,
);
//echo "Signature validated";
return $ret;
}
public static function validateSignature(array $info, MoXMLSecurityKey $key)
{
/** @var MoXMLSecurityDSig $objXMLSecDSig */
$objXMLSecDSig = $info['Signature'];
$sigMethod = self::xpQuery($objXMLSecDSig->sigNode, './ds:SignedInfo/ds:SignatureMethod');
if (empty($sigMethod)) {
echo sprintf('Missing SignatureMethod element');
exit();
}
$sigMethod = $sigMethod[0];
if (!$sigMethod->hasAttribute('Algorithm')) {
echo sprintf('Missing Algorithm-attribute on SignatureMethod element.');
exit;
}
$algo = $sigMethod->getAttribute('Algorithm');
if ($key->type === MoXMLSecurityKey::RSA_SHA1 && $algo !== $key->type) {
$key = self::castKey($key, $algo);
}
/* Check the signature. */
if (! $objXMLSecDSig->verify($key)) {
echo sprintf('Unable to validate Signature');
exit;
}
}
public static function castKey(MoXMLSecurityKey $key, $algorithm, $type = 'public')
{
// do nothing if algorithm is already the type of the key
if ($key->type === $algorithm) {
return $key;
}
$keyInfo = openssl_pkey_get_details($key->key);
if ($keyInfo === FALSE) {
echo sprintf('Unable to get key details from XMLSecurityKey.');
exit;
}
if (!isset($keyInfo['key'])) {
echo sprintf('Missing key in public key details.');
exit;
}
$newKey = new MoXMLSecurityKey($algorithm, array('type'=>$type));
$newKey->loadKey($keyInfo['key']);
return $newKey;
}
public static function processResponse($currentURL, $certFingerprint, $signatureData,
SAML2_Response $response, $certNumber,$relayState) {
$assertion = current($response->getAssertions());
$notBefore = $assertion->getNotBefore();
if ($notBefore !== NULL && $notBefore > time() + 60) {
wp_die('Received an assertion that is valid in the future. Check clock synchronization on IdP and SP.');
}
$notOnOrAfter = $assertion->getNotOnOrAfter();
if ($notOnOrAfter !== NULL && $notOnOrAfter <= time() - 60) {
wp_die('Received an assertion that has expired. Check clock synchronization on IdP and SP.');
}
$sessionNotOnOrAfter = $assertion->getSessionNotOnOrAfter();
if ($sessionNotOnOrAfter !== NULL && $sessionNotOnOrAfter <= time() - 60) {
wp_die('Received an assertion with a session that has expired. Check clock synchronization on IdP and SP.');
}
/* Validate Response-element destination. */
$msgDestination = $response->getDestination();
if(substr($msgDestination, -1) == '/') {
$msgDestination = substr($msgDestination, 0, -1);
}
if(substr($currentURL, -1) == '/') {
$currentURL = substr($currentURL, 0, -1);
}
if ($msgDestination !== NULL && $msgDestination !== $currentURL) {
echo sprintf('Destination in response doesn\'t match the current URL. Destination is "' .
htmlspecialchars($msgDestination) . '", current URL is "' . htmlspecialchars($currentURL) . '".');
exit;
}
$responseSigned = self::checkSign($certFingerprint, $signatureData, $certNumber,$relayState);
/* Returning boolean $responseSigned */
return $responseSigned;
}
public static function checkSign($certFingerprint, $signatureData, $certNumber, $relayState) {
$certificates = $signatureData['Certificates'];
if (count($certificates) === 0) {
$storedCerts = maybe_unserialize(get_option('saml_x509_certificate'));
$pemCert = $storedCerts[$certNumber];
}else{
$fpArray = array();
$fpArray[] = $certFingerprint;
$pemCert = self::findCertificate($fpArray, $certificates, $relayState);
if($pemCert==false)
return false;
}
$lastException = NULL;
$key = new MoXMLSecurityKey(MoXMLSecurityKey::RSA_SHA1, array('type'=>'public'));
$key->loadKey($pemCert);
try {
/*
* Make sure that we have a valid signature
*/
self::validateSignature($signatureData, $key);
return TRUE;
} catch (Exception $e) {
$lastException = $e;
}
/* We were unable to validate the signature with any of our keys. */
if ($lastException !== NULL) {
throw $lastException;
} else {
return FALSE;
}
}
public static function validateIssuerAndAudience($samlResponse, $spEntityId, $issuerToValidateAgainst, $relayState) {
$issuer = current($samlResponse->getAssertions())->getIssuer();
$assertion = current($samlResponse->getAssertions());
$audiences = $assertion->getValidAudiences();
if(strcmp($issuerToValidateAgainst, $issuer) === 0) {
if(!empty($audiences)) {
if(in_array($spEntityId, $audiences, TRUE)) {
return TRUE;
} else {
if($relayState=='testValidate'){
$Error_message=mo_saml_options_error_constants::Error_invalid_audience;
$Cause_message = mo_saml_options_error_constants::Cause_invalid_audience;
echo '
' . __('Error','miniorange-saml-20-single-sign-on') . ': '.$Error_message.'
' . __('Possible Cause','miniorange-saml-20-single-sign-on'). ': '.$Cause_message.'
' . __('Expected one of the Audiences to be','miniorange-saml-20-single-sign-on'). ': '.$spEntityId.'
' . __('Error','miniorange-saml-20-single-sign-on'). ':'.$Error_message.'
' . __('Possible Cause','miniorange-saml-20-single-sign-on') . ':'.$Cause_message.'
Error: Unable to find a certificate matching the configured fingerprint.
//Please contact your administrator and report the following error:
//Possible Cause: Content of \'X.509 Certificate\' field in Service Provider Settings is incorrect. Please replace it with certificate given below.
//Certificate found in SAML Response:
'.$pem.'