![JAR search and dependency download from the Maven repository](/logo.png)
dev.fitko.fitconnect.core.keys.PublicKeyApiService Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of client Show documentation
Show all versions of client Show documentation
Library that provides client access to the FIT-Connect api-endpoints for sending, subscribing and
routing
package dev.fitko.fitconnect.core.keys;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nimbusds.jose.jwk.KeyOperation;
import com.nimbusds.jose.jwk.RSAKey;
import dev.fitko.fitconnect.api.config.ApplicationConfig;
import dev.fitko.fitconnect.api.domain.model.destination.Destination;
import dev.fitko.fitconnect.api.domain.model.jwk.ApiJwk;
import dev.fitko.fitconnect.api.domain.model.jwk.ApiJwkSet;
import dev.fitko.fitconnect.api.domain.validation.ValidationResult;
import dev.fitko.fitconnect.api.exceptions.internal.InvalidKeyException;
import dev.fitko.fitconnect.api.exceptions.internal.RestApiException;
import dev.fitko.fitconnect.api.services.auth.OAuthService;
import dev.fitko.fitconnect.api.services.http.HttpClient;
import dev.fitko.fitconnect.api.services.keys.KeyService;
import dev.fitko.fitconnect.api.services.validation.ValidationService;
import dev.fitko.fitconnect.core.http.HttpHeaders;
import dev.fitko.fitconnect.core.http.MimeTypes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class PublicKeyApiService implements KeyService {
public static final String DESTINATIONS_KEY_PATH = "/v1/destinations/%s/keys/%s";
public static final String WELL_KNOWN_KEYS_PATH = "/.well-known/jwks.json";
private static final Logger LOGGER = LoggerFactory.getLogger(PublicKeyApiService.class);
private static final ObjectMapper MAPPER = new ObjectMapper();
private final ApplicationConfig config;
private final ValidationService validationService;
private final HttpClient httpClient;
private final OAuthService authService;
public PublicKeyApiService(
final ApplicationConfig config,
final HttpClient httpClient,
final OAuthService authService,
final ValidationService validationService) {
this.config = config;
this.httpClient = httpClient;
this.authService = authService;
this.validationService = validationService;
}
public PublicKeyApiService(final ApplicationConfig config, final HttpClient httpClient, final ValidationService validationService) {
this(config, httpClient, null, validationService);
}
@Override
public RSAKey getPublicEncryptionKey(final Destination destination) {
final String destinationUrl = config.getSubmissionBaseUrl() + DESTINATIONS_KEY_PATH;
final ApiJwk publicKey = performRequest(destinationUrl, ApiJwk.class, getHeaders(), destination.getDestinationId(), destination.getEncryptionKid());
final RSAKey rsaKey = toRSAKey(publicKey);
validateEncryptionKey(rsaKey);
return rsaKey;
}
@Override
public RSAKey getPublicSignatureKey(final UUID destinationId, final String keyId) {
final String destinationUrl = config.getSubmissionBaseUrl() + DESTINATIONS_KEY_PATH;
final ApiJwk signatureKey = performRequest(destinationUrl, ApiJwk.class, getHeaders(), destinationId, keyId);
final RSAKey rsaKey = toRSAKey(signatureKey);
validateSignatureKey(rsaKey);
return rsaKey;
}
@Override
public RSAKey getSubmissionServicePublicKey(final String keyId) {
final String submissionServiceUrl = config.getSubmissionBaseUrl() + WELL_KNOWN_KEYS_PATH;
final ApiJwkSet wellKnownKeys = performRequest(submissionServiceUrl, ApiJwkSet.class, getHeaders());
final RSAKey signatureKey = filterKeysById(keyId, wellKnownKeys.getKeys());
validateSignatureKey(signatureKey);
return signatureKey;
}
@Override
public RSAKey getPortalPublicKey(final String keyId) {
final String portalUrl = config.getSelfServicePortalBaseUrl() + WELL_KNOWN_KEYS_PATH;
final ApiJwkSet wellKnownKeys = performRequest(portalUrl, ApiJwkSet.class, getHeadersWithoutAuth());
final RSAKey signatureKey = filterKeysById(keyId, wellKnownKeys.getKeys());
validateSignatureKey(signatureKey);
return signatureKey;
}
@Override
public RSAKey getWellKnownKeysForSubmissionUrl(final String url, final String keyId) {
final var requestUrl = !url.endsWith("/") ? url + WELL_KNOWN_KEYS_PATH : url;
final ApiJwkSet wellKnownKeys = performRequest(requestUrl, ApiJwkSet.class, getHeadersWithoutAuth(), keyId);
final RSAKey signatureKey = filterKeysById(keyId, wellKnownKeys.getKeys());
validateSignatureKey(signatureKey);
return signatureKey;
}
private RSAKey filterKeysById(final String keyId, final List keys) {
return keys.stream()
.filter(key -> key.getKid().equals(keyId))
.map(this::toRSAKey)
.findFirst()
.orElse(null);
}
private void validateEncryptionKey(final RSAKey rsaKey) {
final ValidationResult result = validationService.validatePublicKey(rsaKey, KeyOperation.WRAP_KEY);
validateResult(result, "Invalid public encryption key");
}
private void validateSignatureKey(final RSAKey rsaKey) {
final ValidationResult result = validationService.validatePublicKey(rsaKey, KeyOperation.VERIFY);
validateResult(result, "Public signature key is not valid");
}
private void validateResult(final ValidationResult validationResult, final String message) {
if (validationResult.hasError()) {
if (config.isAllowInsecurePublicKey()) {
LOGGER.warn(message, validationResult.getError());
} else {
throw new InvalidKeyException(message, validationResult.getError());
}
}
}
private RSAKey toRSAKey(final ApiJwk jwk) {
try {
return RSAKey.parse(MAPPER.writeValueAsString(jwk));
} catch (final JsonProcessingException | ParseException e) {
throw new InvalidKeyException("Key could not be parsed", e);
}
}
private T performRequest(final String url, final Class responseType, final Map headers, final Object... params) {
try {
return httpClient.get(String.format(url, params), headers, responseType).getBody();
} catch (final RestApiException e) {
throw new RestApiException("Request failed", e);
}
}
private Map getHeadersWithoutAuth() {
return new HashMap<>(Map.of(
HttpHeaders.CONTENT_TYPE, MimeTypes.APPLICATION_JSON,
HttpHeaders.ACCEPT_CHARSET, StandardCharsets.UTF_8.toString()));
}
private Map getHeaders() {
final var headers = getHeadersWithoutAuth();
headers.put(HttpHeaders.AUTHORIZATION, "Bearer " + authService.getCurrentToken().getAccessToken());
return headers;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy