diff options
| author | Thomas Lenz <thomas.lenz@egiz.gv.at> | 2020-11-27 09:18:38 +0100 | 
|---|---|---|
| committer | Thomas Lenz <thomas.lenz@egiz.gv.at> | 2020-11-27 09:18:38 +0100 | 
| commit | 7a800070338bec49cf3a4e2e6f76a0778f9eae02 (patch) | |
| tree | 91a5f1521a0a6a02ed3e21f21d25b1792881f2ad /eidas_modules/authmodule-eIDAS-v2/src/main/java | |
| parent | 1dcc1b9cd5d6e2a50817474c597e2924f67be2b1 (diff) | |
| parent | 9f684f489a2825d1c8fde371b7e71b8d7513060a (diff) | |
| download | National_eIDAS_Gateway-7a800070338bec49cf3a4e2e6f76a0778f9eae02.tar.gz National_eIDAS_Gateway-7a800070338bec49cf3a4e2e6f76a0778f9eae02.tar.bz2 National_eIDAS_Gateway-7a800070338bec49cf3a4e2e6f76a0778f9eae02.zip | |
Merge branch 'connector_update_SZRv4' into 'nightlybuild'
Integration of E-ID functionality
See merge request egiz/eidas_at_proxy!1
Diffstat (limited to 'eidas_modules/authmodule-eIDAS-v2/src/main/java')
5 files changed, 881 insertions, 205 deletions
| diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/Constants.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/Constants.java index 3eec12bd..83a2afa6 100644 --- a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/Constants.java +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/Constants.java @@ -156,6 +156,11 @@ public class Constants {    // Default values for SZR communication    public static final String SZR_CONSTANTS_DEFAULT_DOCUMENT_TYPE = "ELEKTR_DOKUMENT"; +  // AuthBlock +  public static final String SZR_AUTHBLOCK = "authData_AUTHBLOCK"; +  public static final String EIDAS_BIND = "authData_EIDAS_BIND"; + +    // TODO remove!!!    public static final String SZR_CONSTANTS_DEFAULT_ISSUING_DATE = "2014-01-01";    public static final String SZR_CONSTANTS_DEFAULT_ISSUING_AUTHORITY = "ms-specific eIDAS-Node for AT"; diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/service/AuthBlockSigningService.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/service/AuthBlockSigningService.java new file mode 100644 index 00000000..cfaecfbb --- /dev/null +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/service/AuthBlockSigningService.java @@ -0,0 +1,192 @@ +package at.asitplus.eidas.specific.modules.auth.eidas.v2.service; + +import java.security.Key; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.Provider; +import java.security.cert.X509Certificate; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.Base64; +import java.util.UUID; + +import javax.annotation.PostConstruct; + +import org.apache.commons.lang3.StringUtils; +import org.jose4j.lang.JoseException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; + +import at.asitplus.eidas.specific.connector.MsEidasNodeConstants; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.JoseUtils; +import at.gv.egiz.eaaf.core.api.IRequest; +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.exception.EaafKeyAccessException; +import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; +import at.gv.egiz.eaaf.core.exceptions.EaafException; +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 lombok.Data; +import lombok.extern.slf4j.Slf4j; + +/** + * Service to build and sign AuthBlock's for E-ID system. + *  + * @author tlenz + * + */ +@Slf4j +@Service("authBlockSigningService") +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.  +   *  +   * @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 +   */ +  public String buildSignedAuthBlock(IRequest pendingReq)  +      throws JsonProcessingException, EaafException, JoseException { +     +    // build AuthBlock +    EidasAuchBlock authBlock = new EidasAuchBlock(); +    authBlock.setChallenge(UUID.randomUUID().toString()); +    authBlock.setTimestamp(LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS)); +    authBlock.setUniqueId(pendingReq.getRawData(MsEidasNodeConstants.DATA_REQUESTERID, String.class));     +    String jwsPayload = mapper.writeValueAsString(authBlock); +    log.debug("Building and sign authBlock with data: {}", jwsPayload); +     +    //sign JWS +    return JoseUtils +        .createSignature(keyStore, getKeyAlias(), getKeyPassword(), jwsPayload, false, +                         KEYSTORE_FRIENDLYNAME);     +  } +   + +  /** +   * 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);        +    return Base64.getEncoder().encodeToString(keyPair.getSecond()[0].getPublicKey().getEncoded()); +     +  } + +  @PostConstruct +  private void initialize() throws KeyStoreException, EaafException {    +    log.debug("Initializing AuthBlock signing service ... "); +    // 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(KEYSTORE_FRIENDLYNAME); +     +    keyStoreConfiguration.setSoftKeyStoreFilePath(keyStorePath); +    keyStoreConfiguration.setSoftKeyStorePassword(keyStorePw); +    keyStoreConfiguration.setKeyStoreType(KeyStoreConfiguration.KeyStoreType.fromString(keyStoreType));     +    keyStoreConfiguration.setKeyStoreName(keyStoreName); +     +    //validate KeyStore configuration +    keyStoreConfiguration.validate(); +         +    //validate key alias +    if (StringUtils.isEmpty(getKeyAlias())) { +      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 +    EaafKeyStoreUtils.getPrivateKeyAndCertificates( +        keyStore.getFirst(), getKeyAlias(), getKeyPassword(), true, KEYSTORE_FRIENDLYNAME); +     +    log.info("AuthBlock signing-service successful initialized"); +     +  }    +   +  private char[] getKeyPassword() { +    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 +  private static class EidasAuchBlock { + +    @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'") +    private LocalDateTime timestamp; +     +    @JsonProperty("appId") +    private String uniqueId; +     +     +  } + +   +} diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/szr/SzrClient.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/szr/SzrClient.java index 763d8dab..69b993a4 100644 --- a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/szr/SzrClient.java +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/szr/SzrClient.java @@ -34,6 +34,8 @@ import java.security.NoSuchAlgorithmException;  import java.security.SecureRandom;  import java.security.UnrecoverableKeyException;  import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap;  import java.util.List;  import java.util.Map; @@ -56,6 +58,8 @@ import javax.xml.ws.BindingProvider;  import javax.xml.ws.Dispatch;  import javax.xml.ws.handler.Handler; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper;  import org.apache.commons.lang3.StringUtils;  import org.apache.cxf.configuration.jsse.TLSClientParameters;  import org.apache.cxf.endpoint.Client; @@ -84,10 +88,15 @@ import szrservices.GetBPKResponse;  import szrservices.GetIdentityLinkEidas;  import szrservices.GetIdentityLinkEidasResponse;  import szrservices.IdentityLinkType; +import szrservices.JwsHeaderParam;  import szrservices.ObjectFactory;  import szrservices.PersonInfoType;  import szrservices.SZR;  import szrservices.SZRException_Exception; +import szrservices.SignContent; +import szrservices.SignContentEntry; +import szrservices.SignContentResponseType; +  @Service("SZRClientForeIDAS")  public class SzrClient { @@ -96,6 +105,13 @@ public class SzrClient {    private static final String CLIENT_DEFAULT = "DefaultClient";    private static final String CLIENT_RAW = "RawClient"; +  private static final String ATTR_NAME_VSZ = "urn:eidgvat:attributes.vsz.value"; +  private static final String ATTR_NAME_PUBKEYS = "urn:eidgvat:attributes.user.pubkeys"; +  private static final String ATTR_NAME_STATUS = "urn:eidgvat:attributes.eid.status"; +  private static final String KEY_BC_BIND = "bcBindReq"; +  private static final String JOSE_HEADER_USERCERTPINNING_TYPE = "urn:at.gv.eid:bindtype"; +  private static final String JOSE_HEADER_USERCERTPINNING_EIDASBIND = "urn:at.gv.eid:eidasBind"; +    @Autowired    private IConfiguration basicConfig; @@ -109,9 +125,11 @@ public class SzrClient {    private String szrUrl = null;    private QName qname = null; +  final ObjectMapper mapper = new ObjectMapper(); +    /**     * Get IdentityLink of a person. -   *  +   *     * @param personInfo Person identification information     * @return IdentityLink     * @throws SzrCommunicationException In case of a SZR error @@ -165,7 +183,7 @@ public class SzrClient {      } catch (final Exception e) {        log.warn("SZR communication FAILED. Reason: " + e.getMessage(), e); -      throw new SzrCommunicationException("ernb.02", new Object[] { e.getMessage() }, e); +      throw new SzrCommunicationException("ernb.02", new Object[]{e.getMessage()}, e);      } @@ -173,19 +191,19 @@ public class SzrClient {    /**     * Get bPK of person. -   *  +   *     * @param personInfo Person identification information -   * @param target requested bPK target -   * @param vkz Verfahrenskennzeichen +   * @param target     requested bPK target +   * @param vkz        Verfahrenskennzeichen     * @return bPK for this person     * @throws SzrCommunicationException In case of a SZR error     */ -  public String getBpk(PersonInfoType personInfo, String target, String vkz) +  public List<String> getBpk(PersonInfoType personInfo, String target, String vkz)        throws SzrCommunicationException {      try {        final GetBPK parameters = new GetBPK();        parameters.setPersonInfo(personInfo); -      parameters.setBereichsKennung(target); +      parameters.getBereichsKennung().add(target);        parameters.setVKZ(vkz);        final GetBPKResponse result = this.szr.getBPK(parameters); @@ -193,16 +211,91 @@ public class SzrClient {      } catch (final SZRException_Exception e) {        log.warn("SZR communication FAILED. Reason: " + e.getMessage(), e); -      throw new SzrCommunicationException("ernb.02", new Object[] { e.getMessage() }, e); +      throw new SzrCommunicationException("ernb.02", new Object[]{e.getMessage()}, e);      }    } +  /** +   * Request a encryped baseId from SRZ. +   * +   * @param personInfo Minimum dataset of person +   * @return encrypted baseId +   * @throws SzrCommunicationException    In case of a SZR error +   */ +  public String getEncryptedStammzahl(final PersonInfoType personInfo) +      throws SzrCommunicationException { + +    final String resp; +    try { +      resp = this.szr.getStammzahlEncrypted(personInfo, false); +    } catch (SZRException_Exception e) { +      throw new SzrCommunicationException("ernb.02", new Object[]{e.getMessage()}, e); +    } + +    if (StringUtils.isEmpty(resp)) { +      throw new SzrCommunicationException("ernb.01", new Object[]{"Stammzahl response empty"}); // TODO error handling +    } + +    return resp; + +  } + + +  /** +   * Signs content. +   * +   * @param vsz ? TODO +   * @param bindingPubKey  binding PublikKey as PKCS1# (ASN.1) container +   * @param eidStatus Status of the E-ID +   * @return bPK for this person +   * @throws SzrCommunicationException In case of a SZR error +   */ +  public String getBcBind(final String vsz, final String bindingPubKey, final String eidStatus) +      throws SzrCommunicationException { + +    final Map<String, Object> bcBindMap = new HashMap<>(); +    bcBindMap.put(ATTR_NAME_VSZ, vsz); +    bcBindMap.put(ATTR_NAME_STATUS, eidStatus); +    bcBindMap.put(ATTR_NAME_PUBKEYS, Arrays.asList(bindingPubKey)); + +    try { +      final String serializedBcBind = mapper.writeValueAsString(bcBindMap); +      final SignContent req = new SignContent(); +      final SignContentEntry bcBindInfo = new SignContentEntry(); +      bcBindInfo.setKey(KEY_BC_BIND); +      bcBindInfo.setValue(serializedBcBind); +      req.getIn().add(bcBindInfo); +      req.setAppendCert(false); +      final JwsHeaderParam bcBindJoseHeader = new JwsHeaderParam(); +      bcBindJoseHeader.setKey(JOSE_HEADER_USERCERTPINNING_TYPE); +      bcBindJoseHeader.setValue(JOSE_HEADER_USERCERTPINNING_EIDASBIND); +      req.getJWSHeaderParam().add(bcBindJoseHeader); + +      log.trace("Requesting SZR to sign bcBind datastructure ... "); +      final SignContentResponseType resp = szr.signContent(req.isAppendCert(), req.getJWSHeaderParam(), req.getIn()); +      log.trace("Receive SZR response on bcBind siging operation "); + +      if (resp == null || resp.getOut() == null +          || resp.getOut().isEmpty() +          || StringUtils.isEmpty(resp.getOut().get(0).getValue())) { +        throw new SzrCommunicationException("ernb.01", new Object[]{"BcBind response empty"}); +      } + +      return resp.getOut().get(0).getValue(); + +    } catch (final JsonProcessingException | SZRException_Exception e) { +      log.warn("Requesting bcBind by using SZR FAILED. Reason: {}", e.getMessage(), null, e); +      throw new SzrCommunicationException("ernb.02", +          new Object[]{e.getMessage()}, e); +    } +  } +    @PostConstruct    private void initialize() {      log.info("Starting SZR-Client initialization .... "); -    final URL url = SzrClient.class.getResource("/szr_client/SZR-1.1.WSDL"); +    final URL url = SzrClient.class.getResource("/szr_client/SZR_v4.0.wsdl");      final boolean useTestSzr = basicConfig.getBasicConfigurationBoolean(          Constants.CONIG_PROPS_EIDAS_SZRCLIENT_USETESTSERVICE, diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/CreateIdentityLinkTask.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/CreateIdentityLinkTask.java index 88c3515b..f9142f8e 100644 --- a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/CreateIdentityLinkTask.java +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/CreateIdentityLinkTask.java @@ -19,10 +19,11 @@   * file for details on the various modules and licenses.   * The "NOTICE" text file is part of the distribution. Any derivative works   * that you distribute must include a readable copy of the "NOTICE" text file. -*/ + */  package at.asitplus.eidas.specific.modules.auth.eidas.v2.tasks; +import java.io.IOException;  import java.io.InputStream;  import java.util.HashMap;  import java.util.List; @@ -30,6 +31,7 @@ import java.util.Map;  import javax.servlet.http.HttpServletRequest;  import javax.servlet.http.HttpServletResponse; +import javax.xml.parsers.ParserConfigurationException;  import org.apache.commons.lang3.StringUtils;  import org.joda.time.DateTime; @@ -37,6 +39,7 @@ import org.springframework.beans.factory.annotation.Autowired;  import org.springframework.stereotype.Component;  import org.w3c.dom.Element;  import org.w3c.dom.Node; +import org.xml.sax.SAXException;  import com.google.common.collect.ImmutableMap;  import com.google.common.collect.ImmutableSet; @@ -47,6 +50,7 @@ import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants;  import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.ErnbEidData;  import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidasAttributeException;  import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.SzrCommunicationException; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.AuthBlockSigningService;  import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.ICcSpecificEidProcessingService;  import at.asitplus.eidas.specific.modules.auth.eidas.v2.szr.SzrClient;  import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.EidasResponseUtils; @@ -71,6 +75,7 @@ import eu.eidas.auth.commons.attribute.AttributeDefinition;  import eu.eidas.auth.commons.attribute.AttributeValue;  import eu.eidas.auth.commons.light.ILightResponse;  import eu.eidas.auth.commons.protocol.eidas.impl.PostalAddress; +import lombok.Data;  import lombok.extern.slf4j.Slf4j;  import szrservices.IdentityLinkType;  import szrservices.PersonInfoType; @@ -78,9 +83,8 @@ import szrservices.TravelDocumentType;  /**   * Task that creates the IdentityLink for an eIDAS authenticated person. - *  - * @author tlenz   * + * @author tlenz   */  @Slf4j  @Component("CreateIdentityLinkTask") @@ -92,29 +96,30 @@ public class CreateIdentityLinkTask extends AbstractAuthServletTask {    private SzrClient szrClient;    @Autowired    private ICcSpecificEidProcessingService eidPostProcessor; +   +  @Autowired +  private AuthBlockSigningService authBlockSigner; +  private static final String EID_STATUS = "urn:eidgvat:eid.status.eidas"; +      /*     * (non-Javadoc) -   *  +   *     * @see at.gv.egovernment.moa.id.process.springweb.MoaIdTask#execute(at.gv.     * egovernment.moa.id.process.api.ExecutionContext,     * javax.servlet.http.HttpServletRequest,     * javax.servlet.http.HttpServletResponse)     */    @Override -  public void execute(ExecutionContext executionContext, -      HttpServletRequest request, HttpServletResponse response) +  public void execute(ExecutionContext executionContext, HttpServletRequest request, HttpServletResponse response)        throws TaskExecutionException {      try {        final AuthProcessDataWrapper authProcessData = pendingReq.getSessionData(AuthProcessDataWrapper.class); -      final ILightResponse eidasResponse = authProcessData.getGenericDataFromSession( -          Constants.DATA_FULL_EIDAS_RESPONSE, ILightResponse.class); +      final ILightResponse eidasResponse = authProcessData +          .getGenericDataFromSession(Constants.DATA_FULL_EIDAS_RESPONSE, ILightResponse.class); -      final Map<String, Object> simpleAttrMap = convertEidasAttrToSimpleMap(eidasResponse.getAttributes() -          .getAttributeMap()); - -      IIdentityLink identityLink = null; -      String bpk = null; +      final Map<String, Object> simpleAttrMap = convertEidasAttrToSimpleMap( +          eidasResponse.getAttributes().getAttributeMap());        // post-process eIDAS attributes        final ErnbEidData eidData = eidPostProcessor.postProcess(simpleAttrMap); @@ -122,178 +127,83 @@ public class CreateIdentityLinkTask extends AbstractAuthServletTask {        // write MDS into technical log and revision log        writeMdsLogInformation(eidData); -      // connect SZR-Gateway -      if (basicConfig.getBasicConfigurationBoolean( -          Constants.CONIG_PROPS_EIDAS_SZRCLIENT_DEBUG_USEDUMMY, false)) { -        log.warn("SZR-Dummy IS ACTIVE! IdentityLink is NOT VALID!!!!"); -        // create fake IdL -        // - fetch IdL template from resources -        final InputStream s = CreateIdentityLinkTask.class.getResourceAsStream( -            "/resources/xmldata/fakeIdL_IdL_template.xml"); -        final Element idlTemplate = DomUtils.parseXmlValidating(s); - -        identityLink = new SimpleIdentityLinkAssertionParser(idlTemplate).parseIdentityLink(); - -        // replace data -        final Element idlassertion = identityLink.getSamlAssertion(); - -        // - set fake baseID; -        final Node prIdentification = XPathUtils.selectSingleNode(idlassertion, -            SimpleIdentityLinkAssertionParser.PERSON_IDENT_VALUE_XPATH); -        prIdentification.getFirstChild().setNodeValue(eidData.getPseudonym()); - -        // - set last name -        final Node prFamilyName = XPathUtils.selectSingleNode(idlassertion, -            SimpleIdentityLinkAssertionParser.PERSON_FAMILY_NAME_XPATH); -        prFamilyName.getFirstChild().setNodeValue(eidData.getFamilyName()); - -        // - set first name -        final Node prGivenName = XPathUtils.selectSingleNode(idlassertion, -            SimpleIdentityLinkAssertionParser.PERSON_GIVEN_NAME_XPATH); -        prGivenName.getFirstChild().setNodeValue(eidData.getGivenName()); - -        // - set date of birth -        final Node prDateOfBirth = XPathUtils.selectSingleNode(idlassertion, -            SimpleIdentityLinkAssertionParser.PERSON_DATE_OF_BIRTH_XPATH); - -        prDateOfBirth.getFirstChild().setNodeValue(eidData.getFormatedDateOfBirth()); - -        identityLink = new SimpleIdentityLinkAssertionParser(idlassertion).parseIdentityLink(); - -        new BpkBuilder(); -        final Pair<String, String> bpkCalc = BpkBuilder.generateAreaSpecificPersonIdentifier( -            identityLink.getIdentificationValue(), -            identityLink.getIdentificationType(), -            pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier()); -        bpk = bpkCalc.getFirst(); - +      //build IdentityLink or VSZ and eidasBind +      if (basicConfig.getBasicConfigurationBoolean(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_DEBUG_USEDUMMY, false)) { +        SzrResultHolder idlResult = createDummyIdentityLinkForTestDeployment(eidData); +        //inject personal-data into session +        authProcessData.setIdentityLink(idlResult.getIdentityLink());   +         +        // set bPK and bPKType into auth session +        authProcessData.setGenericDataToSession(PvpAttributeDefinitions.BPK_NAME, extendBpkByPrefix( +            idlResult.getBpK(), pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier())); +        authProcessData.setGenericDataToSession(PvpAttributeDefinitions.EID_SECTOR_FOR_IDENTIFIER_NAME, +                                                pendingReq.getServiceProviderConfiguration() +                                                          .getAreaSpecificTargetIdentifier()); +                         } else { -        // contact SZR Gateway -        log.debug("Starting connecting SZR Gateway"); -        final PersonInfoType personInfo = new PersonInfoType(); -        final PersonNameType personName = new PersonNameType(); -        final PhysicalPersonType naturalPerson = new PhysicalPersonType(); -        final TravelDocumentType eDocument = new TravelDocumentType(); - -        naturalPerson.setName(personName); -        personInfo.setPerson(naturalPerson); -        personInfo.setTravelDocument(eDocument); - -        // person information -        personName.setFamilyName(eidData.getFamilyName()); -        personName.setGivenName(eidData.getGivenName()); -        naturalPerson.setDateOfBirth(eidData.getFormatedDateOfBirth()); -        eDocument.setIssuingCountry(eidData.getCitizenCountryCode()); -        eDocument.setDocumentNumber(eidData.getPseudonym()); - -        // eID document information -        eDocument.setDocumentType(basicConfig.getBasicConfiguration( -            Constants.CONIG_PROPS_EIDAS_SZRCLIENT_PARAMS_EDOCUMENTTYPE, -            Constants.SZR_CONSTANTS_DEFAULT_DOCUMENT_TYPE)); - -        // set PlaceOfBirth if available -        if (eidData.getPlaceOfBirth() != null) { -          log.trace("Find 'PlaceOfBirth' attribute: " + eidData.getPlaceOfBirth()); -          if (basicConfig.getBasicConfigurationBoolean( -              Constants.CONIG_PROPS_EIDAS_SZRCLIENT_PARAMS_SETPLACEOFBIRTHIFAVAILABLE, -              true)) { -            naturalPerson.setPlaceOfBirth(eidData.getPlaceOfBirth()); -            log.trace("Adding 'PlaceOfBirth' to ERnB request ... "); - -          } -        } - -        // set BirthName if available -        if (eidData.getBirthName() != null) { -          log.trace("Find 'BirthName' attribute: " + eidData.getBirthName()); +        //build SZR request from eIDAS data         +        final PersonInfoType personInfo = generateSzrRequest(eidData); +         +        //request SZR based on IDL or E-ID mode +        if (pendingReq.getServiceProviderConfiguration() +            .isConfigurationValue(MsEidasNodeConstants.PROP_CONFIG_SP_NEW_EID_MODE, false)) { + +          // get encrypted baseId +          String vsz = szrClient.getEncryptedStammzahl(personInfo); +                             +          // get eIDAS bind +          String signedEidasBind = szrClient.getBcBind(vsz,  +              authBlockSigner.getBase64EncodedPublicKey(),  +              EID_STATUS); + +          //get signed AuthBlock +          String jwsSignature = authBlockSigner.buildSignedAuthBlock(pendingReq); +        +          //inject personal-data into session +          authProcessData.setGenericDataToSession(Constants.SZR_AUTHBLOCK, jwsSignature); +          authProcessData.setGenericDataToSession(Constants.EIDAS_BIND, signedEidasBind); +                +        } else { +          //request SZR +          SzrResultHolder idlResult = requestSzrForIdentityLink(personInfo); +           +          // write ERnB input-data into revision-log            if (basicConfig.getBasicConfigurationBoolean( -              Constants.CONIG_PROPS_EIDAS_SZRCLIENT_PARAMS_SETBIRTHNAMEIFAVAILABLE, -              true)) { -            final AlternativeNameType alternativeName = new AlternativeNameType(); -            naturalPerson.setAlternativeName(alternativeName); -            alternativeName.setFamilyName(eidData.getBirthName()); -            log.trace("Adding 'BirthName' to ERnB request ... "); +              Constants.CONIG_PROPS_EIDAS_SZRCLIENT_WORKAROUND_REVISIONLOGDATASTORE_ACTIVE, false)) { +            revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.SZR_ERNB_EIDAS_RAW_ID, +                                     (String) simpleAttrMap.get(Constants.eIDAS_ATTR_PERSONALIDENTIFIER)); +            revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.SZR_ERNB_EIDAS_ERNB_ID, eidData.getPseudonym());            } +           +          //check result-data and write revision-log based on current state +          checkStateAndWriteRevisionLog(idlResult); +           + +          //inject personal-data into session +          authProcessData.setIdentityLink(idlResult.getIdentityLink());   +           +          // set bPK and bPKType into auth session +          authProcessData.setGenericDataToSession(PvpAttributeDefinitions.BPK_NAME, extendBpkByPrefix( +              idlResult.getBpK(), pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier())); +          authProcessData.setGenericDataToSession(PvpAttributeDefinitions.EID_SECTOR_FOR_IDENTIFIER_NAME, +                                                  pendingReq.getServiceProviderConfiguration() +                                                            .getAreaSpecificTargetIdentifier()); +                    } - -        final IdentityLinkType result = szrClient.getIdentityLinkInRawMode(personInfo); - -        final Element idlFromSzr = (Element) result.getAssertion(); -        identityLink = new SimpleIdentityLinkAssertionParser(idlFromSzr).parseIdentityLink(); - -        // write ERnB inputdata into revisionlog -        if (basicConfig.getBasicConfigurationBoolean( -            Constants.CONIG_PROPS_EIDAS_SZRCLIENT_WORKAROUND_REVISIONLOGDATASTORE_ACTIVE, false)) { -          revisionsLogger.logEvent(pendingReq, -              MsConnectorEventCodes.SZR_ERNB_EIDAS_RAW_ID, -              (String) simpleAttrMap.get(Constants.eIDAS_ATTR_PERSONALIDENTIFIER)); -          revisionsLogger.logEvent(pendingReq, -              MsConnectorEventCodes.SZR_ERNB_EIDAS_ERNB_ID, eidData.getPseudonym()); - -        } - -        // get bPK from SZR -        if (basicConfig.getBasicConfigurationBoolean( -            Constants.CONIG_PROPS_EIDAS_SZRCLIENT_DEBUG_USESRZFORBPKGENERATION, true)) { -          bpk = szrClient.getBpk( -              personInfo, -              pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier(), -              basicConfig.getBasicConfiguration( -                  Constants.CONIG_PROPS_EIDAS_SZRCLIENT_PARAMS_VKZ, -                  "no VKZ defined")); - -        } else { -          log.debug("Calculating bPK from baseId ... "); -          new BpkBuilder(); -          final Pair<String, String> bpkCalc = BpkBuilder.generateAreaSpecificPersonIdentifier( -              identityLink.getIdentificationValue(), -              identityLink.getIdentificationType(), -              pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier()); -          bpk = bpkCalc.getFirst(); - -        } - -      } - -      if (identityLink == null) { -        log.error("ERnB did not return an identity link."); -        throw new SzrCommunicationException("ernb.00", null); - -      } -      revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.SZR_IDL_RECEIVED, -          identityLink.getSamlAssertion().getAttribute(SimpleIdentityLinkAssertionParser.ASSERTIONID)); - -      if (bpk == null) { -        log.error("ERnB did not return a bPK for target: " + pendingReq.getServiceProviderConfiguration() -            .getAreaSpecificTargetIdentifier()); -        throw new SzrCommunicationException("ernb.01", null); -        } -      revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.SZR_BPK_RECEIVED); - -      log.debug("ERnB communication was successfull"); - +       +      //add generic info's into session        authProcessData.setForeigner(true); -      authProcessData.setIdentityLink(identityLink); -      authProcessData.setGenericDataToSession( -          PvpAttributeDefinitions.EID_ISSUING_NATION_NAME, -          EidasResponseUtils.parseEidasPersonalIdentifier((String) simpleAttrMap.get( -              Constants.eIDAS_ATTR_PERSONALIDENTIFIER)).getFirst()); - -      // set bPK and bPKType into auth session -      authProcessData.setGenericDataToSession( -          PvpAttributeDefinitions.BPK_NAME, -          extendBpkByPrefix( -              bpk, -              pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier())); -      authProcessData.setGenericDataToSession( -          PvpAttributeDefinitions.EID_SECTOR_FOR_IDENTIFIER_NAME, -          pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier()); - +      authProcessData.setGenericDataToSession(PvpAttributeDefinitions.EID_ISSUING_NATION_NAME, EidasResponseUtils +          .parseEidasPersonalIdentifier((String) simpleAttrMap.get(Constants.eIDAS_ATTR_PERSONALIDENTIFIER)) +          .getFirst()); +      authProcessData.setQaaLevel(eidasResponse.getLevelOfAssurance()); +                   // store pending-request        requestStoreage.storePendingRequest(pendingReq); - +       +            } catch (final EidasAttributeException e) {        throw new TaskExecutionException(pendingReq, "Minimum required eIDAS attributeset not found.", e); @@ -307,6 +217,116 @@ public class CreateIdentityLinkTask extends AbstractAuthServletTask {      }    } +  private PersonInfoType generateSzrRequest(ErnbEidData eidData) { +    log.debug("Starting connecting SZR Gateway"); +    final PersonInfoType personInfo = new PersonInfoType(); +    final PersonNameType personName = new PersonNameType(); +    final PhysicalPersonType naturalPerson = new PhysicalPersonType(); +    final TravelDocumentType eDocument = new TravelDocumentType(); + +    naturalPerson.setName(personName); +    personInfo.setPerson(naturalPerson); +    personInfo.setTravelDocument(eDocument); + +    // person information +    personName.setFamilyName(eidData.getFamilyName()); +    personName.setGivenName(eidData.getGivenName()); +    naturalPerson.setDateOfBirth(eidData.getFormatedDateOfBirth()); +    eDocument.setIssuingCountry(eidData.getCitizenCountryCode()); +    eDocument.setDocumentNumber(eidData.getPseudonym()); + +    // eID document information +    eDocument.setDocumentType(basicConfig +                                  .getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_PARAMS_EDOCUMENTTYPE, +                                                         Constants.SZR_CONSTANTS_DEFAULT_DOCUMENT_TYPE)); + +    // set PlaceOfBirth if available +    if (eidData.getPlaceOfBirth() != null) { +      log.trace("Find 'PlaceOfBirth' attribute: " + eidData.getPlaceOfBirth()); +      if (basicConfig +          .getBasicConfigurationBoolean(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_PARAMS_SETPLACEOFBIRTHIFAVAILABLE, +                                        true)) { +        naturalPerson.setPlaceOfBirth(eidData.getPlaceOfBirth()); +        log.trace("Adding 'PlaceOfBirth' to ERnB request ... "); + +      } +    } + +    // set BirthName if available +    if (eidData.getBirthName() != null) { +      log.trace("Find 'BirthName' attribute: " + eidData.getBirthName()); +      if (basicConfig +          .getBasicConfigurationBoolean(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_PARAMS_SETBIRTHNAMEIFAVAILABLE, +                                        true)) { +        final AlternativeNameType alternativeName = new AlternativeNameType(); +        naturalPerson.setAlternativeName(alternativeName); +        alternativeName.setFamilyName(eidData.getBirthName()); +        log.trace("Adding 'BirthName' to ERnB request ... "); + +      } +    } +     +    return personInfo; +     +  } + +  private SzrResultHolder requestSzrForIdentityLink(PersonInfoType personInfo)  +      throws SzrCommunicationException, EaafException { +    //request IdentityLink from SZR +    final IdentityLinkType result = szrClient.getIdentityLinkInRawMode(personInfo); +     +    final Element idlFromSzr = (Element) result.getAssertion(); +    IIdentityLink identityLink = new SimpleIdentityLinkAssertionParser(idlFromSzr).parseIdentityLink(); + +    // get bPK from SZR +    String bpk; +    if (basicConfig +        .getBasicConfigurationBoolean(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_DEBUG_USESRZFORBPKGENERATION, true)) { +      bpk = szrClient +          .getBpk(personInfo, pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier(), +                  basicConfig +                      .getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_PARAMS_VKZ, "no VKZ defined")) +          .get(0); + +    } else { +      log.debug("Calculating bPK from baseId ... "); +      new BpkBuilder(); +      final Pair<String, String> bpkCalc = BpkBuilder +          .generateAreaSpecificPersonIdentifier(identityLink.getIdentificationValue(), +                                                identityLink.getIdentificationType(), +                                                pendingReq.getServiceProviderConfiguration() +                                                          .getAreaSpecificTargetIdentifier()); +      bpk = bpkCalc.getFirst(); + +    } +     +    return new SzrResultHolder(identityLink, bpk); +     +  } +   +  private void checkStateAndWriteRevisionLog(SzrResultHolder idlResult) throws SzrCommunicationException { +    // write some infos into revision log +    if (idlResult.getIdentityLink() == null) { +      log.error("ERnB did not return an identity link."); +      throw new SzrCommunicationException("ernb.00", null); + +    } +    revisionsLogger.logEvent(pendingReq, +                             MsConnectorEventCodes.SZR_IDL_RECEIVED, +                             idlResult.getIdentityLink().getSamlAssertion() +                                         .getAttribute(SimpleIdentityLinkAssertionParser.ASSERTIONID)); + +    if (idlResult.getBpK() == null) { +      log.error("ERnB did not return a bPK for target: " + pendingReq.getServiceProviderConfiguration() +                                                                     .getAreaSpecificTargetIdentifier()); +      throw new SzrCommunicationException("ernb.01", null); + +    } +    revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.SZR_BPK_RECEIVED); +    log.debug("ERnB communication was successfull"); +         +  } +      private String extendBpkByPrefix(String bpk, String type) {      String bpkType = null; @@ -336,10 +356,9 @@ public class CreateIdentityLinkTask extends AbstractAuthServletTask {      for (final AttributeDefinition<?> el : attributeMap.keySet()) { -      final Class parameterizedType = el.getParameterizedType(); +      final Class<?> parameterizedType = el.getParameterizedType();        if (DateTime.class.equals(parameterizedType)) { -        final DateTime attribute = EidasResponseUtils.translateDateAttribute(el, attributeMap.get(el) -            .asList()); +        final DateTime attribute = EidasResponseUtils.translateDateAttribute(el, attributeMap.get(el).asList());          if (attribute != null) {            result.put(el.getFriendlyName(), attribute);            log.trace("Find attr '" + el.getFriendlyName() + "' with value: " + attribute.toString()); @@ -349,8 +368,8 @@ public class CreateIdentityLinkTask extends AbstractAuthServletTask {          }        } else if (PostalAddress.class.equals(parameterizedType)) { -        final PostalAddress addressAttribute = EidasResponseUtils.translateAddressAttribute(el, attributeMap -            .get(el).asList()); +        final PostalAddress addressAttribute = EidasResponseUtils +            .translateAddressAttribute(el, attributeMap.get(el).asList());          if (addressAttribute != null) {            result.put(el.getFriendlyName(), addressAttribute);            log.trace("Find attr '" + el.getFriendlyName() + "' with value: " + addressAttribute.toString()); @@ -360,8 +379,8 @@ public class CreateIdentityLinkTask extends AbstractAuthServletTask {          }        } else { -        final List<String> natPersonIdObj = EidasResponseUtils.translateStringListAttribute(el, attributeMap -            .get(el).asList()); +        final List<String> natPersonIdObj = EidasResponseUtils +            .translateStringListAttribute(el, attributeMap.get(el).asList());          final String stringAttr = natPersonIdObj.get(0);          if (StringUtils.isNotEmpty(stringAttr)) {            result.put(el.getFriendlyName(), stringAttr); @@ -381,25 +400,87 @@ public class CreateIdentityLinkTask extends AbstractAuthServletTask {    private void writeMdsLogInformation(ErnbEidData eidData) {      // log MDS and country code into technical log -    if (basicConfig.getBasicConfigurationBoolean( -        MsEidasNodeConstants.PROP_CONFIG_TECHNICALLOG_WRITE_MDS_INTO_TECH_LOG, false)) { -      log.info("eIDAS Auth. for user: " -          + eidData.getGivenName() + " " -          + eidData.getFamilyName() + " " -          + eidData.getFormatedDateOfBirth() + " " -          + "from " + eidData.getCitizenCountryCode()); +    if (basicConfig +        .getBasicConfigurationBoolean(MsEidasNodeConstants.PROP_CONFIG_TECHNICALLOG_WRITE_MDS_INTO_TECH_LOG, false)) { +      log.info("eIDAS Auth. for user: " + eidData.getGivenName() + " " + eidData.getFamilyName() + " " + eidData +          .getFormatedDateOfBirth() + " " + "from " + eidData.getCitizenCountryCode());      }      // log MDS and country code into revision log -    if (basicConfig.getBasicConfigurationBoolean( -        MsEidasNodeConstants.PROP_CONFIG_REVISIONLOG_WRITE_MDS_INTO_REVISION_LOG, false)) { +    if (basicConfig +        .getBasicConfigurationBoolean(MsEidasNodeConstants.PROP_CONFIG_REVISIONLOG_WRITE_MDS_INTO_REVISION_LOG, +                                      false)) {        revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.RESPONSE_FROM_EIDAS_MDSDATA, -          "{" + eidData.getGivenName() + "," -              + eidData.getFamilyName() + "," -              + eidData.getFormatedDateOfBirth() + "," -              + eidData.getCitizenCountryCode() + "}"); +                               "{" + eidData.getGivenName() + "," + eidData.getFamilyName() + "," + eidData +                                   .getFormatedDateOfBirth() + "," + eidData.getCitizenCountryCode() + "}");      }    } +   +  @Data +  private static class SzrResultHolder {     +    final IIdentityLink identityLink; +    final String bpK; +     +  } +     +  /** +   * Build a dummy IdentityLink and a dummy bPK based on eIDAS information. +   *  +   * <br><br> +   * <b>FOR LOCAL TESTING ONLY!!!</b> +   *  +   * @param eidData Information from eIDAS response +   * @return IdentityLink and bPK +   * @throws ParserConfigurationException In case of an IDL processing error +   * @throws SAXException In case of an IDL processing error +   * @throws IOException In case of an IDL processing error +   * @throws EaafException In case of a bPK generation error +   */ +  private SzrResultHolder createDummyIdentityLinkForTestDeployment(ErnbEidData eidData)  +      throws ParserConfigurationException, SAXException, IOException, EaafException { +    log.warn("SZR-Dummy IS ACTIVE! IdentityLink is NOT VALID!!!!"); +    // create fake IdL +    // - fetch IdL template from resources +    final InputStream s = CreateIdentityLinkTask.class +        .getResourceAsStream("/resources/xmldata/fakeIdL_IdL_template.xml"); +    final Element idlTemplate = DomUtils.parseXmlValidating(s); + +    IIdentityLink identityLink = new SimpleIdentityLinkAssertionParser(idlTemplate).parseIdentityLink(); + +    // replace data +    final Element idlassertion = identityLink.getSamlAssertion(); + +    // - set fake baseID; +    final Node prIdentification = XPathUtils +        .selectSingleNode(idlassertion, SimpleIdentityLinkAssertionParser.PERSON_IDENT_VALUE_XPATH); +    prIdentification.getFirstChild().setNodeValue(eidData.getPseudonym()); + +    // - set last name +    final Node prFamilyName = XPathUtils +        .selectSingleNode(idlassertion, SimpleIdentityLinkAssertionParser.PERSON_FAMILY_NAME_XPATH); +    prFamilyName.getFirstChild().setNodeValue(eidData.getFamilyName()); + +    // - set first name +    final Node prGivenName = XPathUtils +        .selectSingleNode(idlassertion, SimpleIdentityLinkAssertionParser.PERSON_GIVEN_NAME_XPATH); +    prGivenName.getFirstChild().setNodeValue(eidData.getGivenName()); + +    // - set date of birth +    final Node prDateOfBirth = XPathUtils +        .selectSingleNode(idlassertion, SimpleIdentityLinkAssertionParser.PERSON_DATE_OF_BIRTH_XPATH); + +    prDateOfBirth.getFirstChild().setNodeValue(eidData.getFormatedDateOfBirth()); + +    identityLink = new SimpleIdentityLinkAssertionParser(idlassertion).parseIdentityLink(); + +    final Pair<String, String> bpkCalc = BpkBuilder +        .generateAreaSpecificPersonIdentifier(identityLink.getIdentificationValue(), +                                              identityLink.getIdentificationType(), +                                              pendingReq.getServiceProviderConfiguration() +                                                        .getAreaSpecificTargetIdentifier());    +    return new SzrResultHolder(identityLink, bpkCalc.getFirst()); +     +  }  } diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/utils/JoseUtils.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/utils/JoseUtils.java new file mode 100644 index 00000000..e81c4c92 --- /dev/null +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/utils/JoseUtils.java @@ -0,0 +1,305 @@ +package at.asitplus.eidas.specific.modules.auth.eidas.v2.utils; + +import at.gv.egiz.eaaf.core.exception.EaafKeyUsageException; +import at.gv.egiz.eaaf.core.exceptions.EaafException; +import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreUtils; +import at.gv.egiz.eaaf.core.impl.data.Pair; +import at.gv.egiz.eaaf.core.impl.utils.X509Utils; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.jose4j.jca.ProviderContext; +import org.jose4j.jwa.AlgorithmConstraints; +import org.jose4j.jws.AlgorithmIdentifiers; +import org.jose4j.jws.JsonWebSignature; +import org.jose4j.jwx.Headers; +import org.jose4j.jwx.JsonWebStructure; +import org.jose4j.keys.resolvers.X509VerificationKeyResolver; +import org.jose4j.lang.JoseException; +import org.springframework.util.Base64Utils; + +import javax.annotation.Nonnull; +import java.io.IOException; +import java.security.Key; +import java.security.KeyStore; +import java.security.Provider; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.RSAPrivateKey; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * {@link JoseUtils} provides static methods JWS and JWE processing. + * + * @author tlenz + * + */ +@Slf4j +public class JoseUtils { + +  /** +   * Create a JWS signature. +   * +   * <p> +   * Use {@link AlgorithmIdentifiers.RSA_PSS_USING_SHA256} in case +   * of a RSA based key and +   * {@link AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256} +   * in case of an ECC based key. +   * </p> +   * +   * @param keyStore               KeyStore that should be used +   * @param keyAlias               Alias of the private key +   * @param keyPassword            Password to access the key +   * @param payLoad                PayLoad to sign +   * @param addFullCertChain       If true the full certificate chain will be +   *                               added, otherwise only the +   *                               X509CertSha256Fingerprint is added into JOSE +   *                               header +   * @param friendlyNameForLogging FriendlyName for the used KeyStore for logging +   *                               purposes only +   * @return Signed PayLoad in serialized form +   * @throws EaafException In case of a key-access or key-usage error +   * @throws JoseException In case of a JOSE error +   */ +  public static String createSignature(@Nonnull Pair<KeyStore, Provider> keyStore, +      @Nonnull final String keyAlias, @Nonnull final char[] keyPassword, +      @Nonnull final String payLoad, boolean addFullCertChain, +      @Nonnull String friendlyNameForLogging) throws EaafException, JoseException { +    return createSignature(keyStore, keyAlias, keyPassword, payLoad, addFullCertChain, Collections.emptyMap(), +        AlgorithmIdentifiers.RSA_PSS_USING_SHA256, AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256, +        friendlyNameForLogging); + +  } + +  /** +   * Create a JWS signature. +   * +   * <p> +   * Use {@link AlgorithmIdentifiers.RSA_PSS_USING_SHA256} in case +   * of a RSA based key and +   * {@link AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256} +   * in case of an ECC based key. +   * </p> +   * +   * @param keyStore               KeyStore that should be used +   * @param keyAlias               Alias of the private key +   * @param keyPassword            Password to access the key +   * @param payLoad                PayLoad to sign +   * @param addFullCertChain       If true the full certificate chain will be +   *                               added, otherwise only the +   *                               X509CertSha256Fingerprint is added into JOSE +   *                               header +   * @param joseHeaders            HeaderName and HeaderValue that should be set +   *                               into JOSE header +   * @param friendlyNameForLogging FriendlyName for the used KeyStore for logging +   *                               purposes only +   * @return Signed PayLoad in serialized form +   * @throws EaafException In case of a key-access or key-usage error +   * @throws JoseException In case of a JOSE error +   */ +  public static String createSignature(@Nonnull Pair<KeyStore, Provider> keyStore, +      @Nonnull final String keyAlias, @Nonnull final char[] keyPassword, +      @Nonnull final String payLoad, boolean addFullCertChain, +      @Nonnull final Map<String, String> joseHeaders, +      @Nonnull String friendlyNameForLogging) throws EaafException, JoseException { +    return createSignature(keyStore, keyAlias, keyPassword, payLoad, addFullCertChain, joseHeaders, +        AlgorithmIdentifiers.RSA_PSS_USING_SHA256, AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256, +        friendlyNameForLogging); + +  } + +  /** +   * Create a JWS signature. +   * +   * @param keyStore               KeyStore that should be used +   * @param keyAlias               Alias of the private key +   * @param keyPassword            Password to access the key +   * @param payLoad                PayLoad to sign +   * @param addFullCertChain       If true the full certificate chain will be +   *                               added, otherwise only the +   *                               X509CertSha256Fingerprint is added into JOSE +   *                               header +   * @param joseHeaders            HeaderName and HeaderValue that should be set +   *                               into JOSE header +   * @param rsaAlgToUse            Signing algorithm that should be used in case +   *                               of a signing key based on RSA +   * @param eccAlgToUse            Signing algorithm that should be used in case +   *                               of a signing key based on ECC +   * @param friendlyNameForLogging FriendlyName for the used KeyStore for logging +   *                               purposes only +   * @return Signed PayLoad in serialized form +   * @throws EaafException In case of a key-access or key-usage error +   * @throws JoseException In case of a JOSE error +   */ +  public static String createSignature(@Nonnull Pair<KeyStore, Provider> keyStore, +      @Nonnull final String keyAlias, @Nonnull final char[] keyPassword, +      @Nonnull final String payLoad, boolean addFullCertChain, +      @Nonnull final Map<String, String> joseHeaders, +      @Nonnull final String rsaAlgToUse, @Nonnull final String eccAlgToUse, +      @Nonnull String friendlyNameForLogging) throws EaafException, JoseException { + +    final JsonWebSignature jws = new JsonWebSignature(); + +    // set payload +    jws.setPayload(payLoad); + +    // set JOSE headers +    for (final Entry<String, String> el : joseHeaders.entrySet()) { +      log.trace("Set JOSE header: {} with value: {} into JWS", el.getKey(), el.getValue()); +      jws.setHeader(el.getKey(), el.getValue()); + +    } + +    // set signing information +    final Pair<Key, X509Certificate[]> signingCred = EaafKeyStoreUtils.getPrivateKeyAndCertificates( +        keyStore.getFirst(), keyAlias, keyPassword, true, friendlyNameForLogging); +    jws.setKey(signingCred.getFirst()); +    jws.setAlgorithmHeaderValue(getKeyOperationAlgorithmFromCredential( +        jws.getKey(), rsaAlgToUse, eccAlgToUse, friendlyNameForLogging)); + +    // set special provider if required +    if (keyStore.getSecond() != null) { +      log.trace("Injecting special Java Security Provider: {}", keyStore.getSecond().getName()); +      final ProviderContext providerCtx = new ProviderContext(); +      providerCtx.getSuppliedKeyProviderContext().setSignatureProvider( +          keyStore.getSecond().getName()); +      jws.setProviderContext(providerCtx); + +    } + +    if (addFullCertChain) { +      jws.setCertificateChainHeaderValue(signingCred.getSecond()); + +    } + +    jws.setX509CertSha256ThumbprintHeaderValue(signingCred.getSecond()[0]); + +    return jws.getCompactSerialization(); + +  } + +  /** +   * Verify a JOSE signature. +   * +   * @param serializedContent Serialized content that should be verified +   * @param trustedCerts      Trusted certificates that should be used for +   *                          verification +   * @param constraints       {@link AlgorithmConstraints} for verification +   * @return {@link JwsResult} object +   * @throws JoseException In case of a signature verification error +   * @throws IOException   In case of a general error +   */ +  public static JwsResult validateSignature(@Nonnull final String serializedContent, +      @Nonnull final List<X509Certificate> trustedCerts, @Nonnull final AlgorithmConstraints constraints) +      throws JoseException, IOException { +    final JsonWebSignature jws = new JsonWebSignature(); +    // set payload +    jws.setCompactSerialization(serializedContent); + +    // set security constrains +    jws.setAlgorithmConstraints(constraints); + +    // load signinc certs +    Key selectedKey = null; +    final List<X509Certificate> x5cCerts = jws.getCertificateChainHeaderValue(); +    final String x5t256 = jws.getX509CertSha256ThumbprintHeaderValue(); +    if (x5cCerts != null) { +      log.debug("Found x509 certificate in JOSE header ... "); +      log.trace("Sorting received X509 certificates ... "); +      final List<X509Certificate> sortedX5cCerts = X509Utils.sortCertificates(x5cCerts); + +      if (trustedCerts.contains(sortedX5cCerts.get(0))) { +        selectedKey = sortedX5cCerts.get(0).getPublicKey(); + +      } else { +        log.info("Can NOT find JOSE certificate in truststore."); +        if (log.isDebugEnabled()) { +          try { +            log.debug("Cert: {}", Base64Utils.encodeToString(sortedX5cCerts.get(0).getEncoded())); + +          } catch (final CertificateEncodingException e) { +            log.warn("Can not create DEBUG output", e); + +          } +        } +      } + +    } else if (StringUtils.isNotEmpty(x5t256)) { +      log.debug("Found x5t256 fingerprint in JOSE header .... "); +      final X509VerificationKeyResolver x509VerificationKeyResolver = new X509VerificationKeyResolver( +          trustedCerts); +      selectedKey = x509VerificationKeyResolver.resolveKey(jws, Collections.<JsonWebStructure>emptyList()); + +    } else { +      throw new JoseException("JWS contains NO signature certificate or NO certificate fingerprint"); + +    } + +    if (selectedKey == null) { +      throw new JoseException("Can NOT select verification key for JWS. Signature verification FAILED"); + +    } + +    // set verification key +    jws.setKey(selectedKey); + +    // load payLoad +    return new JwsResult( +        jws.verifySignature(), +        jws.getUnverifiedPayload(), +        jws.getHeaders(), +        x5cCerts); + +  } + +  /** +   * Select signature algorithm for a given credential. +   * +   * @param key                    {@link X509Credential} that will be used for +   *                               key operations +   * @param rsaSigAlgorithm        RSA based algorithm that should be used in case +   *                               of RSA credential +   * @param ecSigAlgorithm         EC based algorithm that should be used in case +   *                               of RSA credential +   * @param friendlyNameForLogging KeyStore friendlyName for logging purposes +   * @return either the RSA based algorithm or the EC based algorithm +   * @throws EaafKeyUsageException In case of an unsupported private-key type +   */ +  private static String getKeyOperationAlgorithmFromCredential(Key key, +      String rsaSigAlgorithm, String ecSigAlgorithm, String friendlyNameForLogging) +      throws EaafKeyUsageException { +    if (key instanceof RSAPrivateKey) { +      return rsaSigAlgorithm; + +    } else if (key instanceof ECPrivateKey) { +      return ecSigAlgorithm; + +    } else { +      log.warn("Could NOT select the cryptographic algorithm from Private-Key type"); +      throw new EaafKeyUsageException(EaafKeyUsageException.ERROR_CODE_01, +          friendlyNameForLogging, +          "Can not select cryptographic algorithm"); + +    } + +  } + +  private JoseUtils() { + +  } + +  @Getter +  @AllArgsConstructor +  public static class JwsResult { +    final boolean valid; +    final String payLoad; +    final Headers fullJoseHeader; +    final List<X509Certificate> x5cCerts; + +  } +} | 
