diff --git a/modules/saml/www/sp/metadata.php b/modules/saml/www/sp/metadata.php
index 6b3aa05e4ba7e851be30f2f62f6976d13054e505..ed28dcde834cf0ea24641e506c15728c185a2a57 100644
--- a/modules/saml/www/sp/metadata.php
+++ b/modules/saml/www/sp/metadata.php
@@ -5,6 +5,7 @@ if (!array_key_exists('PATH_INFO', $_SERVER)) {
 	throw new SimpleSAML_Error_BadRequest('Missing authentication source id in metadata URL');
 }
 
+$config = SimpleSAML_Configuration::getInstance();
 $sourceId = substr($_SERVER['PATH_INFO'], 1);
 $source = SimpleSAML_Auth_Source::getById($sourceId);
 if ($source === NULL) {
@@ -16,106 +17,140 @@ if (!($source instanceof sspmod_saml_Auth_Source_SP)) {
 }
 
 $entityId = $source->getEntityId();
-$metaArray11 = array(
-	'metadata-set' => 'shib13-sp-remote',
-	'entityid' => $entityId,
-	'AssertionConsumerService' => array(
-		array(
-			'index' => 0,
-			'Binding' => 'urn:oasis:names:tc:SAML:1.0:profiles:browser-post',
-			'Location' => SimpleSAML_Module::getModuleURL('saml/sp/saml1-acs.php/' . $sourceId),
-		),
-	),
-);
-
 $spconfig = $source->getMetadata();
-if ($spconfig->getBoolean('saml11.binding.artifact.enable', FALSE)) {
-	$metaArray11['AssertionConsumerService'][] = array(
-		'index' => 1,
-		'Binding' => 'urn:oasis:names:tc:SAML:1.0:profiles:artifact-01',
-		'Location' => SimpleSAML_Module::getModuleURL('saml/sp/saml1-acs.php/' . $sourceId . '/artifact'),
-	);
-}
 
+$ed = new SAML2_XML_md_EntityDescriptor();
+$ed->entityID = $entityId;
 
-$metaArray20 = array(
-	'metadata-set' => 'saml20-sp-remote',
-	'entityid' => $entityId,
-	'AssertionConsumerService' => array(
-		array(
-			'index' => 0,
-			'Binding' => SAML2_Const::BINDING_HTTP_POST,
-			'Location' => SimpleSAML_Module::getModuleURL('saml/sp/saml2-acs.php/' . $sourceId),
-		),
-	),
-	'SingleLogoutService' => array(
-		array(
-			'Binding' => SAML2_Const::BINDING_HTTP_REDIRECT,
-			'Location' => SimpleSAML_Module::getModuleURL('saml/sp/saml2-logout.php/' . $sourceId),
-		),
-	),
+$sp = new SAML2_XML_md_SPSSODescriptor();
+$ed->RoleDescriptor[] = $sp;
+$sp->protocolSupportEnumeration = array(
+	'urn:oasis:names:tc:SAML:1.1:protocol',
+	'urn:oasis:names:tc:SAML:2.0:protocol'
 );
 
+$slo = new SAML2_XML_md_EndpointType();
+$slo->Binding = SAML2_Const::BINDING_HTTP_REDIRECT;
+$slo->Location = SimpleSAML_Module::getModuleURL('saml/sp/saml2-logout.php/' . $sourceId);
+$sp->SingleLogoutService[] = $slo;
+
+
+$acs = new SAML2_XML_md_IndexedEndpointType();
+$acs->index = 0;
+$acs->Binding = SAML2_Const::BINDING_HTTP_POST;
+$acs->Location = SimpleSAML_Module::getModuleURL('saml/sp/saml2-acs.php/' . $sourceId);
+$sp->AssertionConsumerService[] = $acs;
+
+$acs = new SAML2_XML_md_IndexedEndpointType();
+$acs->index = 1;
+$acs->Binding = 'urn:oasis:names:tc:SAML:1.0:profiles:browser-post';
+$acs->Location = SimpleSAML_Module::getModuleURL('saml/sp/saml1-acs.php/' . $sourceId);
+$sp->AssertionConsumerService[] = $acs;
+
 if ($spconfig->getBoolean('saml20.binding.artifact.enable', FALSE)) {
 	$metaArray20['AssertionConsumerService'][] = array(
-		'index' => 1,
+		'index' => 2,
 		'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact',
 		'Location' => SimpleSAML_Module::getModuleURL('saml/sp/saml2-acs.php/' . $sourceId),
 	);
 }
 
+if ($spconfig->getBoolean('saml11.binding.artifact.enable', FALSE)) {
+	$acs = new SAML2_XML_md_IndexedEndpointType();
+	$acs->index = 3;
+	$acs->Binding = 'urn:oasis:names:tc:SAML:1.0:profiles:artifact-01';
+	$acs->Location = SimpleSAML_Module::getModuleURL('saml/sp/saml1-acs.php/' . $sourceId . '/artifact');
+	$sp->AssertionConsumerService[] = $acs;
+}
+
 $certInfo = SimpleSAML_Utilities::loadPublicKey($spconfig);
 if ($certInfo !== NULL && array_key_exists('certData', $certInfo)) {
 	$certData = $certInfo['certData'];
-	$metaArray11['certData'] = $certData;
-	$metaArray20['certData'] = $certData;
+	$kd = SAML2_Utils::createKeyDescriptor($certData);
+	$kd->use = 'signing';
+	$sp->KeyDescriptor[] = $kd;
+
+	$kd = SAML2_Utils::createKeyDescriptor($certData);
+	$kd->use = 'encryption';
+	$sp->KeyDescriptor[] = $kd;
 }
 
 $name = $spconfig->getLocalizedString('name', NULL);
 $attributes = $spconfig->getArray('attributes', array());
 if ($name !== NULL && !empty($attributes)) {
 	/* We have everything necessary to add an AttributeConsumingService. */
+	$acs = new SAML2_XML_md_AttributeConsumingService();
+	$sp->AttributeConsumingService[] = $acs;
 
-	$metaArray20['name'] = $name;
+	$acs->index = 0;
+	$acs->ServiceName = $name;
 
 	$description = $spconfig->getLocalizedString('description', NULL);
 	if ($description !== NULL) {
-		$metaArray20['description'] = $description;
+		$acs->ServiceDescription = $description;
 	}
 
-	$metaArray20['attributes'] = $attributes;
-	$metaArray20['attributes.NameFormat'] = $spconfig->getString('attributes.NameFormat', SAML2_Const::NAMEFORMAT_UNSPECIFIED);
-}
-
-$metaBuilder = new SimpleSAML_Metadata_SAMLBuilder($entityId);
-$metaBuilder->addMetadataSP11($metaArray11);
-$metaBuilder->addMetadataSP20($metaArray20);
+	$nameFormat = $spconfig->getString('attributes.NameFormat', NULL);
+	foreach ($attributes as $attribute) {
+		$a = new SAML2_XML_md_RequestedAttribute();
+		$a->Name = $attribute;
+		$a->NameFormat = $nameFormat;
+		$acs->RequestedAttribute[] = $a;
+	}
 
+}
 
 $orgName = $spconfig->getLocalizedString('OrganizationName', NULL);
 if ($orgName !== NULL) {
+	$o = new SAML2_XML_md_Organization();
+	$o->OrganizationName = $orgName;
 
-	$orgDisplayName = $spconfig->getLocalizedString('OrganizationDisplayName', NULL);
-	if ($orgDisplayName === NULL) {
-		$orgDisplayName = $orgName;
+	$o->OrganizationDisplayName = $spconfig->getLocalizedString('OrganizationDisplayName', NULL);
+	if ($o->OrganizationDisplayName === NULL) {
+		$o->OrganizationDisplayName = $orgName;
 	}
 
-	$orgURL = $spconfig->getLocalizedString('OrganizationURL', NULL);
-	if ($orgURL === NULL) {
+	$o->OrganizationURL = $spconfig->getLocalizedString('OrganizationURL', NULL);
+	if ($o->OrganizationURL === NULL) {
 		throw new SimpleSAML_Error_Exception('If OrganizationName is set, OrganizationURL must also be set.');
 	}
 
+	$ed->Organization = $o;
+}
+
+$c = new SAML2_XML_md_ContactPerson();
+$c->contactType = 'technical';
 
-	$metaBuilder->addOrganization($orgName, $orgDisplayName, $orgURL);
+$email = $config->getString('technicalcontact_email', NULL);
+if ($email !== NULL) {
+	$c->EmailAddress = array($email);
 }
 
-$config = SimpleSAML_Configuration::getInstance();
-$metaBuilder->addContact('technical', array(
-	'emailAddress' => $config->getString('technicalcontact_email', NULL),
-	'name' => $config->getString('technicalcontact_name', NULL),
-	));
+$name = $config->getString('technicalcontact_name', NULL);
+if ($name === NULL) {
+	/* Nothing to do here... */
+} elseif (preg_match('@^(.*?)\s*,\s*(.*)$@D', $name, $matches)) {
+	$c->SurName = $matches[1];
+	$c->GivenName = $matches[2];
+} elseif (preg_match('@^(.*?)\s+(.*)$@D', $name, $matches)) {
+	$c->GivenName = $matches[1];
+	$c->SurName = $matches[2];
+} else {
+	$c->GivenName = $name;
+}
+$ed->ContactPerson[] = $c;
 
-$xml = $metaBuilder->getEntityDescriptorText();
+$xml = $ed->toXML();
+SimpleSAML_Utilities::formatDOMElement($xml);
+$xml = $xml->ownerDocument->saveXML($xml);
+
+$metaArray20 = array(
+	'AssertionConsumerService' => SimpleSAML_Module::getModuleURL('saml/sp/saml2-acs.php/' . $sourceId),
+	'SingleLogoutService' => SimpleSAML_Module::getModuleURL('saml/sp/saml2-acs.php/' . $sourceId),
+);
+if ($certData !== NULL) {
+	$metaArray20['certData'] = $certData;
+}
 
 if (array_key_exists('output', $_REQUEST) && $_REQUEST['output'] == 'xhtml') {