diff options
12 files changed, 617 insertions, 94 deletions
diff --git a/basicConfig/ms-connector/default_config.properties b/basicConfig/ms-connector/default_config.properties index e1aff309..49b21234 100644 --- a/basicConfig/ms-connector/default_config.properties +++ b/basicConfig/ms-connector/default_config.properties @@ -90,6 +90,8 @@ eidas.ms.auth.eIDAS.authblock.keystore.password=f/+saJBc3a}*/T^s eidas.ms.auth.eIDAS.authblock.key.alias=connectorkeypair eidas.ms.auth.eIDAS.authblock.key.password=f/+saJBc3a}*/T^s +## aktiviert die LegacyVersion des ID Austria AuthBlock +#eidas.ms.auth.eIDAS.authblock.use.legacy.version=false ################################################# #### PVP2 S-Profile end-point configuration #### diff --git a/infos/ms-connector/Handbuch_MS-eIDAS-Node.docx b/infos/ms-connector/Handbuch_MS-eIDAS-Node.docx Binary files differindex d4cfd501..0271359c 100644 --- a/infos/ms-connector/Handbuch_MS-eIDAS-Node.docx +++ b/infos/ms-connector/Handbuch_MS-eIDAS-Node.docx diff --git a/infos/ms-connector/handbook/Handbuch_MS-Connector.pdf b/infos/ms-connector/handbook/Handbuch_MS-Connector.pdf Binary files differindex a8780a97..85e1a7c6 100644 --- a/infos/ms-connector/handbook/Handbuch_MS-Connector.pdf +++ b/infos/ms-connector/handbook/Handbuch_MS-Connector.pdf diff --git a/infos/ms-connector/history.txt b/infos/ms-connector/history.txt index 3df045dc..dd266f5c 100644 --- a/infos/ms-connector/history.txt +++ b/infos/ms-connector/history.txt @@ -1,7 +1,8 @@ Dieses Dokument zeigt die Veränderungen und Erweiterungen am eIDAS MS-Connector -Version 1.3.3 (2022-xx-xx): - - Zusätzliches Logging auf Level _Info_ ergänzt um mehr Informationen aus dem Matching Prozess zu erhalten +Version 1.3.3 (2022-08-26): + - Unterstützung des neuen AuthBlock Format des ID Austria Systems + - Zusätzliches Logging auf Level 'Info' ergänzt um mehr Informationen aus dem Matching Prozess zu erhalten Version 1.3.2 (2022-08-02): - Bugfix diff --git a/infos/ms-connector/readme_1.3.3.md b/infos/ms-connector/readme_1.3.3.md index cccfd4d9..e7917aa3 100644 --- a/infos/ms-connector/readme_1.3.3.md +++ b/infos/ms-connector/readme_1.3.3.md @@ -1,9 +1,10 @@ -# MS-Connector v1.3.3 Release vom xx.xx.2022 +# MS-Connector v1.3.3 Release vom 26.08.2022 Der MS-Connector implementiert eine Bridge zwischen dem österreichischen E-ID System und dem eIDAS Framework um ausländischen Benutzern eine Anmeldung am österreichischen Service-Providern zu ermöglichen. ### Änderungen in dieser Version + - Unterstützung des neuen AuthBlock Format des ID Austria Systems - Zusätzliches Logging auf Level _Info_ ergänzt um mehr Informationen aus dem Matching Prozess zu erhalten @@ -19,6 +20,13 @@ Nachfolgend finden Sie die erforderlichen Schritte für das Update eines bestehe 3. Kopieren sie die Applikation __MsConnectorPackage__/ms_connector.war nach in das Applikationsverzeichnis ihres Applikationsservers +4. Aus dem ID Austria Projekt kommt es zu einer Anpassung an der Frontend/Backend Schnittstelle des ID Austria System (BRZ/BM.I) welche in weiterer Folge auch Einfluss auf den MS-Connector hat. Hintergrund ist, dass vom MS-Connector ein AuthBlock für das ID Austria System erzeugt und signiert wird welcher anschließend im ID Austria System verifiziert wird. Im Zuge der Schnittstellenanpassung kommt es zu einer Änderung des Inhalts des AuthBlock. Die MS-Connector Version 1.3.3 unterstützt sowohl das alte als auch das neue AuthBlock Format wobei das zu verwendete Version per Konfiguration geändert werden kann. Da aus aktueller Sicht offen ist ab wann das ID Austria Backend das neue Format unterstützt und ob eine parallele Unterstützung beide Formate durch das Backend umgesetzt wird, muss der MS-Connector bis auf weiteres mit dem Konfigurationsparameter ```eidas.ms.auth.eIDAS.authblock.use.legacy.version=true```, für die Erstellung des alten AuthBlock Formats, betrieben werden. + +5. Neue optionale Konfigurationsparameter + + - *ID Austria AuthBlock Format* + - ```eidas.ms.auth.eIDAS.authblock.use.legacy.version``` + diff --git a/infos/ms-proxyservice/history.txt b/infos/ms-proxyservice/history.txt index 048ae73a..e9a7cec4 100644 --- a/infos/ms-proxyservice/history.txt +++ b/infos/ms-proxyservice/history.txt @@ -1,6 +1,6 @@ Dieses Dokument zeigt die Veränderungen und Erweiterungen am eIDAS MS-Proxy-Service -Version 1.0 (2022-xx-xx): +Version 1.0 (2022-08-28): - Initiale Version des eIDAS MS-Proxy-Service diff --git a/infos/ms-proxyservice/readme_1.0.0.md b/infos/ms-proxyservice/readme_1.0.0.md index b3c50127..9f5725a6 100644 --- a/infos/ms-proxyservice/readme_1.0.0.md +++ b/infos/ms-proxyservice/readme_1.0.0.md @@ -1,4 +1,4 @@ -# MS-Proxy-Service v1.0.0 Release vom xx.xx.2022 +# MS-Proxy-Service v1.0.0 Release vom 26.08.2022 ## Beschreibung Das MS-Proxy-Service implementiert eine Bridge zwischen dem österreichischen E-ID System und dem eIDAS Framework um österreichischen Benutzern eine Anmeldung an ausländischen Service-Providern zu ermöglichen. diff --git a/modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/service/AuthBlockSigningService.java b/modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/service/AuthBlockSigningService.java index 098e76ce..1998fa85 100644 --- a/modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/service/AuthBlockSigningService.java +++ b/modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/service/AuthBlockSigningService.java @@ -1,5 +1,6 @@ package at.asitplus.eidas.specific.modules.auth.eidas.v2.service; +import java.io.Serializable; import java.security.Key; import java.security.KeyStore; import java.security.KeyStoreException; @@ -44,7 +45,7 @@ import lombok.extern.slf4j.Slf4j; /** * Service to build and sign AuthBlock's for E-ID system. - * + * * @author tlenz * */ @@ -55,157 +56,206 @@ public class AuthBlockSigningService { private static final String KEYSTORE_FRIENDLYNAME = "AuthBlock_Signing"; private static ObjectMapper mapper = new ObjectMapper(); - + @Autowired IConfiguration basicConfig; - + @Autowired EaafKeyStoreFactory keyStoreFactory; - private Pair<KeyStore, Provider> keyStore; - + /** - * Build and sign an AuthBlock for E-ID system. - * + * Build and sign an AuthBlock for E-ID system. + * * @param pendingReq data that should be added into AuthBlock * @return serialized JWS - * @throws JsonProcessingException In case of a AuthBlock generation error - * @throws JoseException In case of a JWS signing error - * @throws EaafException In case of a KeyStore or Key error + * @throws JsonProcessingException In case of a AuthBlock generation error + * @throws JoseException In case of a JWS signing error + * @throws EaafException In case of a KeyStore or Key error */ - public String buildSignedAuthBlock(IRequest pendingReq) + public String buildSignedAuthBlock(IRequest pendingReq) throws JsonProcessingException, EaafException, JoseException { - - //TODO: set Challenge to SAML2 requestId to create link between authentication request and authBlock - + + // TODO: set Challenge to SAML2 requestId to create link between authentication + // request and authBlock + // build AuthBlock - EidasAuchBlock authBlock = new EidasAuchBlock(); - authBlock.setChallenge(UUID.randomUUID().toString()); - authBlock.setTimestamp(LocalDateTime.now(ZoneOffset.UTC).truncatedTo(ChronoUnit.SECONDS)); - authBlock.setUniqueId(pendingReq.getRawData(MsEidasNodeConstants.DATA_REQUESTERID, String.class)); - authBlock.setPiiTransactionId(pendingReq.getUniquePiiTransactionIdentifier()); - - //set Binding PublicKey if available - Object bindingPubKey = pendingReq.getRawData(MsEidasNodeConstants.EID_BINDING_PUBLIC_KEY_NAME); - if (bindingPubKey instanceof String) { - authBlock.setBindingPublicKey((String) bindingPubKey); - - } - - String jwsPayload = mapper.writeValueAsString(authBlock); + final String jwsPayload = mapper.writeValueAsString(buildAuthBlock(pendingReq)); log.debug("Building and sign authBlock with data: {}", jwsPayload); - - //sign JWS - return JoseUtils - .createSignature(keyStore, getKeyAlias(), getKeyPassword(), jwsPayload, false, - KEYSTORE_FRIENDLYNAME); + + // sign JWS + return JoseUtils.createSignature( + keyStore, getKeyAlias(), getKeyPassword(), jwsPayload, false, KEYSTORE_FRIENDLYNAME); + + } + + private Serializable buildAuthBlock(IRequest pendingReq) { + if (basicConfig.getBasicConfigurationBoolean( + MsEidasNodeConstants.PROP_CONFIG_AUTHBLOCK_LEGACY_USE, false)) { + final EidasAuchBlockV1 authBlock = new EidasAuchBlockV1(); + authBlock.setChallenge(UUID.randomUUID().toString()); + authBlock.setTimestamp(LocalDateTime.now(ZoneOffset.UTC).truncatedTo(ChronoUnit.SECONDS)); + authBlock.setUniqueId(pendingReq.getRawData(MsEidasNodeConstants.DATA_REQUESTERID, String.class)); + authBlock.setPiiTransactionId(pendingReq.getUniquePiiTransactionIdentifier()); + + // set Binding PublicKey if available + final Object bindingPubKey = pendingReq.getRawData(MsEidasNodeConstants.EID_BINDING_PUBLIC_KEY_NAME); + if (bindingPubKey instanceof String) { + authBlock.setBindingPublicKey((String) bindingPubKey); + + } + return authBlock; + + } else { + final EidasAuchBlockV2 authBlock = new EidasAuchBlockV2(); + authBlock.setChallenge(UUID.randomUUID().toString()); + authBlock.setTimestamp(LocalDateTime.now(ZoneOffset.UTC).truncatedTo(ChronoUnit.SECONDS)); + + // set Binding PublicKey if available + final Object bindingPubKey = pendingReq.getRawData(MsEidasNodeConstants.EID_BINDING_PUBLIC_KEY_NAME); + if (bindingPubKey instanceof String) { + authBlock.setBindingPublicKey((String) bindingPubKey); + + } + return authBlock; + + } } - /** * Get the Base64 encoded PublicKey that is used to sign the AuthBlock. - * + * * @return Base64 encoded PublicKey * @throws EaafKeyAccessException In case of an unknown or invalid key */ - public String getBase64EncodedPublicKey() throws EaafKeyAccessException { - Pair<Key, X509Certificate[]> keyPair = EaafKeyStoreUtils.getPrivateKeyAndCertificates( - keyStore.getFirst(), getKeyAlias(), getKeyPassword(), true, KEYSTORE_FRIENDLYNAME); + public String getBase64EncodedPublicKey() throws EaafKeyAccessException { + final Pair<Key, X509Certificate[]> keyPair = EaafKeyStoreUtils.getPrivateKeyAndCertificates( + keyStore.getFirst(), getKeyAlias(), getKeyPassword(), true, KEYSTORE_FRIENDLYNAME); return Base64.getEncoder().encodeToString(keyPair.getSecond()[0].getPublicKey().getEncoded()); - + } @PostConstruct - private void initialize() throws KeyStoreException, EaafException { + private void initialize() throws KeyStoreException, EaafException { log.debug("Initializing AuthBlock signing service ... "); - // read Connector wide config data TODO connector wide! - String keyStoreName = basicConfig + // read Connector wide config data TODO connector wide! + final String keyStoreName = basicConfig .getBasicConfiguration(MsEidasNodeConstants.PROP_CONFIG_AUTHBLOCK_KEYSTORE_NAME); - String keyStorePw = basicConfig + final String keyStorePw = basicConfig .getBasicConfiguration(MsEidasNodeConstants.PROP_CONFIG_AUTHBLOCK_KEYSTORE_PASSWORD); - String keyStorePath = basicConfig + final String keyStorePath = basicConfig .getBasicConfiguration(MsEidasNodeConstants.PROP_CONFIG_AUTHBLOCK_KEYSTORE_PATH); - String keyStoreType = basicConfig + final String keyStoreType = basicConfig .getBasicConfiguration(MsEidasNodeConstants.PROP_CONFIG_AUTHBLOCK_KEYSTORE_TYPE); - - //build new KeyStore configuration - KeyStoreConfiguration keyStoreConfiguration = new KeyStoreConfiguration(); + // build new KeyStore configuration + final KeyStoreConfiguration keyStoreConfiguration = new KeyStoreConfiguration(); keyStoreConfiguration.setFriendlyName(KEYSTORE_FRIENDLYNAME); - + keyStoreConfiguration.setSoftKeyStoreFilePath(keyStorePath); keyStoreConfiguration.setSoftKeyStorePassword(keyStorePw); - keyStoreConfiguration.setKeyStoreType(KeyStoreConfiguration.KeyStoreType.fromString(keyStoreType)); + keyStoreConfiguration.setKeyStoreType(KeyStoreConfiguration.KeyStoreType.fromString(keyStoreType)); keyStoreConfiguration.setKeyStoreName(keyStoreName); - - //validate KeyStore configuration + + // validate KeyStore configuration keyStoreConfiguration.validate(); - - //validate key alias + + // validate key alias if (StringUtils.isEmpty(getKeyAlias())) { - throw new EaafConfigurationException("config.08", - new Object[] {MsEidasNodeConstants.PROP_CONFIG_AUTHBLOCK_KEY_ALIAS}); - + throw new EaafConfigurationException("config.08", + new Object[] { MsEidasNodeConstants.PROP_CONFIG_AUTHBLOCK_KEY_ALIAS }); + } - - //build new KeyStore based on configuration - keyStore = keyStoreFactory.buildNewKeyStore(keyStoreConfiguration); - - //check if Key is accessible + + // build new KeyStore based on configuration + keyStore = keyStoreFactory.buildNewKeyStore(keyStoreConfiguration); + + // check if Key is accessible EaafKeyStoreUtils.getPrivateKeyAndCertificates( keyStore.getFirst(), getKeyAlias(), getKeyPassword(), true, KEYSTORE_FRIENDLYNAME); - - log.info("AuthBlock signing-service successful initialized"); - - } - + + log.info("AuthBlock signing-service successful initialized {}", + basicConfig.getBasicConfigurationBoolean(MsEidasNodeConstants.PROP_CONFIG_AUTHBLOCK_LEGACY_USE, false) + ? " in legacy mode" + : ""); + + } + private char[] getKeyPassword() { - final String value = basicConfig.getBasicConfiguration(MsEidasNodeConstants.PROP_CONFIG_AUTHBLOCK_KEY_PASSWORD); + final String value = basicConfig.getBasicConfiguration( + MsEidasNodeConstants.PROP_CONFIG_AUTHBLOCK_KEY_PASSWORD); if (value != null) { return value.trim().toCharArray(); } return null; - - } + } private String getKeyAlias() { return basicConfig .getBasicConfiguration(MsEidasNodeConstants.PROP_CONFIG_AUTHBLOCK_KEY_ALIAS); - + } - + /** * Technical AuthBlock for eIDAS Authentication. - * + * * @author tlenz * */ @Data @JsonInclude(JsonInclude.Include.NON_NULL) - private static class EidasAuchBlock { + @Deprecated + private static class EidasAuchBlockV1 implements Serializable { + + private static final long serialVersionUID = 8437172632081476257L; @JsonProperty("challenge") private String challenge; - + @JsonProperty("timestamp") @JsonSerialize(using = LocalDateTimeSerializer.class) @JsonDeserialize(using = LocalDateTimeDeserializer.class) @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", timezone = "UTC") private LocalDateTime timestamp; - + @JsonProperty("appId") private String uniqueId; - + @JsonProperty("piiTransactionId") private String piiTransactionId; - + + @JsonProperty("bindingPublicKey") + private String bindingPublicKey; + + } + + /** + * AuthBlock for Binding Authentication. + * + * @author tlenz + * + */ + @Data + @JsonInclude(JsonInclude.Include.NON_NULL) + public class EidasAuchBlockV2 implements Serializable { + + private static final long serialVersionUID = -2013435642666124497L; + + @JsonProperty("challenge") + private String challenge; + + @JsonProperty("issuedAt") + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", timezone = "UTC") + private LocalDateTime timestamp; + @JsonProperty("bindingPublicKey") private String bindingPublicKey; - + } - } diff --git a/modules/authmodule-eIDAS-v2/src/test/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/test/tasks/CreateIdentityLinkTaskEidLegacyTest.java b/modules/authmodule-eIDAS-v2/src/test/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/test/tasks/CreateIdentityLinkTaskEidLegacyTest.java new file mode 100644 index 00000000..a7d625f5 --- /dev/null +++ b/modules/authmodule-eIDAS-v2/src/test/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/test/tasks/CreateIdentityLinkTaskEidLegacyTest.java @@ -0,0 +1,459 @@ +package at.asitplus.eidas.specific.modules.auth.eidas.v2.test.tasks; + +import static at.asitplus.eidas.specific.core.MsEidasNodeConstants.PROP_CONFIG_SP_NEW_EID_MODE; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import java.net.URISyntaxException; +import java.security.KeyStore; +import java.security.Provider; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nonnull; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; + +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; +import org.jose4j.jwa.AlgorithmConstraints; +import org.jose4j.jwa.AlgorithmConstraints.ConstraintType; +import org.jose4j.jws.AlgorithmIdentifiers; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.skjolber.mockito.soap.SoapServiceRule; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +import at.asitplus.eidas.specific.core.MsEidasNodeConstants; +import at.asitplus.eidas.specific.core.test.config.dummy.MsConnectorDummyConfigMap; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.MatchedPersonResult; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidPostProcessingException; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidasAttributeException; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.ICcSpecificEidProcessingService; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.tasks.CreateIdentityLinkTask; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.EidasResponseUtils; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.JoseUtils; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.JoseUtils.JwsResult; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.MatchingTaskUtils; +import at.asitplus.eidas.specific.modules.core.eidas.EidasConstants; +import at.asitplus.eidas.specific.modules.core.eidas.service.EidasAttributeRegistry; +import at.gv.egiz.eaaf.core.api.IRequest; +import at.gv.egiz.eaaf.core.api.IRequestStorage; +import at.gv.egiz.eaaf.core.api.data.EaafConfigConstants; +import at.gv.egiz.eaaf.core.api.data.EaafConstants; +import at.gv.egiz.eaaf.core.api.data.PvpAttributeDefinitions; +import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext; +import at.gv.egiz.eaaf.core.exceptions.EaafException; +import at.gv.egiz.eaaf.core.exceptions.EaafStorageException; +import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreFactory; +import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreUtils; +import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration; +import at.gv.egiz.eaaf.core.impl.data.Pair; +import at.gv.egiz.eaaf.core.impl.idp.auth.data.AuthProcessDataWrapper; +import at.gv.egiz.eaaf.core.impl.idp.module.test.DummySpConfiguration; +import at.gv.egiz.eaaf.core.impl.idp.module.test.TestRequestImpl; +import at.gv.egiz.eaaf.core.impl.idp.process.ExecutionContextImpl; +import at.gv.egiz.eaaf.core.impl.utils.Random; +import eu.eidas.auth.commons.attribute.AttributeDefinition; +import eu.eidas.auth.commons.attribute.AttributeValue; +import eu.eidas.auth.commons.attribute.ImmutableAttributeMap; +import eu.eidas.auth.commons.attribute.ImmutableAttributeMap.Builder; +import eu.eidas.auth.commons.protocol.eidas.impl.PostalAddress; +import eu.eidas.auth.commons.protocol.impl.AuthenticationResponse; +import lombok.val; +import szrservices.GetIdentityLinkEidasResponse; +import szrservices.PersonInfoType; +import szrservices.SZR; +import szrservices.SZRException_Exception; +import szrservices.SignContentEntry; +import szrservices.SignContentResponseType; + +@RunWith(SpringJUnit4ClassRunner.class) +@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS) +@ContextConfiguration(locations = { + "/SpringTest-context_tasks_test.xml", + "/SpringTest-context_basic_mapConfig.xml"}) +public class CreateIdentityLinkTaskEidLegacyTest { + + @Autowired(required = true) + private CreateIdentityLinkTask task; + + @Autowired(required = true) + private MsConnectorDummyConfigMap basicConfig; + @Autowired + protected EidasAttributeRegistry attrRegistry; + + @Autowired + EaafKeyStoreFactory keyStoreFactory; + + @Autowired + ICcSpecificEidProcessingService eidPostProcessor; + + @Autowired + private IRequestStorage requestStorage; + + final ExecutionContext executionContext = new ExecutionContextImpl(); + private MockHttpServletRequest httpReq; + private MockHttpServletResponse httpResp; + private TestRequestImpl pendingReq; + private DummySpConfiguration oaParam; + private SZR szrMock; + + private static final String PW = "f/+saJBc3a}*/T^s"; + private static final String ALIAS = "connectorkeypair"; + + private static final List<String> BINDING_AUTH_ALGORITHM_WHITELIST_SIGNING = Collections.unmodifiableList(Arrays + .asList(AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256, + AlgorithmIdentifiers.ECDSA_USING_P521_CURVE_AND_SHA512, AlgorithmIdentifiers.RSA_PSS_USING_SHA256, + AlgorithmIdentifiers.RSA_PSS_USING_SHA512)); + + private static ObjectMapper mapper = new ObjectMapper(); + + private AuthenticationResponse response; + private MatchedPersonResult matchingInfos; + + @Rule + public final SoapServiceRule soap = SoapServiceRule.newInstance(); + + /** + * jUnit test set-up. + * @throws EidasAttributeException + * @throws EidPostProcessingException + */ + @Before + public void setUp() throws EaafStorageException, URISyntaxException, EidPostProcessingException, EidasAttributeException { + + httpReq = new MockHttpServletRequest("POST", "https://localhost/authhandler"); + httpResp = new MockHttpServletResponse(); + RequestContextHolder.resetRequestAttributes(); + RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(httpReq, httpResp)); + + basicConfig.putConfigValue("eidas.ms.auth.eIDAS.szrclient.debug.useDummySolution", "false"); + basicConfig.putConfigValue("eidas.ms.auth.eIDAS.authblock.use.legacy.version", "true"); + + final Map<String, String> spConfig = new HashMap<>(); + spConfig.put(EaafConfigConstants.SERVICE_UNIQUEIDENTIFIER, "testSp"); + spConfig.put("target", "urn:publicid:gv.at:cdid+XX"); + spConfig.put(PROP_CONFIG_SP_NEW_EID_MODE, "true"); + oaParam = new DummySpConfiguration(spConfig, basicConfig); + pendingReq = new TestRequestImpl(); + + response = buildDummyAuthResponse(false); + pendingReq.getSessionData(AuthProcessDataWrapper.class) + .setGenericDataToSession(Constants.DATA_FULL_EIDAS_RESPONSE, response); + + final Map<String, Object> eidasAttributes = convertEidasAttrToSimpleMap( + response.getAttributes().getAttributeMap()); + final SimpleEidasData eidData = eidPostProcessor.postProcess(eidasAttributes); + MatchingTaskUtils.storeInitialEidasData(pendingReq, eidData); + + matchingInfos = MatchedPersonResult.builder() + .bpk(RandomStringUtils.randomAlphabetic(5)) + .givenName(eidData.getGivenName()) + .familyName(eidData.getFamilyName()) + .dateOfBirth(eidData.getDateOfBirth()) + .countryCode(eidData.getCitizenCountryCode()) + .build(); + MatchingTaskUtils.storeFinalMatchingResult(pendingReq, matchingInfos); + + pendingReq.setSpConfig(oaParam); + pendingReq.setPendingReqId(at.gv.egiz.eaaf.core.impl.utils.Random.nextProcessReferenceValue()); + pendingReq.setAuthUrl("http://test.com/"); + pendingReq.setTransactionId("avaasbav"); + pendingReq.setPiiTransactionId(RandomStringUtils.randomAlphanumeric(10)); + + executionContext.put(MsEidasNodeConstants.REQ_PARAM_SELECTED_COUNTRY, "XX"); + executionContext.put(EaafConstants.PROCESS_ENGINE_REQUIRES_NO_POSTAUTH_REDIRECT, true); + + szrMock = soap.mock(SZR.class, "http://localhost:1234/demoszr"); + } + + @Test + public void successfulProcessWithDeInfos() throws Exception { + //initialize test + response = buildDummyAuthResponse(true); + pendingReq.getSessionData(AuthProcessDataWrapper.class) + .setGenericDataToSession(Constants.DATA_FULL_EIDAS_RESPONSE, response); + + SimpleEidasData eidData = eidPostProcessor.postProcess( + convertEidasAttrToSimpleMap(response.getAttributes().getAttributeMap())); + MatchingTaskUtils.storeInitialEidasData(pendingReq, eidData); + + matchingInfos = MatchedPersonResult.builder() + .bpk(RandomStringUtils.randomAlphabetic(5)) + .givenName(eidData.getGivenName()) + .familyName(eidData.getFamilyName()) + .dateOfBirth(eidData.getDateOfBirth()) + .countryCode(eidData.getCitizenCountryCode()) + .build(); + MatchingTaskUtils.storeFinalMatchingResult(pendingReq, matchingInfos); + + String vsz = RandomStringUtils.randomNumeric(10); + when(szrMock.getStammzahlEncrypted(any(), any())).thenReturn(vsz); + SignContentResponseType signContentResp = new SignContentResponseType(); + final SignContentEntry signContentEntry = new SignContentEntry(); + signContentEntry.setValue(RandomStringUtils.randomAlphanumeric(10)); + signContentResp.getOut().add(signContentEntry); + when(szrMock.signContent(any(), any(), any())).thenReturn(signContentResp); + + String randomTestSp = RandomStringUtils.randomAlphabetic(10); + pendingReq.setRawDataToTransaction(MsEidasNodeConstants.DATA_REQUESTERID, randomTestSp); + + //perform test + task.execute(pendingReq, executionContext); + + //validate state + // check if pendingRequest was stored + IRequest storedPendingReq = requestStorage.getPendingRequest(pendingReq.getPendingRequestId()); + Assert.assertNotNull("pendingReq not stored", storedPendingReq); + + //check data in session + final AuthProcessDataWrapper authProcessData = storedPendingReq.getSessionData(AuthProcessDataWrapper.class); + Assert.assertNotNull("AuthProcessData", authProcessData); + Assert.assertNotNull("eidasBind", authProcessData.getGenericDataFromSession(MsEidasNodeConstants.AUTH_DATA_EIDAS_BIND, String.class)); + + String authBlock = authProcessData.getGenericDataFromSession(MsEidasNodeConstants.AUTH_DATA_SZR_AUTHBLOCK, String.class); + Assert.assertNotNull("AuthBlock", authBlock); + + Assert.assertTrue("EID process", authProcessData.isEidProcess()); + Assert.assertTrue("foreigner process", authProcessData.isForeigner()); + Assert.assertEquals("EID-ISSUING_NATION", "LU", + authProcessData.getGenericDataFromSession(PvpAttributeDefinitions.EID_ISSUING_NATION_NAME, String.class)); + + // check authblock signature + final AlgorithmConstraints constraints = new AlgorithmConstraints(ConstraintType.PERMIT, + BINDING_AUTH_ALGORITHM_WHITELIST_SIGNING.toArray(new String[BINDING_AUTH_ALGORITHM_WHITELIST_SIGNING.size()])); + Pair<KeyStore, Provider> keyStore = getKeyStore(); + X509Certificate[] trustedCerts = EaafKeyStoreUtils + .getPrivateKeyAndCertificates(keyStore.getFirst(), ALIAS, PW.toCharArray(), true, "junit").getSecond(); + JwsResult result = JoseUtils.validateSignature(authBlock, Arrays.asList(trustedCerts), constraints); + Assert.assertTrue("AuthBlock not valid", result.isValid()); + JsonNode authBlockJson = mapper.readTree(result.getPayLoad()); + Assert.assertNotNull("deserialized AuthBlock", authBlockJson); + + Assert.assertNotNull("no piiTransactionId in pendingRequesdt", + storedPendingReq.getUniquePiiTransactionIdentifier()); + Assert.assertFalse("'challenge' is null", authBlockJson.get("challenge").asText().isEmpty()); + Assert.assertEquals("piiTransactionId", storedPendingReq.getUniquePiiTransactionIdentifier(), + authBlockJson.get("piiTransactionId").asText()); + Assert.assertEquals("appId", randomTestSp, authBlockJson.get("appId").asText()); + Assert.assertFalse("'timestamp' is null", authBlockJson.get("timestamp").asText().isEmpty()); + Assert.assertFalse("binding pubKey", authBlockJson.has("bindingPublicKey")); + + } + + + + @Test + public void successfulProcessWithDataFromMatching() throws Exception { + //initialize test + String vsz = RandomStringUtils.randomNumeric(10); + when(szrMock.getStammzahlEncrypted(any(), any())).thenReturn(vsz); + SignContentResponseType signContentResp = new SignContentResponseType(); + final SignContentEntry signContentEntry = new SignContentEntry(); + signContentEntry.setValue(RandomStringUtils.randomAlphanumeric(10)); + signContentResp.getOut().add(signContentEntry); + + when(szrMock.signContent(any(), any(), any())).thenReturn(signContentResp); + + String randomTestSp = RandomStringUtils.randomAlphabetic(10); + String bindingPubKey = RandomStringUtils.randomAlphabetic(10); + pendingReq.setRawDataToTransaction(MsEidasNodeConstants.DATA_REQUESTERID, randomTestSp); + pendingReq.setRawDataToTransaction(MsEidasNodeConstants.EID_BINDING_PUBLIC_KEY_NAME, bindingPubKey); + + + //perform test + task.execute(pendingReq, executionContext); + + + //validate state + // check if pendingRequest was stored + IRequest storedPendingReq = requestStorage.getPendingRequest(pendingReq.getPendingRequestId()); + Assert.assertNotNull("pendingReq not stored", storedPendingReq); + + //check data in session + final AuthProcessDataWrapper authProcessData = storedPendingReq.getSessionData(AuthProcessDataWrapper.class); + Assert.assertNotNull("AuthProcessData", authProcessData); + Assert.assertNotNull("eidasBind", authProcessData.getGenericDataFromSession(MsEidasNodeConstants.AUTH_DATA_EIDAS_BIND, String.class)); + + // check authblock signature + String authBlock = authProcessData.getGenericDataFromSession(MsEidasNodeConstants.AUTH_DATA_SZR_AUTHBLOCK, String.class); + Assert.assertNotNull("AuthBlock", authBlock); + + final AlgorithmConstraints constraints = new AlgorithmConstraints(ConstraintType.PERMIT, + BINDING_AUTH_ALGORITHM_WHITELIST_SIGNING.toArray(new String[BINDING_AUTH_ALGORITHM_WHITELIST_SIGNING.size()])); + Pair<KeyStore, Provider> keyStore = getKeyStore(); + X509Certificate[] trustedCerts = EaafKeyStoreUtils + .getPrivateKeyAndCertificates(keyStore.getFirst(), ALIAS, PW.toCharArray(), true, "junit").getSecond(); + JwsResult result = JoseUtils.validateSignature(authBlock, Arrays.asList(trustedCerts), constraints); + Assert.assertTrue("AuthBlock not valid", result.isValid()); + JsonNode authBlockJson = mapper.readTree(result.getPayLoad()); + Assert.assertNotNull("deserialized AuthBlock", authBlockJson); + + Assert.assertNotNull("no piiTransactionId in pendingRequesdt", + storedPendingReq.getUniquePiiTransactionIdentifier()); + Assert.assertFalse("'challenge' is null", authBlockJson.get("challenge").asText().isEmpty()); + Assert.assertEquals("piiTransactionId", storedPendingReq.getUniquePiiTransactionIdentifier(), + authBlockJson.get("piiTransactionId").asText()); + Assert.assertEquals("appId", randomTestSp, authBlockJson.get("appId").asText()); + Assert.assertFalse("'timestamp' is null", authBlockJson.get("timestamp").asText().isEmpty()); + + } + + private Pair<KeyStore, Provider> getKeyStore() throws EaafException { + // read Connector wide config data TODO connector wide! + String keyStoreName = basicConfig.getBasicConfiguration(MsEidasNodeConstants.PROP_CONFIG_AUTHBLOCK_KEYSTORE_NAME); + String keyStorePw = basicConfig.getBasicConfiguration(MsEidasNodeConstants.PROP_CONFIG_AUTHBLOCK_KEYSTORE_PASSWORD); + String keyStorePath = basicConfig.getBasicConfiguration(MsEidasNodeConstants.PROP_CONFIG_AUTHBLOCK_KEYSTORE_PATH); + String keyStoreType = basicConfig.getBasicConfiguration(MsEidasNodeConstants.PROP_CONFIG_AUTHBLOCK_KEYSTORE_TYPE); + + + //build new KeyStore configuration + KeyStoreConfiguration keyStoreConfiguration = new KeyStoreConfiguration(); + keyStoreConfiguration.setFriendlyName("jUnit test"); + + keyStoreConfiguration.setSoftKeyStoreFilePath(keyStorePath); + keyStoreConfiguration.setSoftKeyStorePassword(keyStorePw); + keyStoreConfiguration.setKeyStoreType(KeyStoreConfiguration.KeyStoreType.fromString(keyStoreType)); + keyStoreConfiguration.setKeyStoreName(keyStoreName); + + //build new KeyStore based on configuration + return keyStoreFactory.buildNewKeyStore(keyStoreConfiguration); + + } + + @Nonnull + private void setSzrResponseIdentityLink(String responseXmlPath) throws JAXBException, SZRException_Exception { + final JAXBContext jaxbContext = JAXBContext + .newInstance(szrservices.ObjectFactory.class, org.w3._2001._04.xmldsig_more.ObjectFactory.class, + org.w3._2000._09.xmldsig.ObjectFactory.class, + at.gv.e_government.reference.namespace.persondata._20020228.ObjectFactory.class); + final Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); + final GetIdentityLinkEidasResponse szrResponse = (GetIdentityLinkEidasResponse) jaxbUnmarshaller + .unmarshal(this.getClass().getResourceAsStream(responseXmlPath)); + org.mockito.Mockito.when(szrMock.getIdentityLinkEidas(any(PersonInfoType.class))).thenReturn(szrResponse.getGetIdentityLinkReturn()); + + } + + @Nonnull + private AuthenticationResponse buildDummyAuthResponse(boolean withAll) throws URISyntaxException { + return buildDummyAuthResponse(withAll, false); + + } + + @Nonnull + private AuthenticationResponse buildDummyAuthResponse(boolean withAll, boolean withEmpty) throws URISyntaxException { + final AttributeDefinition attributeDef = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( + EidasConstants.eIDAS_ATTR_PERSONALIDENTIFIER).first(); + final AttributeDefinition attributeDef2 = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( + EidasConstants.eIDAS_ATTR_CURRENTFAMILYNAME).first(); + final AttributeDefinition attributeDef3 = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( + EidasConstants.eIDAS_ATTR_CURRENTGIVENNAME).first(); + final AttributeDefinition attributeDef4 = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( + EidasConstants.eIDAS_ATTR_DATEOFBIRTH).first(); + final AttributeDefinition attributeDef5 = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( + EidasConstants.eIDAS_ATTR_PLACEOFBIRTH).first(); + final AttributeDefinition attributeDef6 = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( + EidasConstants.eIDAS_ATTR_BIRTHNAME).first(); + + final Builder attributeMap = ImmutableAttributeMap.builder(); + attributeMap.put(attributeDef, "LU/AT/" + RandomStringUtils.randomNumeric(64)); + attributeMap.put(attributeDef2, RandomStringUtils.randomAlphabetic(10)); + attributeMap.put(attributeDef3, RandomStringUtils.randomAlphabetic(10)); + attributeMap.put(attributeDef4, "2001-01-01"); + if (withAll) { + if (withEmpty) { + attributeMap.put(attributeDef5, Collections.emptySet()); + + } else { + attributeMap.put(attributeDef5, RandomStringUtils.randomAlphabetic(10)); + + } + attributeMap.put(attributeDef6, RandomStringUtils.randomAlphabetic(10)); + + } + + val b = new AuthenticationResponse.Builder(); + return b.id("_".concat(Random.nextHexRandom16())) + .issuer(RandomStringUtils.randomAlphabetic(10)) + .subject(RandomStringUtils.randomAlphabetic(10)) + .statusCode(EidasConstants.SUCCESS_URI) + .inResponseTo("_".concat(Random.nextHexRandom16())) + .subjectNameIdFormat("afaf") + .levelOfAssurance(EaafConstants.EIDAS_LOA_PREFIX + RandomStringUtils.randomAlphabetic(5)) + .attributes(attributeMap.build()) + .build(); + } + + private Map<String, Object> convertEidasAttrToSimpleMap( + ImmutableMap<AttributeDefinition<?>, ImmutableSet<? extends AttributeValue<?>>> attributeMap) { + final Map<String, Object> result = new HashMap<>(); + for (final AttributeDefinition<?> el : attributeMap.keySet()) { + final Class<?> parameterizedType = el.getParameterizedType(); + if (DateTime.class.equals(parameterizedType)) { + convertDateTime(attributeMap, result, el); + } else if (PostalAddress.class.equals(parameterizedType)) { + convertPostalAddress(attributeMap, result, el); + } else { + convertString(attributeMap, result, el); + } + } + return result; + } + + private void convertString(ImmutableMap<AttributeDefinition<?>, + ImmutableSet<? extends AttributeValue<?>>> attributeMap, + Map<String, Object> result, AttributeDefinition<?> el) { + final List<String> natPersonIdObj = EidasResponseUtils + .translateStringListAttribute(el, attributeMap.get(el)); + final String stringAttr = natPersonIdObj.get(0); + if (StringUtils.isNotEmpty(stringAttr)) { + result.put(el.getFriendlyName(), stringAttr); + + } + } + + private void convertPostalAddress(ImmutableMap<AttributeDefinition<?>, + ImmutableSet<? extends AttributeValue<?>>> attributeMap, + Map<String, Object> result, AttributeDefinition<?> el) { + final PostalAddress addressAttribute = EidasResponseUtils + .translateAddressAttribute(el, attributeMap.get(el).asList()); + if (addressAttribute != null) { + result.put(el.getFriendlyName(), addressAttribute); + + } + } + + private void convertDateTime(ImmutableMap<AttributeDefinition<?>, + ImmutableSet<? extends AttributeValue<?>>> attributeMap, + Map<String, Object> result, AttributeDefinition<?> el) { + final DateTime attribute = EidasResponseUtils.translateDateAttribute(el, attributeMap.get(el).asList()); + if (attribute != null) { + result.put(el.getFriendlyName(), attribute); + + } + } +} diff --git a/modules/authmodule-eIDAS-v2/src/test/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/test/tasks/CreateIdentityLinkTaskEidNewTest.java b/modules/authmodule-eIDAS-v2/src/test/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/test/tasks/CreateIdentityLinkTaskEidNewTest.java index 36c0c2af..9060762c 100644 --- a/modules/authmodule-eIDAS-v2/src/test/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/test/tasks/CreateIdentityLinkTaskEidNewTest.java +++ b/modules/authmodule-eIDAS-v2/src/test/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/test/tasks/CreateIdentityLinkTaskEidNewTest.java @@ -1,6 +1,7 @@ package at.asitplus.eidas.specific.modules.auth.eidas.v2.test.tasks; import static at.asitplus.eidas.specific.core.MsEidasNodeConstants.PROP_CONFIG_SP_NEW_EID_MODE; +import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -262,11 +263,10 @@ public class CreateIdentityLinkTaskEidNewTest { Assert.assertNotNull("no piiTransactionId in pendingRequesdt", storedPendingReq.getUniquePiiTransactionIdentifier()); - Assert.assertEquals("piiTransactionId", storedPendingReq.getUniquePiiTransactionIdentifier(), - authBlockJson.get("piiTransactionId").asText()); - Assert.assertEquals("appId", randomTestSp, authBlockJson.get("appId").asText()); Assert.assertFalse("'challenge' is null", authBlockJson.get("challenge").asText().isEmpty()); - Assert.assertFalse("'timestamp' is null", authBlockJson.get("timestamp").asText().isEmpty()); + Assert.assertFalse("'timestamp' is null", authBlockJson.get("issuedAt").asText().isEmpty()); + assertNull("piiTransactionId", authBlockJson.get("piiTransactionId")); + assertNull("appId", authBlockJson.get("appId")); Assert.assertFalse("binding pubKey", authBlockJson.has("bindingPublicKey")); @@ -385,12 +385,12 @@ public class CreateIdentityLinkTaskEidNewTest { Assert.assertNotNull("deserialized AuthBlock", authBlockJson); Assert.assertNotNull("no piiTransactionId in pendingRequesdt", - storedPendingReq.getUniquePiiTransactionIdentifier()); - Assert.assertEquals("piiTransactionId", storedPendingReq.getUniquePiiTransactionIdentifier(), - authBlockJson.get("piiTransactionId").asText()); - Assert.assertEquals("appId", randomTestSp, authBlockJson.get("appId").asText()); + storedPendingReq.getUniquePiiTransactionIdentifier()); Assert.assertFalse("'challenge' is null", authBlockJson.get("challenge").asText().isEmpty()); - Assert.assertFalse("'timestamp' is null", authBlockJson.get("timestamp").asText().isEmpty()); + Assert.assertFalse("'timestamp' is null", authBlockJson.get("issuedAt").asText().isEmpty()); + assertNull("piiTransactionId", authBlockJson.get("piiTransactionId")); + assertNull("appId", authBlockJson.get("appId")); + Assert.assertTrue("binding pubKey", authBlockJson.has("bindingPublicKey")); Assert.assertEquals("binding PubKey", bindingPubKey, authBlockJson.get("bindingPublicKey").asText()); diff --git a/modules/core_common_lib/src/main/java/at/asitplus/eidas/specific/core/MsEidasNodeConstants.java b/modules/core_common_lib/src/main/java/at/asitplus/eidas/specific/core/MsEidasNodeConstants.java index 68ef4560..4304ddd5 100644 --- a/modules/core_common_lib/src/main/java/at/asitplus/eidas/specific/core/MsEidasNodeConstants.java +++ b/modules/core_common_lib/src/main/java/at/asitplus/eidas/specific/core/MsEidasNodeConstants.java @@ -97,6 +97,8 @@ public class MsEidasNodeConstants { public static final String PROP_EIDAS_REQUEST_LOA_MINIMUM_LEVEL = "auth.eIDAS.node_v2.loa.requested.minimum"; + public static final String PROP_CONFIG_AUTHBLOCK_LEGACY_USE = + "auth.eIDAS.authblock.use.legacy.version"; public static final String PROP_CONFIG_AUTHBLOCK_KEYSTORE_TYPE = "auth.eIDAS.authblock.keystore.type"; public static final String PROP_CONFIG_AUTHBLOCK_KEYSTORE_PATH = diff --git a/ms_specific_connector/src/main/resources/application.properties b/ms_specific_connector/src/main/resources/application.properties index 5a68e29a..659a5a4e 100644 --- a/ms_specific_connector/src/main/resources/application.properties +++ b/ms_specific_connector/src/main/resources/application.properties @@ -121,6 +121,7 @@ eidas.ms.auth.eIDAS.szrclient.eidasbind.mds.inject=false # tech. AuthBlock signing for E-ID process +eidas.ms.auth.eIDAS.authblock.use.legacy.version=false #eidas.ms.auth.eIDAS.authblock.keystore.password=f/+saJBc3a}*/T^s #eidas.ms.auth.eIDAS.authblock.keystore.friendlyName=connectorkeypair #eidas.ms.auth.eIDAS.authblock.keystore.path=keys/teststore.jks |