diff options
Diffstat (limited to 'eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/service/AuthBlockSigningService.java')
-rw-r--r-- | eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/service/AuthBlockSigningService.java | 192 |
1 files changed, 192 insertions, 0 deletions
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; + + + } + + +} |