From 588b730eff977fa7fba62612ec7b4bb1bc56ebd8 Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Tue, 3 Oct 2017 16:17:17 +0200 Subject: update mobile-auth module for eGov KU 2017/18 --- .../moa-id-module-bkaMobilaAuthSAML2Test/pom.xml | 31 +++ .../bkamobileauthtests/BKAMobileAuthModule.java | 24 +- .../BKAMobileAuthSpringResourceProvider.java | 2 +- .../tasks/FirstBKAMobileAuthTask.java | 257 ++++++++++++++++++++- .../src/main/resources/BKAMobileAuth.process.xml | 6 +- 5 files changed, 309 insertions(+), 11 deletions(-) diff --git a/id/server/modules/moa-id-module-bkaMobilaAuthSAML2Test/pom.xml b/id/server/modules/moa-id-module-bkaMobilaAuthSAML2Test/pom.xml index 0db2b26a8..0207eb6c9 100644 --- a/id/server/modules/moa-id-module-bkaMobilaAuthSAML2Test/pom.xml +++ b/id/server/modules/moa-id-module-bkaMobilaAuthSAML2Test/pom.xml @@ -7,4 +7,35 @@ moa-id-module-bkaMobilaAuthSAML2Test BKA MobileAuth Test for SAML2 applications + + + + org.bouncycastle + bcprov-jdk15on + 1.52 + + + org.bouncycastle + bcpkix-jdk15on + 1.52 + + + + + com.googlecode.jsontoken + jsontoken + 1.1 + + + javax.servlet + servlet-api + + + google-collections + com.google.collections + + + + + \ No newline at end of file diff --git a/id/server/modules/moa-id-module-bkaMobilaAuthSAML2Test/src/main/java/at/gv/egovernment/moa/id/auth/modules/bkamobileauthtests/BKAMobileAuthModule.java b/id/server/modules/moa-id-module-bkaMobilaAuthSAML2Test/src/main/java/at/gv/egovernment/moa/id/auth/modules/bkamobileauthtests/BKAMobileAuthModule.java index 44554e21d..b31985542 100644 --- a/id/server/modules/moa-id-module-bkaMobilaAuthSAML2Test/src/main/java/at/gv/egovernment/moa/id/auth/modules/bkamobileauthtests/BKAMobileAuthModule.java +++ b/id/server/modules/moa-id-module-bkaMobilaAuthSAML2Test/src/main/java/at/gv/egovernment/moa/id/auth/modules/bkamobileauthtests/BKAMobileAuthModule.java @@ -30,9 +30,11 @@ import javax.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; import at.gv.egovernment.moa.id.auth.modules.AuthModule; +import at.gv.egovernment.moa.id.auth.modules.bkamobileauthtests.tasks.FirstBKAMobileAuthTask; import at.gv.egovernment.moa.id.commons.MOAIDAuthConstants; import at.gv.egovernment.moa.id.commons.api.AuthConfiguration; import at.gv.egovernment.moa.id.commons.utils.KeyValueUtils; +import at.gv.egovernment.moa.id.moduls.AuthenticationManager; import at.gv.egovernment.moa.id.process.api.ExecutionContext; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.MiscUtil; @@ -45,7 +47,8 @@ public class BKAMobileAuthModule implements AuthModule { private int priority = 1; - @Autowired protected AuthConfiguration authConfig; + @Autowired(required=true) protected AuthConfiguration authConfig; + @Autowired(required=true) private AuthenticationManager authManager; private List uniqueIDsDummyAuthEnabled = new ArrayList(); @@ -77,7 +80,10 @@ public class BKAMobileAuthModule implements AuthModule { for (String el : uniqueIDsDummyAuthEnabled) Logger.info(" EntityID: " + el); } - } + } + + //parameter to whiteList + authManager.addParameterNameToWhiteList(FirstBKAMobileAuthTask.REQ_PARAM_eID_BLOW); } /* (non-Javadoc) @@ -87,9 +93,17 @@ public class BKAMobileAuthModule implements AuthModule { public String selectProcess(ExecutionContext context) { String spEntityID = (String) context.get(MOAIDAuthConstants.PROCESSCONTEXT_UNIQUE_OA_IDENTFIER); if (MiscUtil.isNotEmpty(spEntityID)) { - if (uniqueIDsDummyAuthEnabled.contains(spEntityID)) - return "BKAMobileAuthentication"; - + if (uniqueIDsDummyAuthEnabled.contains(spEntityID)) { + String eIDBlob = (String)context.get(FirstBKAMobileAuthTask.REQ_PARAM_eID_BLOW); + if (eIDBlob != null && MiscUtil.isNotEmpty(eIDBlob.trim())) { + return "BKAMobileAuthentication"; + + } else { + Logger.debug("Dummy-auth are enabled for " + spEntityID + " but no '" + + FirstBKAMobileAuthTask.REQ_PARAM_eID_BLOW + "' req. parameter available."); + + } + } } return null; diff --git a/id/server/modules/moa-id-module-bkaMobilaAuthSAML2Test/src/main/java/at/gv/egovernment/moa/id/auth/modules/bkamobileauthtests/BKAMobileAuthSpringResourceProvider.java b/id/server/modules/moa-id-module-bkaMobilaAuthSAML2Test/src/main/java/at/gv/egovernment/moa/id/auth/modules/bkamobileauthtests/BKAMobileAuthSpringResourceProvider.java index 884129453..f8c4e8ce9 100644 --- a/id/server/modules/moa-id-module-bkaMobilaAuthSAML2Test/src/main/java/at/gv/egovernment/moa/id/auth/modules/bkamobileauthtests/BKAMobileAuthSpringResourceProvider.java +++ b/id/server/modules/moa-id-module-bkaMobilaAuthSAML2Test/src/main/java/at/gv/egovernment/moa/id/auth/modules/bkamobileauthtests/BKAMobileAuthSpringResourceProvider.java @@ -56,7 +56,7 @@ public class BKAMobileAuthSpringResourceProvider implements SpringResourceProvid */ @Override public String getName() { - return "BKA MobileAuth SAML2 Test"; + return "SSL Client-Certificate authentication"; } } diff --git a/id/server/modules/moa-id-module-bkaMobilaAuthSAML2Test/src/main/java/at/gv/egovernment/moa/id/auth/modules/bkamobileauthtests/tasks/FirstBKAMobileAuthTask.java b/id/server/modules/moa-id-module-bkaMobilaAuthSAML2Test/src/main/java/at/gv/egovernment/moa/id/auth/modules/bkamobileauthtests/tasks/FirstBKAMobileAuthTask.java index 66112edc5..d6e814a9d 100644 --- a/id/server/modules/moa-id-module-bkaMobilaAuthSAML2Test/src/main/java/at/gv/egovernment/moa/id/auth/modules/bkamobileauthtests/tasks/FirstBKAMobileAuthTask.java +++ b/id/server/modules/moa-id-module-bkaMobilaAuthSAML2Test/src/main/java/at/gv/egovernment/moa/id/auth/modules/bkamobileauthtests/tasks/FirstBKAMobileAuthTask.java @@ -22,16 +22,56 @@ */ package at.gv.egovernment.moa.id.auth.modules.bkamobileauthtests.tasks; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.SecretKeySpec; +import javax.security.cert.CertificateException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.bouncycastle.asn1.cms.CMSObjectIdentifiers; +import org.bouncycastle.cms.CMSSignedData; +import org.joda.time.DateTime; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonParser; + +import at.gv.egovernment.moa.id.auth.invoke.SignatureVerificationInvoker; import at.gv.egovernment.moa.id.auth.modules.AbstractAuthServletTask; import at.gv.egovernment.moa.id.auth.modules.TaskExecutionException; -import at.gv.egovernment.moa.id.auth.servlet.GeneralProcessEngineSignalController; +import at.gv.egovernment.moa.id.auth.parser.IdentityLinkAssertionParser; +import at.gv.egovernment.moa.id.commons.api.AuthConfiguration; +import at.gv.egovernment.moa.id.commons.api.IRequest; +import at.gv.egovernment.moa.id.commons.api.data.IAuthenticationSession; +import at.gv.egovernment.moa.id.commons.api.data.IIdentityLink; +import at.gv.egovernment.moa.id.commons.api.exceptions.MOAIDException; import at.gv.egovernment.moa.id.process.api.ExecutionContext; +import at.gv.egovernment.moa.id.protocols.pvp2x.PVPConstants; import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.spss.api.cmsverify.VerifyCMSSignatureRequest; +import at.gv.egovernment.moa.spss.api.cmsverify.VerifyCMSSignatureResponse; +import at.gv.egovernment.moa.spss.api.cmsverify.VerifyCMSSignatureResponseElement; +import at.gv.egovernment.moa.spss.api.common.SignerInfo; +import at.gv.egovernment.moa.spss.api.impl.VerifyCMSSignatureRequestImpl; +import at.gv.egovernment.moa.util.Base64Utils; +import at.gv.egovernment.moa.util.MiscUtil; /** @@ -41,6 +81,20 @@ import at.gv.egovernment.moa.logging.Logger; @Component("FirstBKAMobileAuthTask") public class FirstBKAMobileAuthTask extends AbstractAuthServletTask { + private static final String CONF_MOASPSS_TRUSTPROFILE = "modules.bkamobileAuth.verify.trustprofile"; + private static final String CONF_SIGNING_TIME_JITTER = "modules.bkamobileAuth.verify.time.jitter"; + private static final String CONF_EID_TOKEN_ENCRYPTION_KEY = "modules.bkamobileAuth.eIDtoken.encryption.pass"; + + private static final String EIDCONTAINER_KEY_SALT = "salt"; + private static final String EIDCONTAINER_KEY_IV = "iv"; + private static final String EIDCONTAINER_EID = "eid"; + private static final String EIDCONTAINER_KEY_IDL = "idl"; + private static final String EIDCONTAINER_KEY_BINDINGCERT = "cert"; + + public static final String REQ_PARAM_eID_BLOW = "eidToken"; + + @Autowired(required=true) private AuthConfiguration authConfig; + /* (non-Javadoc) * @see at.gv.egovernment.moa.id.auth.modules.AbstractAuthServletTask#execute(at.gv.egovernment.moa.id.process.api.ExecutionContext, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ @@ -48,9 +102,206 @@ public class FirstBKAMobileAuthTask extends AbstractAuthServletTask { public void execute(ExecutionContext executionContext, HttpServletRequest request, HttpServletResponse response) throws TaskExecutionException { - Logger.info("Redirect to Second BKA Mobile Auth task"); - performRedirectToItself(pendingReq, response, GeneralProcessEngineSignalController.ENDPOINT_GENERIC); + try { + String eIDBlobRawB64 = request.getParameter(REQ_PARAM_eID_BLOW); + if (MiscUtil.isEmpty(eIDBlobRawB64)) { + //TODO: add dummy-auth functionality + + Logger.warn("NO eID data blob included!"); + throw new MOAIDException("NO eID data blob included!", null); + } + + parseDemoValuesIntoMOASession(pendingReq, pendingReq.getMOASession(), eIDBlobRawB64); + + } catch (MOAIDException e) { + throw new TaskExecutionException(pendingReq, e.getMessage(), e); + + } catch (Exception e) { + throw new TaskExecutionException(pendingReq, e.getMessage(), e); + + } + + //Logger.info("Redirect to Second BKA Mobile Auth task"); + //performRedirectToItself(pendingReq, response, GeneralProcessEngineSignalController.ENDPOINT_GENERIC); + + } + + /** + * @param pendingReq + * @param moaSession + * @param eIDBlobRaw + * @throws MOAIDException + * @throws IOException + */ + private void parseDemoValuesIntoMOASession(IRequest pendingReq, IAuthenticationSession moaSession, String eIDBlobRawB64) throws MOAIDException, IOException { + Logger.debug("Check eID blob signature ... "); + byte[] eIDBlobRaw = Base64Utils.decode(eIDBlobRawB64.trim(), false); + + VerifyCMSSignatureResponse cmsResp = SignatureVerificationInvoker.getInstance().verifyCMSSignature( + createCMSVerificationReq(eIDBlobRaw)); + + if (cmsResp.getResponseElements().isEmpty()) { + Logger.warn("No CMS signature-verification response"); + throw new MOAIDException("Signature verification FAILED: No response", null); + + } + VerifyCMSSignatureResponseElement sigVerifyResp = (VerifyCMSSignatureResponseElement) cmsResp.getResponseElements().get(0); + analyseCMSSignatureVerificationResponse(sigVerifyResp); + + + Logger.info("eID blob signature is VALID!"); + byte[] decRawEidBlob = null; + byte[] signedData = null; + try { + Logger.debug("Starting eID information extraction ... "); + CMSSignedData cmsContent = new CMSSignedData(eIDBlobRaw); + signedData = (byte[])cmsContent.getSignedContent().getContent(); + if (!cmsContent.getSignedContent().getContentType().equals(CMSObjectIdentifiers.data)) { + Logger.warn("Signature contains NO 'data' OID 1.2.840.113549.1.7.1"); + throw new MOAIDException("Signature contains NO 'data' OID 1.2.840.113549.1.7.1", null); + } + if (signedData == null) { + Logger.warn("CMS SignedData is empty or null"); + throw new MOAIDException("CMS SignedData is empty or null", null); + } + Logger.info("Signed content extracted"); + + + Logger.debug("Starting signed content decryption ... "); + JsonParser parser = new JsonParser(); + JsonObject signedDataJson = (JsonObject) parser.parse(new String(signedData, "UTF-8")); + byte[] salt = Base64Utils.decode(signedDataJson.get(EIDCONTAINER_KEY_SALT).getAsString(), false); + byte[] ivraw = Base64Utils.decode(signedDataJson.get(EIDCONTAINER_KEY_IV).getAsString(), false); + byte[] encRawEidBlob = Base64Utils.decode(signedDataJson.get(EIDCONTAINER_EID).getAsString(), false); + SecretKey seckey = generateDecryptionKey(salt); + IvParameterSpec iv = new IvParameterSpec(ivraw); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, seckey, iv); + decRawEidBlob = cipher.doFinal(encRawEidBlob); + Logger.info("eID data decryption completed"); + + + Logger.debug("Starting eID-blob parsing ..."); + JsonObject eIDBlobJson = (JsonObject) parser.parse(new String(decRawEidBlob, "UTF-8")); + String idlB64 = eIDBlobJson.get( + EIDCONTAINER_KEY_IDL).getAsString(); + String bindingCertB64 = eIDBlobJson.get( + EIDCONTAINER_KEY_BINDINGCERT).getAsString(); + javax.security.cert.X509Certificate bindingCert = javax.security.cert.X509Certificate.getInstance(Base64Utils.decode(bindingCertB64, false)); + if (!sigVerifyResp.getSignerInfo().getSignerCertificate().equals(bindingCert)) { + Logger.error("eID-blob signing certificate DOES NOT match to binding certificate included in eID blob!"); + Logger.info("BindingCert: " + bindingCert.toString()); + Logger.info("SigningCert: " + sigVerifyResp.getSignerInfo().getSignerCertificate().toString()); + throw new MOAIDException("eID-blob signing certificate DOES NOT match to binding certificate included in eID blob!", null); + + } + Logger.info("eID-blob parsing completed"); + + + Logger.debug("Parse eID information into MOA-Session ..."); + byte[] rawIDL = Base64Utils.decode(idlB64, false); + IIdentityLink identityLink = new IdentityLinkAssertionParser(new ByteArrayInputStream(rawIDL)).parseIdentityLink(); + moaSession.setIdentityLink(identityLink); + moaSession.setUseMandates(false); + moaSession.setForeigner(false); + moaSession.setBkuURL("http://egiz.gv.at/BKA_MobileAuthTest"); + moaSession.setQAALevel(PVPConstants.STORK_QAA_1_3); + Logger.info("Session Restore completed"); + + + } catch (MOAIDException e) { + throw e; + + } catch (JsonParseException e) { + if (decRawEidBlob != null) + Logger.error("eID-blob parse error! blob: " + new String(decRawEidBlob, "UTF-8"), e); + + if (signedData != null) + Logger.error("eID-blob parse error! blob: " + new String(signedData, "UTF-8"), e); + + if (decRawEidBlob == null && signedData == null) + Logger.error("eID-blob parse error!", e); + + throw new MOAIDException("eID-blob parse error!", null); + + } catch (org.bouncycastle.cms.CMSException e) { + Logger.error("Can not parse CMS signature.", e); + throw new MOAIDException("Can not parse CMS signature.", null, e); + + } catch (InvalidAlgorithmParameterException| NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) { + Logger.error("Can not decrypte eID data.", e); + throw new MOAIDException("Can not decrypte eID data", null, e); + } catch (CertificateException e) { + Logger.error("Can not extract mobile-app binding-certificate from eID blob.", e); + throw new MOAIDException("Can not extract mobile-app binding-certificate from eID blob.", null, e); + + } finally { + + } + + } + + private SecretKey generateDecryptionKey(byte[] salt) throws MOAIDException { + String decryptionPassPhrase = authConfig.getBasicMOAIDConfiguration(CONF_EID_TOKEN_ENCRYPTION_KEY, "DEFAULTPASSWORD"); + try { + SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); + KeySpec spec = new PBEKeySpec(decryptionPassPhrase.toCharArray(), salt, 2000, 128); + SecretKey derivedKey = factory.generateSecret(spec); + SecretKeySpec symKeySpec = new SecretKeySpec(derivedKey.getEncoded(), "AES"); + return symKeySpec; + +// KeyGenerator pbkdf2 = KeyGenerator.getInstance("PBKDF2"); +// PBEKeyAndParameterSpec parameterSpec = +// new PBEKeyAndParameterSpec(decryptionPassPhrase.getBytes(), salt, 2000, 16); +// pbkdf2.init(parameterSpec, random); +// SecretKey derivedKey = pbkdf2.generateKey(); +// +// SecretKeySpec spec = new SecretKeySpec(derivedKey.getEncoded(), "AES"); +// SecretKeyFactory kf = SecretKeyFactory.getInstance("AES"); +// return kf.generateSecret(spec); + + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + Logger.error("Mobile-Auth Module has an internal errror.", e); + throw new MOAIDException("Mobile-Auth Module has an internal errror.", null, e); + + } + } + + /** + * @throws MOAIDException + * + */ + private void analyseCMSSignatureVerificationResponse(VerifyCMSSignatureResponseElement verifySigResult) throws MOAIDException { + //validate CMS signature verification response + if (verifySigResult.getSignatureCheck().getCode() != 0) { + Logger.warn("CMS signature verification FAILED with StatusCode: " + verifySigResult.getSignatureCheck().getCode()); + throw new MOAIDException("CMS signature verification FAILED with StatusCode: " + verifySigResult.getSignatureCheck().getCode(), null); + + } + if (verifySigResult.getCertificateCheck().getCode() != 0) { + Logger.warn("CMS certificate verification FAILED with StatusCode: " + verifySigResult.getCertificateCheck().getCode()); + throw new MOAIDException("CMS certificate verification FAILED with StatusCode: " + verifySigResult.getCertificateCheck().getCode(), null); + + } + SignerInfo signerInfos = verifySigResult.getSignerInfo(); + DateTime date = new DateTime(signerInfos.getSigningTime().getTime()); + Integer signingTimeJitter = Integer.valueOf(authConfig.getBasicMOAIDConfiguration(CONF_SIGNING_TIME_JITTER, "5")); + if (date.plusMinutes(signingTimeJitter).isBeforeNow()) { + Logger.warn("CMS signature-time is before: " + date.plusMinutes(signingTimeJitter)); + throw new MOAIDException("CMS signature-time is before: " + date.plusMinutes(signingTimeJitter), null); + + } + } + private VerifyCMSSignatureRequest createCMSVerificationReq(byte[] eIDBlobRaw) { + VerifyCMSSignatureRequestImpl cmsSigVerifyReq = new VerifyCMSSignatureRequestImpl(); + cmsSigVerifyReq.setSignatories(VerifyCMSSignatureRequestImpl.ALL_SIGNATORIES); + cmsSigVerifyReq.setExtended(false); + cmsSigVerifyReq.setPDF(false); + cmsSigVerifyReq.setTrustProfileId(authConfig.getBasicMOAIDConfiguration(CONF_MOASPSS_TRUSTPROFILE, "!!NOT SET!!!")); + cmsSigVerifyReq.setCMSSignature(new ByteArrayInputStream(eIDBlobRaw)); + return cmsSigVerifyReq; + } } diff --git a/id/server/modules/moa-id-module-bkaMobilaAuthSAML2Test/src/main/resources/BKAMobileAuth.process.xml b/id/server/modules/moa-id-module-bkaMobilaAuthSAML2Test/src/main/resources/BKAMobileAuth.process.xml index 4a0f4d5f2..6f41f347a 100644 --- a/id/server/modules/moa-id-module-bkaMobilaAuthSAML2Test/src/main/resources/BKAMobileAuth.process.xml +++ b/id/server/modules/moa-id-module-bkaMobilaAuthSAML2Test/src/main/resources/BKAMobileAuth.process.xml @@ -12,8 +12,10 @@ - - + + + -- cgit v1.2.3