aboutsummaryrefslogtreecommitdiff
path: root/id/server/modules/moa-id-module-sl20_authentication/src/main/java/at/gv/egovernment/moa/id/auth/modules/sl20_auth/tasks/ReceiveQualeIDTask.java
diff options
context:
space:
mode:
Diffstat (limited to 'id/server/modules/moa-id-module-sl20_authentication/src/main/java/at/gv/egovernment/moa/id/auth/modules/sl20_auth/tasks/ReceiveQualeIDTask.java')
-rw-r--r--id/server/modules/moa-id-module-sl20_authentication/src/main/java/at/gv/egovernment/moa/id/auth/modules/sl20_auth/tasks/ReceiveQualeIDTask.java323
1 files changed, 323 insertions, 0 deletions
diff --git a/id/server/modules/moa-id-module-sl20_authentication/src/main/java/at/gv/egovernment/moa/id/auth/modules/sl20_auth/tasks/ReceiveQualeIDTask.java b/id/server/modules/moa-id-module-sl20_authentication/src/main/java/at/gv/egovernment/moa/id/auth/modules/sl20_auth/tasks/ReceiveQualeIDTask.java
new file mode 100644
index 000000000..fc386b796
--- /dev/null
+++ b/id/server/modules/moa-id-module-sl20_authentication/src/main/java/at/gv/egovernment/moa/id/auth/modules/sl20_auth/tasks/ReceiveQualeIDTask.java
@@ -0,0 +1,323 @@
+package at.gv.egovernment.moa.id.auth.modules.sl20_auth.tasks;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.security.cert.X509Certificate;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.entity.ContentType;
+import org.jose4j.base64url.Base64Url;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonSyntaxException;
+
+import at.gv.egiz.eaaf.core.api.data.EAAFConstants;
+import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext;
+import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException;
+import at.gv.egiz.eaaf.core.impl.idp.auth.modules.AbstractAuthServletTask;
+import at.gv.egiz.eaaf.core.impl.utils.DataURLBuilder;
+import at.gv.egiz.eaaf.core.impl.utils.StreamUtils;
+import at.gv.egiz.eaaf.core.impl.utils.TransactionIDUtils;
+import at.gv.egovernment.moa.id.advancedlogging.MOAIDEventConstants;
+import at.gv.egovernment.moa.id.auth.modules.sl20_auth.Constants;
+import at.gv.egovernment.moa.id.auth.modules.sl20_auth.data.VerificationResult;
+import at.gv.egovernment.moa.id.auth.modules.sl20_auth.exceptions.SL20Exception;
+import at.gv.egovernment.moa.id.auth.modules.sl20_auth.exceptions.SL20SecurityException;
+import at.gv.egovernment.moa.id.auth.modules.sl20_auth.exceptions.SLCommandoParserException;
+import at.gv.egovernment.moa.id.auth.modules.sl20_auth.sl20.IJOSETools;
+import at.gv.egovernment.moa.id.auth.modules.sl20_auth.sl20.SL20Constants;
+import at.gv.egovernment.moa.id.auth.modules.sl20_auth.sl20.SL20JSONBuilderUtils;
+import at.gv.egovernment.moa.id.auth.modules.sl20_auth.sl20.SL20JSONExtractorUtils;
+import at.gv.egovernment.moa.id.commons.api.exceptions.MOAIDException;
+import at.gv.egovernment.moa.util.MiscUtil;
+import at.gv.egovernment.moaspss.logging.Logger;
+
+
+@Component("ReceiveQualeIDTask")
+public class ReceiveQualeIDTask extends AbstractAuthServletTask {
+
+ @Autowired(required=true) private IJOSETools joseTools;
+
+ @Override
+ public void execute(ExecutionContext executionContext, HttpServletRequest request, HttpServletResponse response)
+ throws TaskExecutionException {
+ String sl20Result = null;
+
+ try {
+ Logger.debug("Receiving SL2.0 response process .... ");
+ JsonObject sl20ReqObj = null;
+ try {
+ //get SL2.0 command or result from HTTP request
+ Map<String, String> reqParams = getParameters(request);
+ sl20Result = reqParams.get(SL20Constants.PARAM_SL20_REQ_COMMAND_PARAM);
+
+ if (MiscUtil.isEmpty(sl20Result)) {
+ //Workaround for SIC Handy-Signature, because it sends result in InputStream
+ String isReqInput = StreamUtils.readStream(request.getInputStream(), "UTF-8");
+ if (MiscUtil.isNotEmpty(isReqInput)) {
+ Logger.info("Use SIC Handy-Signature work-around!");
+ sl20Result = isReqInput.substring("slcommand=".length());
+
+ } else {
+ Logger.info("NO SL2.0 commando or result FOUND.");
+ throw new SL20Exception("sl20.04", null);
+ }
+
+ }
+
+ Logger.trace("Received SL2.0 result: " + sl20Result);
+ revisionsLogger.logEvent(pendingReq, MOAIDEventConstants.AUTHPROCESS_BKU_DATAURL_IP, request.getRemoteAddr());
+
+ //parse SL2.0 command/result into JSON
+ try {
+ JsonParser jsonParser = new JsonParser();
+ JsonElement sl20Req = jsonParser.parse(Base64Url.decodeToUtf8String(sl20Result));
+ sl20ReqObj = sl20Req.getAsJsonObject();
+
+ } catch (JsonSyntaxException e) {
+ Logger.warn("SL2.0 command or result is NOT valid JSON.", e);
+ Logger.debug("SL2.0 msg: " + sl20Result);
+ throw new SL20Exception("sl20.02", new Object[]{"SL2.0 command or result is NOT valid JSON."}, e);
+
+ }
+
+ //validate reqId with inResponseTo
+ String sl20ReqId = pendingReq.getRawData(Constants.PENDING_REQ_STORAGE_PREFIX + SL20Constants.SL20_REQID, String.class);
+ String inRespTo = SL20JSONExtractorUtils.getStringValue(sl20ReqObj, SL20Constants.SL20_INRESPTO, true);
+ if (sl20ReqId == null || !sl20ReqId.equals(inRespTo)) {
+ Logger.info("SL20 'reqId': " + sl20ReqId + " does NOT match to 'inResponseTo':" + inRespTo);
+ throw new SL20SecurityException("SL20 'reqId': " + sl20ReqId + " does NOT match to 'inResponseTo':" + inRespTo);
+ }
+
+
+ //validate signature
+ VerificationResult payLoadContainer = SL20JSONExtractorUtils.extractSL20PayLoad(
+ sl20ReqObj, joseTools,
+ authConfig.getBasicMOAIDConfigurationBoolean(Constants.CONFIG_PROP_FORCE_EID_SIGNED_RESULT, true));
+
+ if ( (payLoadContainer.isValidSigned() == null || !payLoadContainer.isValidSigned())) {
+ if (authConfig.getBasicMOAIDConfigurationBoolean(Constants.CONFIG_PROP_FORCE_EID_SIGNED_RESULT, true)) {
+ Logger.info("SL20 result from VDA was not valid signed");
+ throw new SL20SecurityException(new Object[]{"Signature on SL20 result NOT valid."});
+
+ } else {
+ Logger.warn("SL20 result from VDA is NOT valid signed, but signatures-verification is DISABLED by configuration!");
+
+ }
+ }
+
+ /*TODO validate certificate by using MOA-SPSS
+ * currently, the certificate is validated in IJOSETools by using a pkcs12 or jks keystore
+ */
+ List<X509Certificate> sigCertChain = payLoadContainer.getCertChain();
+
+
+ //extract payloaf
+ JsonObject payLoad = payLoadContainer.getPayload();
+
+ //check response type
+ if (SL20JSONExtractorUtils.getStringValue(
+ payLoad, SL20Constants.SL20_COMMAND_CONTAINER_NAME, true)
+ .equals(SL20Constants.SL20_COMMAND_IDENTIFIER_QUALIFIEDEID)) {
+ Logger.debug("Find " + SL20Constants.SL20_COMMAND_IDENTIFIER_QUALIFIEDEID + " result .... ");
+
+ JsonElement qualeIDResult = SL20JSONExtractorUtils.extractSL20Result(
+ payLoad, joseTools,
+ authConfig.getBasicMOAIDConfigurationBoolean(Constants.CONFIG_PROP_FORCE_EID_ENCRYPTION, true));
+
+ //extract attributes from result
+ Map<String, String> eIDData = SL20JSONExtractorUtils.getMapOfStringElements(qualeIDResult);
+ String idlB64 = eIDData.get(SL20Constants.SL20_COMMAND_PARAM_EID_RESULT_IDL);
+ String authBlockB64 = eIDData.get(SL20Constants.SL20_COMMAND_PARAM_EID_RESULT_AUTHBLOCK);
+ String ccsURL = eIDData.get(SL20Constants.SL20_COMMAND_PARAM_EID_RESULT_CCSURL);
+ String LoA = eIDData.get(SL20Constants.SL20_COMMAND_PARAM_EID_RESULT_LOA);
+
+
+
+ if (MiscUtil.isEmpty(idlB64) || MiscUtil.isEmpty(authBlockB64)
+ || MiscUtil.isEmpty(LoA) || MiscUtil.isEmpty(ccsURL)) {
+ Logger.info("SL20 'qualifiedeID' result does NOT contain all required attributes.");
+ throw new SLCommandoParserException("SL20 'qualifiedeID' result does NOT contain all required attributes.");
+
+ }
+
+ //cache qualified eID data into pending request
+ pendingReq.setRawDataToTransaction(
+ Constants.PENDING_REQ_STORAGE_PREFIX + SL20Constants.SL20_COMMAND_PARAM_EID_RESULT_IDL,
+ idlB64);
+ pendingReq.setRawDataToTransaction(
+ Constants.PENDING_REQ_STORAGE_PREFIX + SL20Constants.SL20_COMMAND_PARAM_EID_RESULT_AUTHBLOCK,
+ authBlockB64);
+ pendingReq.setRawDataToTransaction(
+ Constants.PENDING_REQ_STORAGE_PREFIX + SL20Constants.SL20_COMMAND_PARAM_EID_RESULT_CCSURL,
+ ccsURL);
+ pendingReq.setRawDataToTransaction(
+ Constants.PENDING_REQ_STORAGE_PREFIX + SL20Constants.SL20_COMMAND_PARAM_EID_RESULT_LOA,
+ LoA);
+
+ } else {
+ Logger.info("SL20 response is NOT a " + SL20Constants.SL20_COMMAND_IDENTIFIER_QUALIFIEDEID + " result");
+ throw new SLCommandoParserException("SL20 response is NOT a " + SL20Constants.SL20_COMMAND_IDENTIFIER_QUALIFIEDEID + " result");
+ }
+
+
+ } catch (MOAIDException e) {
+ Logger.warn("SL2.0 processing error:", e);
+ if (sl20Result != null)
+ Logger.debug("Received SL2.0 result: " + sl20Result);
+ pendingReq.setRawDataToTransaction(
+ Constants.PENDING_REQ_STORAGE_PREFIX + SL20Constants.SL20_COMMAND_IDENTIFIER_ERROR,
+ new TaskExecutionException(pendingReq, "SL2.0 Authentication FAILED. Msg: " + e.getMessage(), e));
+
+ } catch (Exception e) {
+ Logger.warn("ERROR:", e);
+ Logger.warn("SL2.0 Authentication FAILED with a generic error.", e);
+ if (sl20Result != null)
+ Logger.debug("Received SL2.0 result: " + sl20Result);
+ pendingReq.setRawDataToTransaction(
+ Constants.PENDING_REQ_STORAGE_PREFIX + SL20Constants.SL20_COMMAND_IDENTIFIER_ERROR,
+ new TaskExecutionException(pendingReq, e.getMessage(), e));
+
+ } finally {
+ //store pending request
+ requestStoreage.storePendingRequest(pendingReq);
+
+ //write SL2.0 response
+ if (sl20ReqObj != null)
+ buildResponse(request, response, sl20ReqObj);
+ else
+ buildErrorResponse(request, response, "2000", "General transport Binding error");
+
+ }
+
+ } catch (Exception e) {
+ //write internal server errror 500 according to SL2.0 specification, chapter https transport binding
+ Logger.warn("Can NOT build SL2.0 response. Reason: " + e.getMessage(), e);
+ if (sl20Result != null)
+ Logger.debug("Received SL2.0 result: " + sl20Result);
+ try {
+ response.sendError(500, "Internal Server Error.");
+
+ } catch (IOException e1) {
+ Logger.error("Can NOT send error message. SOMETHING IS REALY WRONG!", e);
+
+ }
+
+ } finally {
+ TransactionIDUtils.removeTransactionId();
+ TransactionIDUtils.removeSessionId();
+
+ }
+ }
+
+ private void buildErrorResponse(HttpServletRequest request, HttpServletResponse response, String errorCode, String errorMsg) throws Exception {
+ JsonObject error = SL20JSONBuilderUtils.createErrorCommandResult(errorCode, errorMsg);
+ JsonObject respContainer = SL20JSONBuilderUtils.createGenericRequest(
+ UUID.randomUUID().toString(),
+ null,
+ error ,
+ null);
+
+ Logger.debug("Client request containts 'native client' header ... ");
+ Logger.trace("SL20 response to VDA: " + respContainer);
+ StringWriter writer = new StringWriter();
+ writer.write(respContainer.toString());
+ final byte[] content = writer.toString().getBytes("UTF-8");
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.setContentLength(content.length);
+ response.setContentType(ContentType.APPLICATION_JSON.toString());
+ response.getOutputStream().write(content);
+
+ }
+
+ private void buildResponse(HttpServletRequest request, HttpServletResponse response, JsonObject sl20ReqObj) throws IOException, SL20Exception {
+ //create response
+ Map<String, String> reqParameters = new HashMap<String, String>();
+ reqParameters.put(EAAFConstants.PARAM_HTTP_TARGET_PENDINGREQUESTID, pendingReq.getPendingRequestId());
+ JsonObject callReqParams = SL20JSONBuilderUtils.createCallCommandParameters(
+ new DataURLBuilder().buildDataURL(pendingReq.getAuthURL(), Constants.HTTP_ENDPOINT_RESUME, null),
+ SL20Constants.SL20_COMMAND_PARAM_GENERAL_CALL_METHOD_GET,
+ false,
+ reqParameters);
+ JsonObject callCommand = SL20JSONBuilderUtils.createCommand(SL20Constants.SL20_COMMAND_IDENTIFIER_CALL, callReqParams);
+
+ //build first redirect command for app
+ JsonObject redirectOneParams = SL20JSONBuilderUtils.createRedirectCommandParameters(
+ generateICPRedirectURLForDebugging(),
+ callCommand, null, true);
+ JsonObject redirectOneCommand = SL20JSONBuilderUtils.createCommand(SL20Constants.SL20_COMMAND_IDENTIFIER_REDIRECT, redirectOneParams);
+
+ //build second redirect command for IDP
+ JsonObject redirectTwoParams = SL20JSONBuilderUtils.createRedirectCommandParameters(
+ new DataURLBuilder().buildDataURL(pendingReq.getAuthURL(), Constants.HTTP_ENDPOINT_RESUME, null),
+ redirectOneCommand, null, true);
+ JsonObject redirectTwoCommand = SL20JSONBuilderUtils.createCommand(SL20Constants.SL20_COMMAND_IDENTIFIER_REDIRECT, redirectTwoParams);
+
+ //build generic SL2.0 response container
+ String transactionId = SL20JSONExtractorUtils.getStringValue(sl20ReqObj, SL20Constants.SL20_TRANSACTIONID, false);
+ JsonObject respContainer = SL20JSONBuilderUtils.createGenericRequest(
+ UUID.randomUUID().toString(),
+ transactionId,
+ redirectTwoCommand,
+ null);
+
+ //workaround for A-Trust
+ if (request.getHeader(SL20Constants.HTTP_HEADER_SL20_CLIENT_TYPE) != null &&
+ request.getHeader(SL20Constants.HTTP_HEADER_SL20_CLIENT_TYPE).equals(SL20Constants.HTTP_HEADER_VALUE_NATIVE)
+ || true) {
+ Logger.debug("Client request containts 'native client' header ... ");
+ Logger.trace("SL20 response to VDA: " + respContainer);
+ StringWriter writer = new StringWriter();
+ writer.write(respContainer.toString());
+ final byte[] content = writer.toString().getBytes("UTF-8");
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.setContentLength(content.length);
+ response.setContentType(ContentType.APPLICATION_JSON.toString());
+ response.getOutputStream().write(content);
+
+
+ } else {
+ Logger.info("SL2.0 DataURL communication needs http header: '" + SL20Constants.HTTP_HEADER_SL20_CLIENT_TYPE + "'");
+ throw new SL20Exception("sl20.06",
+ new Object[] {"SL2.0 DataURL communication needs http header: '" + SL20Constants.HTTP_HEADER_SL20_CLIENT_TYPE + "'"});
+
+ }
+ }
+
+ /**
+ * Generates a IPC redirect URL that is configured on IDP side
+ *
+ * @return IPC ReturnURL, or null if no URL is configured
+ */
+ private String generateICPRedirectURLForDebugging() {
+ final String PATTERN_PENDING_REQ_ID = "#PENDINGREQID#";
+
+ String ipcRedirectURLConfig = authConfig.getBasicConfiguration(Constants.CONFIG_PROP_IPC_RETURN_URL);
+ if (MiscUtil.isNotEmpty(ipcRedirectURLConfig)) {
+ if (ipcRedirectURLConfig.contains(PATTERN_PENDING_REQ_ID)) {
+ Logger.trace("Find 'pendingReqId' pattern in IPC redirect URL. Update url ... ");
+ ipcRedirectURLConfig = ipcRedirectURLConfig.replaceAll(
+ "#PENDINGREQID#",
+ EAAFConstants.PARAM_HTTP_TARGET_PENDINGREQUESTID + "=" + pendingReq.getPendingRequestId());
+
+ }
+
+ return ipcRedirectURLConfig;
+ }
+
+ return null;
+
+ }
+
+
+}