aboutsummaryrefslogtreecommitdiff
path: root/id/server/modules/moa-id-module-eIDAS
diff options
context:
space:
mode:
authorThomas Lenz <tlenz@iaik.tugraz.at>2017-02-03 08:51:45 +0100
committerThomas Lenz <tlenz@iaik.tugraz.at>2017-02-03 08:51:45 +0100
commit2b68b287aa55dc48e9f3a01bd42d6099bbe1deb2 (patch)
tree28e34446dc263144a09441120b0483e50e8e95b2 /id/server/modules/moa-id-module-eIDAS
parent3573f8ea5a4b269834723da4708bf0bace50fa65 (diff)
parente25d9bfa5fb81fd275706fb7cbee21fe5add5b19 (diff)
downloadmoa-id-spss-2b68b287aa55dc48e9f3a01bd42d6099bbe1deb2.tar.gz
moa-id-spss-2b68b287aa55dc48e9f3a01bd42d6099bbe1deb2.tar.bz2
moa-id-spss-2b68b287aa55dc48e9f3a01bd42d6099bbe1deb2.zip
Merge branch 'eIDAS_node_implementation' into development_preview
Diffstat (limited to 'id/server/modules/moa-id-module-eIDAS')
-rw-r--r--id/server/modules/moa-id-module-eIDAS/pom.xml16
-rw-r--r--id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/ModifiedEncryptionSW.java8
-rw-r--r--id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAProtocolEngine.java68
-rw-r--r--id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/GenerateAuthnRequestTask.java11
-rw-r--r--id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAProtocolEngineFactory.java17
-rw-r--r--id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAeIDASMetadataGenerator.java24
-rw-r--r--id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASProtocol.java6
-rw-r--r--id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EidasMetaDataRequest.java16
-rw-r--r--id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/eIDASAuthenticationRequest.java6
-rw-r--r--id/server/modules/moa-id-module-eIDAS/src/main/resources/resources/templates/eidas_postbinding_template.vm2
10 files changed, 146 insertions, 28 deletions
diff --git a/id/server/modules/moa-id-module-eIDAS/pom.xml b/id/server/modules/moa-id-module-eIDAS/pom.xml
index 174ce40cb..55d02e82a 100644
--- a/id/server/modules/moa-id-module-eIDAS/pom.xml
+++ b/id/server/modules/moa-id-module-eIDAS/pom.xml
@@ -12,11 +12,11 @@
<properties>
<repositoryPath>${basedir}/../../../../repository</repositoryPath>
- <eidas-commons.version>1.1.0</eidas-commons.version>
- <eidas-light-commons.version>1.1.0</eidas-light-commons.version>
- <eidas-saml-engine.version>1.1.0</eidas-saml-engine.version>
- <eidas-encryption.version>1.1.0</eidas-encryption.version>
- <eidas-configmodule.version>1.1.0</eidas-configmodule.version>
+ <eidas-commons.version>1.2.0</eidas-commons.version>
+ <eidas-light-commons.version>1.2.0</eidas-light-commons.version>
+ <eidas-saml-engine.version>1.2.0</eidas-saml-engine.version>
+ <eidas-encryption.version>1.2.0</eidas-encryption.version>
+ <eidas-configmodule.version>1.2.0</eidas-configmodule.version>
</properties>
@@ -166,6 +166,12 @@
<!-- <scope>provided</scope> -->
</dependency>
+ <dependency>
+ <groupId>com.ibm.icu</groupId>
+ <artifactId>icu4j</artifactId>
+ <version>58.2</version>
+ </dependency>
+
</dependencies>
diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/ModifiedEncryptionSW.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/ModifiedEncryptionSW.java
index 9ad5f0db3..de4f3fc9c 100644
--- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/ModifiedEncryptionSW.java
+++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/ModifiedEncryptionSW.java
@@ -90,17 +90,21 @@ public class ModifiedEncryptionSW extends KeyStoreSamlEngineEncryption {
*/
@Override
public boolean isEncryptionEnabled(String countryCode) {
- // - encrypt if so configured
+ //encryption is enabled by default in MOA-ID configuration object
try {
AuthConfiguration moaconfig = AuthConfigurationProviderFactory.getInstance();
Boolean useEncryption = moaconfig.getStorkConfig().getCPEPS(countryCode).isXMLSignatureSupported();
- Logger.info(useEncryption ? "using encryption" : "do not use encrpytion");
+ String logResult = useEncryption ? " using encryption" : " do not use encrpytion";
+ Logger.debug("eIDAS respone for country " + countryCode + logResult);
return useEncryption;
+
} catch(NullPointerException | ConfigurationException e) {
Logger.warn("failed to gather information about encryption for countryCode " + countryCode + " - thus, enabling encryption");
if(Logger.isDebugEnabled())
e.printStackTrace();
return true;
+
}
+
}
}
diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAProtocolEngine.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAProtocolEngine.java
new file mode 100644
index 000000000..d8fcd1694
--- /dev/null
+++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAProtocolEngine.java
@@ -0,0 +1,68 @@
+package at.gv.egovernment.moa.id.auth.modules.eidas.engine;
+
+import java.security.cert.X509Certificate;
+
+import org.apache.commons.lang3.StringUtils;
+import org.opensaml.saml2.core.Response;
+
+import at.gv.egovernment.moa.logging.Logger;
+import eu.eidas.auth.commons.EidasErrorKey;
+import eu.eidas.auth.commons.protocol.IAuthenticationRequest;
+import eu.eidas.auth.engine.ProtocolEngine;
+import eu.eidas.auth.engine.configuration.ProtocolConfigurationAccessor;
+import eu.eidas.auth.engine.xml.opensaml.SAMLEngineUtils;
+import eu.eidas.engine.exceptions.EIDASSAMLEngineException;
+
+public class MOAProtocolEngine extends ProtocolEngine {
+
+ public MOAProtocolEngine(ProtocolConfigurationAccessor configurationAccessor) {
+ super(configurationAccessor);
+
+ }
+
+// @Override
+// protected X509Certificate getEncryptionCertificate(String requestIssuer,
+// String destinationCountryCode) throws EIDASSAMLEngineException {
+// if ((StringUtils.isNotBlank(destinationCountryCode)) && (null != getProtocolEncrypter())
+// && (getProtocolEncrypter().isEncryptionEnabled(destinationCountryCode))) {
+// X509Certificate encryptionCertificate = getProtocolProcessor().getEncryptionCertificate(requestIssuer);
+//
+// if (null == encryptionCertificate) {
+// return getProtocolEncrypter().getEncryptionCertificate(destinationCountryCode);
+//
+// }
+// return encryptionCertificate;
+// }
+// return null;
+// }
+//
+// @Override
+// protected Response signResponse(IAuthenticationRequest request, Response response)
+// throws EIDASSAMLEngineException {
+// Response responseToSign = response;
+//
+// if ((null != getProtocolEncrypter()) && (!(SAMLEngineUtils.isErrorSamlResponse(responseToSign)))) {
+// X509Certificate destinationCertificate = getEncryptionCertificate(request.getIssuer(),
+// request.getOriginCountryCode());
+//
+// if (null != destinationCertificate) {
+// responseToSign = getProtocolEncrypter().encryptSamlResponse(responseToSign, destinationCertificate);
+//
+// } else if (getProtocolEncrypter().isEncryptionEnabled(request.getOriginCountryCode())) {
+//// Logger.error(SAML_EXCHANGE,
+//// "BUSINESS EXCEPTION : encryption cannot be performed, no matching certificate for issuer="
+//// + request.getIssuer() + " and country=" + request.getOriginCountryCode());
+//
+// throw new EIDASSAMLEngineException(EidasErrorKey.SAML_ENGINE_INVALID_CERTIFICATE.errorCode(),
+// EidasErrorKey.SAML_ENGINE_INVALID_CERTIFICATE.errorMessage());
+// }
+//
+// } else if (!(SAMLEngineUtils.isErrorSamlResponse(responseToSign))) {
+// checkSendingUnencryptedResponsesAllowed();
+//
+// }
+//
+// Logger.debug("Signing SAML Response.");
+// return ((Response) getSigner().sign(responseToSign));
+// }
+}
diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/GenerateAuthnRequestTask.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/GenerateAuthnRequestTask.java
index a9c4d5d3a..0eb067c5a 100644
--- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/GenerateAuthnRequestTask.java
+++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/GenerateAuthnRequestTask.java
@@ -211,8 +211,13 @@ public class GenerateAuthnRequestTask extends AbstractAuthServletTask {
authnRequestBuilder.nameIdFormat(Constants.eIDAS_REQ_NAMEID_FORMAT);
- //set minimum required eIDAS LoA from OA config
- authnRequestBuilder.levelOfAssurance(LevelOfAssurance.fromString(oaConfig.getQaaLevel()));
+ //set minimum required eIDAS LoA from OA config
+ String LoA = oaConfig.getQaaLevel();
+ if (MiscUtil.isNotEmpty(LoA))
+ authnRequestBuilder.levelOfAssurance(LevelOfAssurance.fromString(oaConfig.getQaaLevel()));
+ else
+ authnRequestBuilder.levelOfAssurance(LevelOfAssurance.HIGH);
+
authnRequestBuilder.levelOfAssuranceComparison(LevelOfAssuranceComparison.MINIMUM);
//set correct SPType for this online application
@@ -234,7 +239,7 @@ public class GenerateAuthnRequestTask extends AbstractAuthServletTask {
IRequestMessage authnRequest = engine.generateRequestMessage(authnRequestBuilder.build(), issur);
-
+
//encode AuthnRequest
byte[] token = authnRequest.getMessageBytes();
String SAMLRequest = EidasStringUtil.encodeToBase64(token);
diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAProtocolEngineFactory.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAProtocolEngineFactory.java
index f29d2bb65..47cdb4ade 100644
--- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAProtocolEngineFactory.java
+++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAProtocolEngineFactory.java
@@ -95,5 +95,22 @@ public class MOAProtocolEngineFactory extends ProtocolEngineFactory {
}
+// public static ProtocolEngineI createProtocolEngine(String instanceName,
+// ProtocolEngineConfigurationFactory protocolEngineConfigurationFactory,
+// ProtocolProcessorI protocolProcessor, SamlEngineClock samlEngineClock)
+// throws SamlEngineConfigurationException {
+//
+// ProtocolEngineConfiguration preConfiguration = protocolEngineConfigurationFactory
+// .getConfiguration(instanceName);
+//
+// protocolProcessor.configure();
+//
+// ProtocolEngineConfiguration configuration = ProtocolEngineConfiguration.builder(preConfiguration)
+// .protocolProcessor(protocolProcessor).clock(samlEngineClock).build();
+//
+// ProtocolEngineI samlEngine = new MOAProtocolEngine(new FixedProtocolConfigurationAccessor(configuration));
+//
+// return samlEngine;
+// }
}
diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAeIDASMetadataGenerator.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAeIDASMetadataGenerator.java
index dd14972e3..171d5c8e2 100644
--- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAeIDASMetadataGenerator.java
+++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAeIDASMetadataGenerator.java
@@ -210,10 +210,15 @@ public class MOAeIDASMetadataGenerator extends MetadataGenerator {
addAssertionConsumerService();
}
fillNameIDFormat(spSSODescriptor);
- if (params.getSpEngine() != null) {
- ProtocolEngineI spEngine = params.getSpEngine();
- ((MetadataSignerI) spEngine.getSigner()).signMetadata(spSSODescriptor);
- }
+
+ /**FIXME:
+ * Double signing of SPSSODescribtor is not required
+ */
+// if (params.getSpEngine() != null) {
+// ProtocolEngineI spEngine = params.getSpEngine();
+// ((MetadataSignerI) spEngine.getSigner()).signMetadata(spSSODescriptor);
+// }
+
entityDescriptor.getRoleDescriptors().add(spSSODescriptor);
}
@@ -266,6 +271,8 @@ public class MOAeIDASMetadataGenerator extends MetadataGenerator {
}
idpSSODescriptor.addSupportedProtocol(params.getIdpSamlProtocol());
fillNameIDFormat(idpSSODescriptor);
+
+
if (params.getIdpEngine() != null) {
if (params.getIdpEngine().getProtocolProcessor() != null
&& params.getIdpEngine().getProtocolProcessor().getFormat() == SAMLExtensionFormat.EIDAS10) {
@@ -277,8 +284,13 @@ public class MOAeIDASMetadataGenerator extends MetadataGenerator {
*/
generateSupportedAttributes(idpSSODescriptor, getAllSupportedAttributes());
}
- ProtocolEngineI idpEngine = params.getIdpEngine();
- ((MetadataSignerI) idpEngine.getSigner()).signMetadata(idpSSODescriptor);
+
+
+ /**FIXME:
+ * Double signing of IDPSSODescribtor is not required
+ */
+// ProtocolEngineI idpEngine = params.getIdpEngine();
+// ((MetadataSignerI) idpEngine.getSigner()).signMetadata(idpSSODescriptor);
}
idpSSODescriptor.getSingleSignOnServices().addAll(buildSingleSignOnServicesBindingLocations());
diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASProtocol.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASProtocol.java
index 13e64cdd0..aefae939b 100644
--- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASProtocol.java
+++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASProtocol.java
@@ -36,7 +36,6 @@ import org.opensaml.saml2.core.StatusCode;
import org.opensaml.saml2.metadata.AssertionConsumerService;
import org.opensaml.saml2.metadata.EntityDescriptor;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@@ -51,6 +50,7 @@ import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.EIDASAuthnRequestV
import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.EIDASException;
import at.gv.egovernment.moa.id.auth.modules.eidas.utils.SAMLEngineUtils;
import at.gv.egovernment.moa.id.commons.MOAIDAuthConstants;
+import at.gv.egovernment.moa.id.commons.MOAIDConstants;
import at.gv.egovernment.moa.id.commons.api.IOAAuthParameters;
import at.gv.egovernment.moa.id.commons.api.IRequest;
import at.gv.egovernment.moa.id.commons.api.exceptions.MOAIDException;
@@ -367,7 +367,7 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController {
String token = EidasStringUtil.encodeToBase64(eIDASRespMsg.getMessageBytes());
VelocityEngine velocityEngine = VelocityProvider.getClassPathVelocityEngine();
- Template template = velocityEngine.getTemplate("/resources/templates/stork2_postbinding_template.html");
+ Template template = velocityEngine.getTemplate("/resources/templates/eidas_postbinding_template.vm");
VelocityContext context = new VelocityContext();
context.put("RelayState", eidasReq.getRemoteRelayState());
@@ -387,7 +387,7 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController {
Logger.trace("Sending html content : " + new String(writer.getBuffer()));
byte[] content = writer.getBuffer().toString().getBytes("UTF-8");
- response.setContentType(MediaType.TEXT_HTML.getType());
+ response.setContentType(MOAIDConstants.DEFAULT_CONTENT_TYPE_HTML_UTF8);
response.setContentLength(content.length);
response.getOutputStream().write(content);
diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EidasMetaDataRequest.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EidasMetaDataRequest.java
index 174fa2c17..df96bef12 100644
--- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EidasMetaDataRequest.java
+++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EidasMetaDataRequest.java
@@ -72,13 +72,19 @@ public class EidasMetaDataRequest implements IAction {
String metadata_url = pubURLPrefix + Constants.eIDAS_HTTP_ENDPOINT_METADATA;
String sp_return_url = pubURLPrefix + Constants.eIDAS_HTTP_ENDPOINT_SP_POST;
+ //generate eIDAS metadata
String metaData = generateMetadata(req, metadata_url, sp_return_url);
-
+
+ //write content to response
+ byte[] content = metaData.getBytes("UTF-8");
+ httpResp.setStatus(HttpServletResponse.SC_OK);
+ httpResp.setContentType(MediaType.APPLICATION_XML.toString());
+ httpResp.setContentLength(content.length);
+ httpResp.getOutputStream().write(content);
+
+ //write log if required
Logger.trace(metaData);
-
- httpResp.setContentType(MediaType.APPLICATION_XML.getType());
- httpResp.getWriter().print(metaData);
- httpResp.flushBuffer();
+
} catch (Exception e) {
Logger.error("eIDAS Metadata generation FAILED.", e);
throw new MOAIDException("eIDAS.05", new Object[]{e.getMessage()}, e);
diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/eIDASAuthenticationRequest.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/eIDASAuthenticationRequest.java
index 22ac37604..97241af6a 100644
--- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/eIDASAuthenticationRequest.java
+++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/eIDASAuthenticationRequest.java
@@ -34,7 +34,6 @@ import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.opensaml.saml2.core.StatusCode;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import com.google.common.collect.ImmutableSet;
@@ -44,6 +43,7 @@ import at.gv.egovernment.moa.id.auth.frontend.velocity.VelocityProvider;
import at.gv.egovernment.moa.id.auth.modules.eidas.Constants;
import at.gv.egovernment.moa.id.auth.modules.eidas.engine.MOAeIDASChainingMetadataProvider;
import at.gv.egovernment.moa.id.auth.modules.eidas.utils.SimpleEidasAttributeGenerator;
+import at.gv.egovernment.moa.id.commons.MOAIDConstants;
import at.gv.egovernment.moa.id.commons.api.IRequest;
import at.gv.egovernment.moa.id.commons.api.exceptions.MOAIDException;
import at.gv.egovernment.moa.id.data.IAuthData;
@@ -233,7 +233,7 @@ public class eIDASAuthenticationRequest implements IAction {
// send the response
try {
VelocityEngine velocityEngine = VelocityProvider.getClassPathVelocityEngine();
- Template template = velocityEngine.getTemplate("/resources/templates/stork2_postbinding_template.html");
+ Template template = velocityEngine.getTemplate("/resources/templates/eidas_postbinding_template.vm");
VelocityContext context = new VelocityContext();
context.put("RelayState", eidasRequest.getRemoteRelayState());
@@ -253,7 +253,7 @@ public class eIDASAuthenticationRequest implements IAction {
Logger.trace("Sending html content : " + new String(writer.getBuffer()));
byte[] content = writer.getBuffer().toString().getBytes("UTF-8");
- httpResp.setContentType(MediaType.TEXT_HTML.getType());
+ httpResp.setContentType(MOAIDConstants.DEFAULT_CONTENT_TYPE_HTML_UTF8);
httpResp.setContentLength(content.length);
httpResp.getOutputStream().write(content);
diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/resources/resources/templates/eidas_postbinding_template.vm b/id/server/modules/moa-id-module-eIDAS/src/main/resources/resources/templates/eidas_postbinding_template.vm
index 3bd225b00..0535d48b6 100644
--- a/id/server/modules/moa-id-module-eIDAS/src/main/resources/resources/templates/eidas_postbinding_template.vm
+++ b/id/server/modules/moa-id-module-eIDAS/src/main/resources/resources/templates/eidas_postbinding_template.vm
@@ -7,7 +7,7 @@
## SAMLRequest - String - the Base64 encoded SAML Request
## SAMLResponse - String - the Base64 encoded SAML Response
## Contains target attribute to delegate PEPS authentication out of iFrame
-
+<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>