diff --git a/config-templates/config.php b/config-templates/config.php
index e11704b8863863830bf62602116473bccecaf59d..3104e467218cdc73f16e5370a93d4d47867a504e 100644
--- a/config-templates/config.php
+++ b/config-templates/config.php
@@ -560,6 +560,17 @@ $config = array(
      * - 'file': Path to the XML file with the metadata.
      * - 'url': The URL to fetch metadata from. THIS IS ONLY FOR DEBUGGING - THERE IS NO CACHING OF THE RESPONSE.
      *
+     * MDX metadata handler:
+     * This metadata handler looks up for the metadata of an entity at the given MDX server.
+     * The MDX metadata handler defines the following options:
+     * - 'type': This is always 'mdx'.
+     * - 'server': URL of the MDX server (url:port). Mandatory.
+     * - 'validateFingerprint': The fingerprint of the certificate used to sign the metadata.
+     *                          You don't need this option if you don't want to validate the signature on the metadata. Optional.
+     * - 'cachedir': Directory where metadata can be cached. Optional.
+     * - 'cachelength': Maximum time metadata cah be cached, in seconds. Default to 24
+     *                  hours (86400 seconds). Optional.
+     *
      *
      * Examples:
      *
@@ -577,6 +588,10 @@ $config = array(
      *     array('type' => 'xml', 'file' => 'idp.example.org-idpMeta.xml'),
      *     ),
      *
+     * This example defines an mdx source.
+     * 'metadata.sources' => array(
+     *     array('type' => 'mdx', server => 'http://mdx.server.com:8080', 'cachedir' => '/var/simplesamlphp/mdx-cache', 'cachelength' => 86400)
+     *     ),
      *
      * Default:
      * 'metadata.sources' => array(
diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerMDX.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerMDX.php
new file mode 100644
index 0000000000000000000000000000000000000000..7364a6fe9cad856d59097922779720a3cfec6a20
--- /dev/null
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerMDX.php
@@ -0,0 +1,284 @@
+<?php
+
+/**
+ * This class implements SAML Metadata Exchange Protocol
+ *
+ * @author Andreas Ă…kre Solberg, UNINETT AS.
+ * @author Olav Morken, UNINETT AS.
+ * @author Tamas Frank, NIIFI
+ * @package simpleSAMLphp
+ */
+class SimpleSAML_Metadata_MetaDataStorageHandlerMDX extends SimpleSAML_Metadata_MetaDataStorageSource {
+
+    /**
+     * The URL of MDX server (url:port)
+     */
+    private $server;
+
+    /**
+     * The fingerprint of the certificate used to sign the metadata. You don't need this option if you don't want to validate the signature on the metadata.
+     */
+    private $validateFingerprint;
+
+	/**
+	 * The cache directory, or NULL if no cache directory is configured.
+	 */
+	private $cacheDir;
+
+
+	/**
+	 * The maximum cache length, in seconds.
+	 */
+	private $cacheLength;
+
+
+	/**
+	 * This function initializes the dynamic XML metadata source.
+	 *
+	 * Options:
+	 * - 'server': URL of the MDX server (url:port). Mandatory.
+     * - 'validateFingerprint': The fingerprint of the certificate used to sign the metadata.
+     *                          You don't need this option if you don't want to validate the signature on the metadata. Optional.
+	 * - 'cachedir':  Directory where metadata can be cached. Optional.
+	 * - 'cachelength': Maximum time metadata cah be cached, in seconds. Default to 24
+	 *                  hours (86400 seconds).
+	 *
+	 * @param array $config  The configuration for this instance of the XML metadata source.
+	 */
+	protected function __construct($config) {
+		assert('is_array($config)');
+
+		if (!array_key_exists('server', $config)){
+			throw new Exception('Error, the $server parameter of the config has not been set!');
+		} else {
+			$this->server = $config['server'];
+		}
+
+		if (array_key_exists('validateFingerprint', $config)) {
+			$this->validateFingerprint = $config['validateFingerprint'];
+		} else {
+			$this->validateFingerprint = NULL;
+		}
+
+		if (array_key_exists('cachedir', $config)) {
+			$globalConfig = SimpleSAML_Configuration::getInstance();
+			$this->cacheDir = $globalConfig->resolvePath($config['cachedir']);
+		} else {
+			$this->cacheDir = NULL;
+		}
+
+		if (array_key_exists('cachelength', $config)) {
+			$this->cacheLength = $config['cachelength'];
+		} else {
+			$this->cacheLength = 86400;
+		}
+
+	}
+
+
+	/**
+	 * This function returns an associative array with metadata for all entities in the given set. The
+	 * key of the array is the entity id.
+	 *
+	 * @param $set  The set we want to list metadata for.
+	 * @return An associative array with all entities in the given set.
+	 */
+	public function getMetadataSet($set) {
+
+		/* We don't have this metadata set. */
+		return array();
+	}
+
+
+	/**
+	 * Find the cache file name for an entity,
+	 *
+	 * @param string $set  The metadata set this entity belongs to.
+	 * @param string $entityId  The entity id of this entity.
+	 * @return string  The full path to the cache file.
+	 */
+	private function getCacheFilename($set, $entityId) {
+		assert('is_string($set)');
+		assert('is_string($entityId)');
+
+		$cachekey = sha1($entityId);
+		$globalConfig = SimpleSAML_Configuration::getInstance();
+		return $this->cacheDir . '/' . $set . '-' . $cachekey . '.cached.xml';
+	}
+
+
+	/**
+	 * Load a entity from the cache.
+	 *
+	 * @param string $set  The metadata set this entity belongs to.
+	 * @param string $entityId  The entity id of this entity.
+	 * @return array|NULL  The associative array with the metadata for this entity, or NULL
+	 *                     if the entity could not be found.
+	 */
+	private function getFromCache($set, $entityId) {
+		assert('is_string($set)');
+		assert('is_string($entityId)');
+
+		if (empty($this->cacheDir)) {
+			return NULL;
+		}
+
+		$cachefilename = $this->getCacheFilename($set, $entityId);
+		if (!file_exists($cachefilename)) return NULL;
+		if (!is_readable($cachefilename)) throw new Exception('Could not read cache file for entity [' . $cachefilename. ']');
+		SimpleSAML_Logger::debug('MetaData - Handler.MDX: Reading cache [' . $entityId . '] => [' . $cachefilename . ']' );
+
+		/* Ensure that this metadata isn't older that the cachelength option allows. This
+		 * must be verified based on the file, since this option may be changed after the
+		 * file is written.
+		 */
+		$stat = stat($cachefilename);
+		if ($stat['mtime'] + $this->cacheLength <= time()) {
+			SimpleSAML_Logger::debug('MetaData - Handler.MDX: Cache file older that the cachelength option allows.');
+			return NULL;
+		}
+
+		$rawData = file_get_contents($cachefilename);
+		if (empty($rawData)) {
+			throw new Exception('Error reading metadata from cache file "' . $cachefilename . '": ' .
+				SimpleSAML_Utilities::getLastError());
+		}
+
+		$data = unserialize($rawData);
+		if ($data === FALSE) {
+			throw new Exception('Error deserializing cached data from file "' . $cachefilename .'".');
+		}
+
+		if (!is_array($data)) {
+			throw new Exception('Cached metadata from "' . $cachefilename . '" wasn\'t an array.');
+		}
+
+		return $data;
+	}
+
+
+	/**
+	 * Save a entity to the cache.
+	 *
+	 * @param string $set  The metadata set this entity belongs to.
+	 * @param string $entityId  The entity id of this entity.
+	 * @param array $data  The associative array with the metadata for this entity.
+	 */
+	private function writeToCache($set, $entityId, $data) {
+		assert('is_string($set)');
+		assert('is_string($entityId)');
+		assert('is_array($data)');
+
+		if (empty($this->cacheDir)) {
+			return;
+		}
+
+		$cachefilename = $this->getCacheFilename($set, $entityId);
+		if (!is_writable(dirname($cachefilename))) throw new Exception('Could not write cache file for entity [' . $cachefilename. ']');
+		SimpleSAML_Logger::debug('MetaData - Handler.MDX: Writing cache [' . $entityId . '] => [' . $cachefilename . ']' );
+		file_put_contents($cachefilename, serialize($data));
+	}
+
+
+	/**
+	 * Retrieve metadata for the correct set from a SAML2Parser.
+	 *
+	 * @param SimpleSAML_Metadata_SAMLParser $entity  A SAML2Parser representing an entity.
+	 * @param string $set  The metadata set we are looking for.
+	 * @return array|NULL  The associative array with the metadata, or NULL if no metadata for
+	 *                     the given set was found.
+	 */
+	private static function getParsedSet(SimpleSAML_Metadata_SAMLParser $entity, $set) {
+		assert('is_string($set)');
+
+		switch($set) {
+		case 'saml20-idp-remote':
+			return $entity->getMetadata20IdP();
+		case 'saml20-sp-remote':
+			return $entity->getMetadata20SP();
+		case 'shib13-idp-remote':
+			return $entity->getMetadata1xIdP();
+		case 'shib13-sp-remote':
+			return $entity->getMetadata1xSP();
+		case 'attributeauthority-remote':
+			$ret = $entity->getAttributeAuthorities();
+			return $ret[0];
+
+		default:
+			SimpleSAML_Logger::warning('MetaData - Handler.MDX: Unknown metadata set: ' . $set);
+		}
+
+		return NULL;
+	}
+
+
+	/**
+	 * Overriding this function from the superclass SimpleSAML_Metadata_MetaDataStorageSource.
+	 *
+	 * This function retrieves metadata for the given entity id in the given set of metadata.
+	 * It will return NULL if it is unable to locate the metadata.
+	 *
+	 * This class implements this function using the getMetadataSet-function. A subclass should
+	 * override this function if it doesn't implement the getMetadataSet function, or if the
+	 * implementation of getMetadataSet is slow.
+	 *
+	 * @param $index  The entityId or metaindex we are looking up.
+	 * @param $set  The set we are looking for metadata in.
+	 * @return An associative array with metadata for the given entity, or NULL if we are unable to
+	 *         locate the entity.
+	 */
+	public function getMetaData($index, $set) {
+		assert('is_string($index)');
+		assert('is_string($set)');
+
+		SimpleSAML_Logger::info('MetaData - Handler.MDX: Loading metadata entity [' . $index . '] from [' . $set . ']' );
+
+		/* Read from cache if possible. */
+		$data = $this->getFromCache($set, $index);
+
+		if ($data !== NULL && array_key_exists('expires', $data) && $data['expires'] < time()) {
+			/* Metadata has expired. */
+			$data = NULL;
+		}
+
+		if (isset($data)) {
+			/* Metadata found in cache and not expired. */
+			SimpleSAML_Logger::debug('MetaData - Handler.MDX: Using cached metadata for: ' . $index . '.');
+			return $data;
+		}
+
+		/* Look at Metadata Query Protocol: https://github.com/iay/md-query/blob/master/draft-young-md-query.txt */
+		$mdx_url = $this->server . '/entities/' . urlencode( $index);
+
+		SimpleSAML_Logger::debug('MetaData - Handler.MDX: Downloading metadata for "'. $index .'" from [' . $mdx_url . ']' );
+		try {
+			$xmldata = SimpleSAML_Utilities::fetch($mdx_url);
+		} catch(Exception $e) {
+			SimpleSAML_Logger::warning('Fetching metadata for ' . $index . ': ' . $e->getMessage());
+		}
+
+		if (empty($xmldata)) {
+			throw new Exception('Error downloading metadata for "'. $index .'" from "' . $mdx_url . '": ' .
+				SimpleSAML_Utilities::getLastError());
+		}
+
+		$entity = SimpleSAML_Metadata_SAMLParser::parseString($xmldata);
+		SimpleSAML_Logger::debug('MetaData - Handler.MDX: Completed parsing of [' .	$mdx_url . ']'  );
+
+		if( $this->validateFingerprint !== NULL) {
+			if(!$entity->validateFingerprint($this->validateFingerprint)) {
+				throw new Exception ('Error, could not verify signature for entity: ' . $index . '".');
+			}
+		}
+
+		$data = self::getParsedSet($entity, $set);
+		if ($data === NULL) {
+			throw new Exception('No metadata for set "' . $set . '" available from "' . $index . '".');
+		}
+
+		$this->writeToCache($set, $index, $data);
+
+		return $data;
+	}
+
+}
diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageSource.php b/lib/SimpleSAML/Metadata/MetaDataStorageSource.php
index 076271f420b39446ef136c9f8aa47c162624f2b7..549d92040b9b1e95cbbc2eb583795dd582388f66 100644
--- a/lib/SimpleSAML/Metadata/MetaDataStorageSource.php
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageSource.php
@@ -66,6 +66,8 @@ abstract class SimpleSAML_Metadata_MetaDataStorageSource {
 				return new SimpleSAML_Metadata_MetaDataStorageHandlerXML($sourceConfig);
 			case 'serialize':
 				return new SimpleSAML_Metadata_MetaDataStorageHandlerSerialize($sourceConfig);
+			case 'mdx':
+				return new SimpleSAML_Metadata_MetaDataStorageHandlerMDX($sourceConfig);
 			default:
 				throw new Exception('Invalid metadata source type: "' . $type . '".');
 		}