package at.gv.egovernment.moa.id.auth.modules.sl20_auth.tasks; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.net.ssl.SSLSocketFactory; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.utils.URIBuilder; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.message.BasicNameValuePair; import org.jose4j.base64url.Base64Url; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.google.gson.JsonObject; import at.gv.egiz.eaaf.core.api.idp.ISPConfiguration; 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.KeyValueUtils; import at.gv.egiz.eaaf.core.impl.utils.TransactionIDUtils; import at.gv.egiz.eaaf.modules.pvp2.impl.utils.SAML2Utils; 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.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.SL20HttpBindingUtils; 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.AuthConfiguration; import at.gv.egovernment.moa.id.commons.api.exceptions.MOAIDException; import at.gv.egovernment.moa.id.commons.config.MOAIDConfigurationConstants; import at.gv.egovernment.moa.id.commons.utils.HttpClientWithProxySupport; import at.gv.egovernment.moa.id.util.SSLUtils; import at.gv.egovernment.moa.util.MiscUtil; import at.gv.egovernment.moaspss.logging.Logger; @Component("CreateQualeIDRequestTask") public class CreateQualeIDRequestTask extends AbstractAuthServletTask { @Autowired(required=true) private IJOSETools joseTools; @Autowired private AuthConfiguration moaAuthConfig; @Override public void execute(ExecutionContext executionContext, HttpServletRequest request, HttpServletResponse response) throws TaskExecutionException { Logger.debug("Starting SL2.0 authentication process .... "); revisionsLogger.logEvent(pendingReq, MOAIDEventConstants.AUTHPROCESS_BKUTYPE_SELECTED, "sl20auth"); try { //get service-provider configuration ISPConfiguration oaConfig = pendingReq.getServiceProviderConfiguration(); //get basic configuration parameters String vdaQualeIDUrl = extractVDAURLForSpecificOA(oaConfig, executionContext); if (MiscUtil.isEmpty(vdaQualeIDUrl)) { Logger.error("NO VDA URL for qualified eID (" + Constants.CONFIG_PROP_VDA_ENDPOINT_QUALeID_DEFAULT + ")"); throw new SL20Exception("sl20.03", new Object[]{"NO VDA URL for qualified eID"}); } revisionsLogger.logEvent(pendingReq, MOAIDEventConstants.AUTHPROCESS_BKU_URL, vdaQualeIDUrl); String authBlockId = authConfig.getBasicConfiguration(Constants.CONFIG_PROP_VDA_AUTHBLOCK_ID); if (MiscUtil.isEmpty(authBlockId)) { Logger.error("NO AuthBlock Template identifier for qualified eID (" + Constants.CONFIG_PROP_VDA_AUTHBLOCK_ID + ")"); throw new SL20Exception("sl20.03", new Object[]{"NO AuthBlock Template identifier for qualified eID"}); } //build DataURL for qualified eID response String dataURL = new DataURLBuilder().buildDataURL( pendingReq.getAuthURL(), Constants.HTTP_ENDPOINT_DATAURL, pendingReq.getPendingRequestId()); //build qualifiedeID command Map qualifiedeIDParams = new HashMap(); qualifiedeIDParams.put(SL20Constants.SL20_COMMAND_PARAM_EID_ATTRIBUTES_SPUNIQUEID, oaConfig.getUniqueIdentifier()); qualifiedeIDParams.put(SL20Constants.SL20_COMMAND_PARAM_EID_ATTRIBUTES_SPFRIENDLYNAME, oaConfig.getFriendlyName()); qualifiedeIDParams.put(SL20Constants.SL20_COMMAND_PARAM_EID_ATTRIBUTES_SPCOUNTRYCODE, "AT"); //qualifiedeIDParams.put(SL20Constants.SL20_COMMAND_PARAM_EID_ATTRIBUTES_MANDATEREFVALUE, UUID.randomUUID().toString()); X509Certificate encCert = null; if (authConfig.getBasicMOAIDConfigurationBoolean(Constants.CONFIG_PROP_ENABLE_EID_ENCRYPTION, true)) encCert = joseTools.getEncryptionCertificate(); else Logger.info("eID data encryption is disabled by configuration"); JsonObject qualeIDCommandParams = SL20JSONBuilderUtils.createQualifiedeIDCommandParameters( authBlockId, dataURL, qualifiedeIDParams, encCert ); //String qualeIDReqId = UUID.randomUUID().toString(); String qualeIDReqId = SAML2Utils.getSecureIdentifier(); String signedQualeIDCommand = SL20JSONBuilderUtils.createSignedCommand(SL20Constants.SL20_COMMAND_IDENTIFIER_QUALIFIEDEID, qualeIDCommandParams, joseTools); JsonObject sl20Req = SL20JSONBuilderUtils.createGenericRequest(qualeIDReqId, null, null, signedQualeIDCommand); //open http client SSLSocketFactory sslFactory = SSLUtils.getSSLSocketFactory( moaAuthConfig, vdaQualeIDUrl); CloseableHttpClient httpClient = HttpClientWithProxySupport.getHttpClient( sslFactory, moaAuthConfig.getBasicMOAIDConfigurationBoolean(AuthConfiguration.PROP_KEY_OVS_SSL_HOSTNAME_VALIDATION, true)); //build http POST request HttpPost httpReq = new HttpPost(new URIBuilder(vdaQualeIDUrl).build()); List parameters = new ArrayList();; parameters.add(new BasicNameValuePair(SL20Constants.PARAM_SL20_REQ_COMMAND_PARAM, Base64Url.encode(sl20Req.toString().getBytes()))); httpReq.setEntity(new UrlEncodedFormEntity(parameters )); //build http GET request // URIBuilder sl20ReqUri = new URIBuilder(vdaQualeIDUrl); // sl20ReqUri.addParameter(SL20Constants.PARAM_SL20_REQ_COMMAND_PARAM, Base64Url.encode(sl20Req.toString().getBytes())); // HttpGet httpReq = new HttpGet(sl20ReqUri.build()); //set native client header httpReq.addHeader(SL20Constants.HTTP_HEADER_SL20_CLIENT_TYPE, SL20Constants.HTTP_HEADER_VALUE_NATIVE); Logger.trace("Request VDA via SL20 with: " + Base64Url.encode(sl20Req.toString().getBytes())); //request VDA HttpResponse httpResp = httpClient.execute(httpReq); //parse response Logger.info("Receive response from VDA ... "); JsonObject sl20Resp = SL20JSONExtractorUtils.getSL20ContainerFromResponse(httpResp); VerificationResult respPayloadContainer = SL20JSONExtractorUtils.extractSL20PayLoad(sl20Resp, null, false); if (respPayloadContainer.isValidSigned() == null) { Logger.debug("Receive unsigned payLoad from VDA"); } JsonObject respPayload = respPayloadContainer.getPayload(); if (respPayload.get(SL20Constants.SL20_COMMAND_CONTAINER_NAME).getAsString() .equals(SL20Constants.SL20_COMMAND_IDENTIFIER_REDIRECT)) { Logger.debug("Find 'redirect' command in VDA response ... "); JsonObject params = SL20JSONExtractorUtils.getJSONObjectValue(respPayload, SL20Constants.SL20_COMMAND_CONTAINER_PARAMS, true); String redirectURL = SL20JSONExtractorUtils.getStringValue(params, SL20Constants.SL20_COMMAND_PARAM_GENERAL_REDIRECT_URL, true); JsonObject command = SL20JSONExtractorUtils.getJSONObjectValue(params, SL20Constants.SL20_COMMAND_PARAM_GENERAL_REDIRECT_COMMAND, false); String signedCommand = SL20JSONExtractorUtils.getStringValue(params, SL20Constants.SL20_COMMAND_PARAM_GENERAL_REDIRECT_SIGNEDCOMMAND, false); //create forward SL2.0 command JsonObject sl20Forward = sl20Resp.deepCopy().getAsJsonObject(); SL20JSONBuilderUtils.addOnlyOnceOfTwo(sl20Forward, SL20Constants.SL20_PAYLOAD, SL20Constants.SL20_SIGNEDPAYLOAD, command, signedCommand); //store pending request pendingReq.setRawDataToTransaction(Constants.PENDING_REQ_STORAGE_PREFIX + SL20Constants.SL20_REQID, qualeIDReqId); requestStoreage.storePendingRequest(pendingReq); //forward SL2.0 command //TODO: maybe add SL2ClientType Header from execution context SL20HttpBindingUtils.writeIntoResponse(request, response, sl20Forward, redirectURL); } else if (respPayload.get(SL20Constants.SL20_COMMAND_CONTAINER_NAME).getAsString() .equals(SL20Constants.SL20_COMMAND_IDENTIFIER_ERROR)) { JsonObject result = SL20JSONExtractorUtils.getJSONObjectValue(respPayload, SL20Constants.SL20_COMMAND_CONTAINER_RESULT, false); if (result == null) result = SL20JSONExtractorUtils.getJSONObjectValue(respPayload, SL20Constants.SL20_COMMAND_CONTAINER_PARAMS, false); String errorCode = SL20JSONExtractorUtils.getStringValue(result, SL20Constants.SL20_COMMAND_PARAM_GENERAL_RESPONSE_ERRORCODE, true); String errorMsg = SL20JSONExtractorUtils.getStringValue(result, SL20Constants.SL20_COMMAND_PARAM_GENERAL_RESPONSE_ERRORMESSAGE, true); Logger.info("Receive SL2.0 error. Code:" + errorCode + " Msg:" + errorMsg); throw new SL20Exception("sl20.08", new Object[]{errorCode, errorMsg}); } else { //TODO: update to add error handling Logger.warn("Received an unrecognized command: " + respPayload.get(SL20Constants.SL20_COMMAND_CONTAINER_NAME).getAsString()); throw new SLCommandoParserException("Received an unrecognized command: \" + respPayload.get(SL20Constants.SL20_COMMAND_CONTAINER_NAME).getAsString()"); } } catch (MOAIDException e) { throw new TaskExecutionException(pendingReq, "SL2.0 Authentication FAILED. Msg: " + e.getMessage(), e); } catch (Exception e) { Logger.warn("SL2.0 Authentication FAILED with a generic error.", e); throw new TaskExecutionException(pendingReq, e.getMessage(), e); } finally { TransactionIDUtils.removeTransactionId(); TransactionIDUtils.removeSessionId(); } } private String extractVDAURLForSpecificOA(ISPConfiguration oaConfig, ExecutionContext executionContext) { String spSpecificVDAEndpoints = oaConfig.getConfigurationValue(MOAIDConfigurationConstants.SERVICE_AUTH_SL20_ENDPOINTS); Map endPointMap = authConfig.getBasicMOAIDConfigurationWithPrefix(Constants.CONFIG_PROP_VDA_ENDPOINT_QUALeID_LIST); if (MiscUtil.isNotEmpty(spSpecificVDAEndpoints)) { endPointMap.putAll(KeyValueUtils.convertListToMap( KeyValueUtils.getListOfCSVValues( KeyValueUtils.normalizeCSVValueString(spSpecificVDAEndpoints)))); Logger.debug("Find OA specific SL2.0 endpoints. Updating endPoint list ... "); } Logger.trace("Find #" + endPointMap.size() + " SL2.0 endpoints ... "); //selection based on request Header String sl20VDATypeHeader = (String) executionContext.get(SL20Constants.HTTP_HEADER_SL20_VDA_TYPE.toLowerCase()); if (MiscUtil.isNotEmpty(sl20VDATypeHeader)) { String vdaURL = endPointMap.get(sl20VDATypeHeader); if (MiscUtil.isNotEmpty(vdaURL)) return vdaURL.trim(); else Logger.info("Can NOT find VDA with Id: " + sl20VDATypeHeader + ". Use default VDA"); } Logger.info("NO SP specific VDA endpoint found. Use default VDA"); return endPointMap.getOrDefault(Constants.CONFIG_PROP_VDA_ENDPOINT_QUALeID_DEFAULT, Constants.CONFIG_PROP_VDA_ENDPOINT_QUALeID + Constants.CONFIG_PROP_VDA_ENDPOINT_QUALeID_DEFAULT); } }