diff --git a/config-templates/config.php b/config-templates/config.php
index 71ad261a70850ed4875b4f3aa5bda1b0fc57092c..9323ddea40e34711d99b372b45674b58367116bd 100644
--- a/config-templates/config.php
+++ b/config-templates/config.php
@@ -363,6 +363,27 @@ $config = array (
 	'memcache_store.expires' =>  36 * (60*60), // 36 hours.
 
 
+	/*
+	 * Should signing of generated metadata be enabled by default.
+	 *
+	 * Metadata signing can also be enabled for a individual SP or IdP by setting the
+	 * same option in the metadata for the SP or IdP.
+	 */
+	'metadata.sign.enable' => FALSE,
+
+	/*
+	 * The default key & certificate which should be used to sign generated metadata. These
+	 * are files stored in the cert dir.
+	 * These values can be overridden by the options with the same names in the SP or
+	 * IdP metadata.
+	 *
+	 * If these aren't specified here or in the metadata for the SP or IdP, then
+	 * the 'certificate' and 'privatekey' option in the metadata will be used.
+	 * if those aren't set, signing of metadata will fail.
+	 */
+	'metadata.sign.privatekey' => NULL,
+	'metadata.sign.certificate' => NULL,
+
 
 );
 
diff --git a/lib/SimpleSAML/Metadata/Signer.php b/lib/SimpleSAML/Metadata/Signer.php
new file mode 100644
index 0000000000000000000000000000000000000000..2d69f0d11637938c8a7c1471fdd0da542ba706c2
--- /dev/null
+++ b/lib/SimpleSAML/Metadata/Signer.php
@@ -0,0 +1,183 @@
+<?php
+
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Configuration.php');
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'xmlseclibs.php');
+
+
+/**
+ * This class implements a helper function for signing of metadata.
+ *
+ * @author Olav Morken, UNINETT AS.
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+class SimpleSAML_Metadata_Signer {
+
+	/**
+	 * This functions finds what key & certificate files should be used to sign the metadata
+	 * for the given entity.
+	 *
+	 * @param $config  Our SimpleSAML_Configuration instance.
+	 * @param $entityMetadata  The metadata of the entity.
+	 * @param $type  A string which describes the type entity this is, e.g. 'SAML 2 IdP' or 'Shib 1.3 SP'.
+	 * @return An associative array with the keys 'privatekey' and 'certificate'.
+	 */
+	private static function findKeyCert($config, $entityMetadata, $type) {
+
+		/* First we look for metadata.privatekey and metadata.certificate in the metadata. */
+		if(array_key_exists('metadata.sign.privatekey', $entityMetadata)
+			|| array_key_exists('metadata.sign.certificate', $entityMetadata)) {
+
+			if(!array_key_exists('metadata.sign.privatekey', $entityMetadata)
+				|| !array_key_exists('metadata.sign.certificate', $entityMetadata)) {
+
+				throw new Exception('Missing either the "metadata.sign.privatekey" or the' .
+					' "metadata.sign.certificate" configuration option in the metadata for' .
+					' the ' . $type . ' "' . $entityMetadata['entityid'] . '". If one of' .
+					' these options is specified, then the other must also be specified.');
+			}
+			return array(
+				'privatekey' => $entityMetadata['metadata.sign.privatekey'],
+				'certificate' => $entityMetadata['metadata.sign.certificate']
+				);
+		}
+
+		/* Then we look for default values in the global configuration. */
+		$privatekey = $config->getValue('metadata.sign.privatekey', NULL);
+		$certificate = $config->getValue('metadata.sign.certificate', NULL);
+		if($privatekey !== NULL || $certificate !== NULL) {
+			if($privatekey === NULL || $certificate === NULL) {
+				throw new Exception('Missing either the "metadata.sign.privatekey" or the' .
+					' "metadata.sign.certificate" configuration option in the global' .
+					' configuration. If one of these options is specified, then the other'.
+					' must also be specified.');
+			}
+			return array('privatekey' => $privatekey, 'certificate' => $certificate);
+		}
+
+		/* As a last resort we attempt to use the privatekey and certificate option from the metadata. */
+		if(array_key_exists('privatekey', $entityMetadata)
+			|| array_key_exists('certificate', $entityMetadata)) {
+
+			if(!array_key_exists('privatekey', $entityMetadata)
+				|| !array_key_exists('certificate', $entityMetadata)) {
+				throw new Exception('Both the "privatekey" and the "certificate" option must' .
+					' be set in the metadata for the ' . $type .' "' .
+					$entityMetadata['entityid'] . '" before it is possible to sign metadata' .
+					' from this entity.');
+			}
+
+			return array(
+				'privatekey' => $entityMetadata['privatekey'],
+				'certificate' => $entityMetadata['certificate']
+				);
+
+		}
+
+		throw new Exception('Could not find what key & certificate should be used to sign the metadata' .
+			' for the ' . $type . ' "' . $entityMetadata['entityid'] . '".');
+	}
+
+
+	/**
+	 * Determine whether metadata signing is enabled for the given metadata.
+	 *
+	 * @param $config  Our SimpleSAML_Configuration instance.
+	 * @param $entityMetadata  The metadata of the entity.
+	 * @param $type  A string which describes the type entity this is, e.g. 'SAML 2 IdP' or 'Shib 1.3 SP'.
+	 */
+	private static function isMetadataSigningEnabled($config, $entityMetadata, $type) {
+
+		/* First check the metadata for the entity. */
+		if(array_key_exists('metadata.sign.enable', $entityMetadata)) {
+			if(!is_bool($entityMetadata['metadata.sign.enable'])) {
+				throw new Exception(
+					'Invalid value for the "metadata.sign.enable" configuration option for' .
+					' the ' . $type .' "' . $entityMetadata['entityid'] . '". This option' .
+					' should be a boolean.');
+			}
+
+			return $entityMetadata['metadata.sign.enable'];
+		}
+
+		$enabled = $config->getValue('metadata.sign.enable', FALSE);
+		if(!is_bool($enabled)) {
+			throw new Exception('Invalid value for the "metadata.sign.enable" configuration option.' .
+				' This option should be a boolean.');
+		}
+
+		return $enabled;
+	}
+
+
+	/**
+	 * Signs the given metadata if metadata signing is enabled.
+	 *
+	 * @param $metadataString  A string with the metadata.
+	 * @param $entityMetadata  The metadata of the entity.
+	 * @param $type A string which describes the type entity this is, e.g. 'SAML 2 IdP' or 'Shib 1.3 SP'.
+	 * @return The $metadataString with the signature embedded.
+	 */
+	public static function sign($metadataString, $entityMetadata, $type) {
+
+		$config = SimpleSAML_Configuration::getInstance();
+
+		/* Check if metadata signing is enabled. */
+		if (!self::isMetadataSigningEnabled($config, $entityMetadata, $type)) {
+			return $metadataString;
+		}
+
+
+		/* Find the key & certificate which should be used to sign the metadata. */
+
+		$keyCertFiles = self::findKeyCert($config, $entityMetadata, $type);
+
+		$keyFile = $config->getPathValue('certdir') . $keyCertFiles['privatekey'];
+		if (!file_exists($keyFile)) {
+			throw new Exception('Could not find private key file [' . $keyFile . '], which is needed to sign the metadata');
+		}
+		$keyData = file_get_contents($keyFile);
+
+		$certFile = $config->getPathValue('certdir') . $keyCertFiles['certificate'];
+		if (!file_exists($certFile)) {
+			throw new Exception('Could not find certificate file [' . $certFile . '], which is needed to sign the metadata');
+		}
+		$certData = file_get_contents($certFile);
+
+
+		/* Convert the metadata to a DOM tree. */
+		$xml = new DOMDocument();
+		if(!$xml->loadXML($metadataString)) {
+			throw new Exception('Error parsing self-generated metadata.');
+		}
+
+		/* Load the private key. */
+		$objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type' => 'private'));
+		$objKey->loadKey($keyData, FALSE);
+
+		/* Get the EntityDescriptor node we should sign. */
+		$rootNode = $xml->firstChild;
+
+		/* Sign the metadata with our private key. */
+		$objXMLSecDSig = new XMLSecurityDSig();
+		$objXMLSecDSig->setCanonicalMethod(XMLSecurityDSig::EXC_C14N);
+
+		$objXMLSecDSig->addReferenceList(array($rootNode), XMLSecurityDSig::SHA1,
+			array('http://www.w3.org/2000/09/xmldsig#enveloped-signature', XMLSecurityDSig::EXC_C14N),
+			array('id_name' => 'ID'));
+
+		$objXMLSecDSig->sign($objKey);
+
+		/* Add the certificate to the signature. */
+		$objXMLSecDSig->add509Cert($certData, true);
+
+		/* Add the signature to the metadata. */
+		$objXMLSecDSig->insertSignature($rootNode, $rootNode->firstChild);
+
+		/* Return the DOM tree as a string. */
+		return $xml->saveXML();
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/www/admin/metadata.php b/www/admin/metadata.php
index d4177f772ce6edb02b61d913560770cac3cb6393..2a0337b0a8959407653230822b5dfe5a583c9244 100644
--- a/www/admin/metadata.php
+++ b/www/admin/metadata.php
@@ -34,7 +34,7 @@ try {
 		foreach ($metalist AS $entityid => $mentry) {
 			$results[$entityid] = SimpleSAML_Utilities::checkAssocArrayRules($mentry,
 				array('entityid', 'host'),
-				array('request.signing','certificate','privatekey', 'NameIDFormat', 'ForceAuthn', 'AuthnContextClassRef', 'SPNameQualifier', 'attributemap', 'attributealter', 'attributes')
+				array('request.signing','certificate','privatekey', 'NameIDFormat', 'ForceAuthn', 'AuthnContextClassRef', 'SPNameQualifier', 'attributemap', 'attributealter', 'attributes', 'metadata.sign.enable', 'metadata.sign.privatekey', 'metadata.sign.certificate')
 			);
 		}
 		$et->data['metadata.saml20-sp-hosted'] = $results;
@@ -57,7 +57,7 @@ try {
 		foreach ($metalist AS $entityid => $mentry) {
 			$results[$entityid] = SimpleSAML_Utilities::checkAssocArrayRules($mentry,
 				array('entityid', 'host', 'privatekey', 'certificate', 'auth'),
-				array('requireconsent','request.signing', 'authority', 'attributemap', 'attributealter', 'userid.attribute')
+				array('requireconsent','request.signing', 'authority', 'attributemap', 'attributealter', 'userid.attribute', 'metadata.sign.enable', 'metadata.sign.privatekey', 'metadata.sign.certificate')
 			);
 		}
 		$et->data['metadata.saml20-idp-hosted'] = $results;
@@ -94,7 +94,7 @@ try {
 		foreach ($metalist AS $entityid => $mentry) {
 			$results[$entityid] = SimpleSAML_Utilities::checkAssocArrayRules($mentry,
 				array('entityid', 'SingleSignOnService', 'certFingerprint'),
-				array('name', 'description', 'base64attributes')
+				array('name', 'description', 'base64attributes', 'metadata.sign.enable', 'metadata.sign.privatekey', 'metadata.sign.certificate')
 			);
 		}
 		$et->data['metadata.shib13-idp-remote'] = $results;
@@ -117,7 +117,7 @@ try {
 		foreach ($metalist AS $entityid => $mentry) {
 			$results[$entityid] = SimpleSAML_Utilities::checkAssocArrayRules($mentry,
 				array('entityid', 'AssertionConsumerService'),
-				array('base64attributes', 'audience', 'attributemap', 'attributealter', 'simplesaml.attributes', 'attributes', 'name', 'description')
+				array('base64attributes', 'audience', 'attributemap', 'attributealter', 'simplesaml.attributes', 'attributes', 'name', 'description', 'metadata.sign.enable', 'metadata.sign.privatekey', 'metadata.sign.certificate')
 			);
 		}
 		$et->data['metadata.shib13-sp-remote'] = $results;
diff --git a/www/saml2/idp/metadata.php b/www/saml2/idp/metadata.php
index 1ffa8d010d1a49de7e8a7cbad753a3351e29087c..b4c84f0691900af87b48226c8d6d08a0600a03a1 100644
--- a/www/saml2/idp/metadata.php
+++ b/www/saml2/idp/metadata.php
@@ -5,10 +5,9 @@ require_once('../../_include.php');
 require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Utilities.php');
 require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Session.php');
 require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Metadata/MetaDataStorageHandler.php');
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Metadata/Signer.php');
 require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XHTML/Template.php');
 
-require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'xmlseclibs.php');
-
 /* Load simpleSAMLphp, configuration and metadata */
 $config = SimpleSAML_Configuration::getInstance();
 $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
@@ -91,8 +90,10 @@ try {
         
     </IDPSSODescriptor>
 </EntityDescriptor>';
-	
-	
+
+	/* Sign the metadata if enabled. */
+	$metaxml = SimpleSAML_Metadata_Signer::sign($metaxml, $idpmeta, 'SAML 2 IdP');
+
 	if (array_key_exists('output', $_GET) && $_GET['output'] == 'xhtml') {
 		$defaultidp = $config->getValue('default-saml20-idp');
 		
diff --git a/www/saml2/sp/metadata.php b/www/saml2/sp/metadata.php
index 6f8458d69ec6347aecbc4464c4fc8335324d50b6..e71a0d30c3287fe212312510b637fbcf45f5e467 100644
--- a/www/saml2/sp/metadata.php
+++ b/www/saml2/sp/metadata.php
@@ -5,6 +5,7 @@ require_once('../../_include.php');
 require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Utilities.php');
 require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Session.php');
 require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Metadata/MetaDataStorageHandler.php');
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Metadata/Signer.php');
 require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XHTML/Template.php');
 
 /* Load simpleSAMLphp, configuration and metadata */
@@ -83,6 +84,9 @@ try {
 
 </EntityDescriptor>';
 
+	/* Sign the metadata if enabled. */
+	$metaxml = SimpleSAML_Metadata_Signer::sign($metaxml, $spmeta, 'SAML 2 SP');
+
 	if (array_key_exists('output', $_GET) && $_GET['output'] == 'xhtml') {
 		$defaultidp = $config->getValue('default-saml20-idp');
 		
diff --git a/www/shib13/idp/metadata.php b/www/shib13/idp/metadata.php
index a58f17a3cfc7fd8d11a79def08de3cac3631dd46..1ad41aa6e783e2b6244f86429b26911d5258a52d 100644
--- a/www/shib13/idp/metadata.php
+++ b/www/shib13/idp/metadata.php
@@ -5,10 +5,9 @@ require_once('../../_include.php');
 require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Utilities.php');
 require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Session.php');
 require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Metadata/MetaDataStorageHandler.php');
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Metadata/Signer.php');
 require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XHTML/Template.php');
 
-require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'xmlseclibs.php');
-
 /* Load simpleSAMLphp, configuration and metadata */
 $config = SimpleSAML_Configuration::getInstance();
 $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
@@ -76,6 +75,9 @@ try {
 	</ContactPerson>
 	
 </EntityDescriptor>';
+
+	/* Sign the metadata if enabled. */
+	$metaxml = SimpleSAML_Metadata_Signer::sign($metaxml, $idpmeta, 'Shib 1.3 IdP');
 	
 	
 	if (array_key_exists('output', $_GET) && $_GET['output'] == 'xhtml') {
diff --git a/www/shib13/sp/metadata.php b/www/shib13/sp/metadata.php
index 58e509321924eccda8c66f55718bc4c8e4ae6e15..b9152e8d029d86bb97da9127afbe0e0f6b8038a6 100644
--- a/www/shib13/sp/metadata.php
+++ b/www/shib13/sp/metadata.php
@@ -5,6 +5,7 @@ require_once('../../_include.php');
 require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Utilities.php');
 require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Session.php');
 require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Metadata/MetaDataStorageHandler.php');
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Metadata/Signer.php');
 require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XHTML/Template.php');
 
 /* Load simpleSAMLphp, configuration and metadata */
@@ -55,6 +56,9 @@ try {
 		
 </EntityDescriptor>';
 
+	/* Sign the metadata if enabled. */
+	$metaxml = SimpleSAML_Metadata_Signer::sign($metaxml, $spmeta, 'Shib 1.3 SP');
+
 	if (array_key_exists('output', $_GET) && $_GET['output'] == 'xhtml') {
 		$defaultidp = $config->getValue('default-shib13-idp');