From 354edab28899e095ab5a5062ddb17977e681ffec Mon Sep 17 00:00:00 2001
From: Olav Morken <olav.morken@uninett.no>
Date: Tue, 10 Aug 2010 11:26:36 +0000
Subject: [PATCH] saml_Message::checkSign: Add support for multiple signature
 keys.

git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@2509 44740490-163a-0410-bde0-09ae8108e29a
---
 lib/SimpleSAML/Configuration.php | 11 ++--
 modules/saml/lib/Message.php     | 94 +++++++++++++++++++++-----------
 2 files changed, 68 insertions(+), 37 deletions(-)

diff --git a/lib/SimpleSAML/Configuration.php b/lib/SimpleSAML/Configuration.php
index d091d6038..f0d1ece8a 100644
--- a/lib/SimpleSAML/Configuration.php
+++ b/lib/SimpleSAML/Configuration.php
@@ -1048,9 +1048,10 @@ class SimpleSAML_Configuration {
 				$ret[] = $key;
 
 			}
-		}
-
-		if ($this->hasValue($prefix . 'certData')) {
+			if (!empty($ret)) {
+				return $ret;
+			}
+		} elseif ($this->hasValue($prefix . 'certData')) {
 			$certData = $this->getString($prefix . 'certData');
 			$certData = preg_replace('/\s+/', '', $certData);
 			return array(
@@ -1061,9 +1062,7 @@ class SimpleSAML_Configuration {
 					'X509Certificate' => $certData,
 				),
 			);
-		}
-
-		if ($this->hasValue($prefix . 'certificate')) {
+		} elseif ($this->hasValue($prefix . 'certificate')) {
 			$file = $this->getString($prefix . 'certificate');
 			$file = SimpleSAML_Utilities::resolveCert($file);
 			$data = @file_get_contents($file);
diff --git a/modules/saml/lib/Message.php b/modules/saml/lib/Message.php
index 0d971c28d..33ca2cfb9 100644
--- a/modules/saml/lib/Message.php
+++ b/modules/saml/lib/Message.php
@@ -127,33 +127,44 @@ class sspmod_saml_Message {
 	 */
 	public static function checkSign(SimpleSAML_Configuration $srcMetadata, SAML2_SignedElement $element) {
 
-		$certificates = $element->getCertificates();
-		SimpleSAML_Logger::debug('Found ' . count($certificates) . ' certificates in ' . get_class($element));
-
-		/* Find the certificate that should verify signatures by this entity. */
-		$certArray = SimpleSAML_Utilities::loadPublicKey($srcMetadata, FALSE);
-		if ($certArray !== NULL) {
-			if (array_key_exists('PEM', $certArray)) {
-				$pemCert = $certArray['PEM'];
-			} else {
-				/*
-				 * We don't have the full certificate stored. Try to find it
-				 * in the message or the assertion instead.
-				 */
-				if (count($certificates) === 0) {
-					/* We need the full certificate in order to match it against the fingerprint. */
-					SimpleSAML_Logger::debug('No certificate in message when validating against fingerprint.');
-					return FALSE;
+		/* Find the public key that should verify signatures by this entity. */
+		$keys = $srcMetadata->getPublicKeys('signing');
+		if ($keys !== NULL) {
+			$pemKeys = array();
+			foreach ($keys as $key) {
+				switch ($key['type']) {
+				case 'X509Certificate':
+					$pemKeys[] = "-----BEGIN CERTIFICATE-----\n" .
+						chunk_split($key['X509Certificate'], 64) .
+						"-----END CERTIFICATE-----\n";
+					break;
+				default:
+					SimpleSAML_Logger::debug('Skipping unknown key type: ' . $key['type']);
 				}
+			}
 
-				$certFingerprints = $certArray['certFingerprint'];
-				if (count($certFingerprints) === 0) {
-					/* For some reason, we have a certFingerprint entry without any fingerprints. */
-					throw new SimpleSAML_Error_Exception('certFingerprint array was empty.');
-				}
+		} elseif ($srcMetadata->hasValue('certFingerprint')) {
+			$certFingerPrint = $srcMetadata->getArrayizeString('certFingerprint');
+			foreach ($certFingerprint as &$fp) {
+				$fp = strtolower(str_replace(':', '', $fp));
+			}
+
+			$certificates = $element->getCertificates();
 
-				$pemCert = self::findCertificate($certFingerprints, $certificates);
+			/*
+			 * We don't have the full certificate stored. Try to find it
+			 * in the message or the assertion instead.
+			 */
+			if (count($certificates) === 0) {
+				/* We need the full certificate in order to match it against the fingerprint. */
+				SimpleSAML_Logger::debug('No certificate in message when validating against fingerprint.');
+				return FALSE;
+			} else {
+				SimpleSAML_Logger::debug('Found ' . count($certificates) . ' certificates in ' . get_class($element));
 			}
+
+			$pemCert = self::findCertificate($certFingerprints, $certificates);
+			$pemKeys = array($pemCert);
 		} else {
 			/* Attempt CA validation. */
 			$caFile = $srcMetadata->getString('caFile', NULL);
@@ -176,18 +187,39 @@ class sspmod_saml_Message {
 				"-----END CERTIFICATE-----\n";
 
 			SimpleSAML_Utilities::validateCA($pemCert, $caFile);
+			$pemKeys = array($pemCert);
 		}
 
+		SimpleSAML_Logger::debug('Has ' . count($pemKeys) . ' candidate keys for validation.');
 
-		/* Extract the public key from the certificate for validation. */
-		$key = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type'=>'public'));
-		$key->loadKey($pemCert);
+		$lastException = NULL;
+		foreach ($pemKeys as $i => $pem) {
+			$key = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type'=>'public'));
+			$key->loadKey($pem);
 
-		/*
-		 * Make sure that we have a valid signature on either the response
-		 * or the assertion.
-		 */
-		return $element->validate($key);
+			try {
+				/*
+				 * Make sure that we have a valid signature on either the response
+				 * or the assertion.
+				 */
+				$res = $element->validate($key);
+				if ($res) {
+					SimpleSAML_Logger::debug('Validation with key #' . $i . ' succeeded.');
+					return TRUE;
+				}
+				SimpleSAML_Logger::debug('Validation with key #' . $i . ' failed without exception.');
+			} catch (Exception $e) {
+				SimpleSAML_Logger::debug('Validation with key #' . $i . ' failed with exception: ' . $e->getMessage());
+				$lastException = $e;
+			}
+		}
+
+		/* We were unable to validate the signature with any of our keys. */
+		if ($lastException !== NULL) {
+			throw $lastException;
+		} else {
+			return FALSE;
+		}
 	}
 
 
-- 
GitLab