Skip to content

Commit

Permalink
add interfaces to hide implementation details more
Browse files Browse the repository at this point in the history
  • Loading branch information
bhuism committed Jan 9, 2025
1 parent 0ed373a commit 2258f29
Show file tree
Hide file tree
Showing 52 changed files with 994 additions and 927 deletions.
5 changes: 3 additions & 2 deletions src/main/java/nl/ictu/PseudoniemenServiceApplication.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package nl.ictu;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.security.Security;
import lombok.NoArgsConstructor;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.security.Security;

@SuppressWarnings({"HideUtilityClassConstructor"})
@SuppressFBWarnings(value = "EI_EXPOSE_STATIC_REP2",
justification = "nl.ictu.PseudoniemenServiceApplication$$SpringCGLIB$$0")
justification = "nl.ictu.PseudoniemenServiceApplication$$SpringCGLIB$$0")
@SpringBootApplication
@NoArgsConstructor
public class PseudoniemenServiceApplication {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ public final class PseudoniemenServiceProperties {

/**
* Validates that the required private keys for the token and identifier are set.
*
* <p>
* This method performs a post-construction validation of the `PseudoniemenServiceProperties` object to ensure that
* the `tokenPrivateKey` and `identifierPrivateKey` are properly configured. If either of these properties is not set
* or is empty, specific exceptions are thrown:
*
* <p>
* - If `tokenPrivateKey` is null or empty, a {@link TokenPrivateKeyException} is thrown.
* - If `identifierPrivateKey` is null or empty, a {@link IdentifierPrivateKeyException} is thrown.
*
* @throws TokenPrivateKeyException if the `tokenPrivateKey` is missing or empty.
* @throws TokenPrivateKeyException if the `tokenPrivateKey` is missing or empty.
* @throws IdentifierPrivateKeyException if the `identifierPrivateKey` is missing or empty.
*/
@PostConstruct
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/nl/ictu/controller/GlobalExceptionHandler.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package nl.ictu.controller;

import lombok.extern.slf4j.Slf4j;
import nl.ictu.service.exception.InvalidOINException;
import nl.ictu.service.exception.IdentifierPrivateKeyException;
import nl.ictu.service.exception.InvalidOINException;
import nl.ictu.service.exception.InvalidWsIdentifierRequestTypeException;
import nl.ictu.service.exception.InvalidWsIdentifierTokenException;
import nl.ictu.service.exception.TokenPrivateKeyException;
Expand Down Expand Up @@ -31,8 +31,8 @@ public ResponseEntity<String> handleGenericException(final Exception ex) {

log.error("Unexpected error occurred", ex);
return new ResponseEntity<>(
"An unexpected error occurred: " + ex.getMessage(),
HttpStatus.INTERNAL_SERVER_ERROR
"An unexpected error occurred: " + ex.getMessage(),
HttpStatus.INTERNAL_SERVER_ERROR
);
}

Expand Down Expand Up @@ -63,7 +63,7 @@ public String handleIdentifierPrivateKeyException(final IdentifierPrivateKeyExce
@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
@ResponseBody
public String handleInvalidWsIdentifierRequestTypeException(
final InvalidWsIdentifierRequestTypeException ex) {
final InvalidWsIdentifierRequestTypeException ex) {

return ex.getMessage();
}
Expand All @@ -79,7 +79,7 @@ public String handleInvalidWsIdentifierRequestTypeException(
@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
@ResponseBody
public String handleInvalidWsIdentifierTokenException(
final InvalidWsIdentifierTokenException ex) {
final InvalidWsIdentifierTokenException ex) {

return ex.getMessage();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ public final class ExchangeTokenController implements ExchangeTokenApi, VersionO
*/
@Override
public ResponseEntity<WsExchangeTokenResponse> exchangeToken(final String callerOIN,
final WsExchangeTokenRequest wsExchangeTokenForIdentifierRequest) {
final WsExchangeTokenRequest wsExchangeTokenForIdentifierRequest) {

final var wsExchangeTokenResponse = exchangeTokenService.exchangeToken(callerOIN,
wsExchangeTokenForIdentifierRequest);
wsExchangeTokenForIdentifierRequest);
return ResponseEntity.ok(wsExchangeTokenResponse);
}
}
4 changes: 2 additions & 2 deletions src/main/java/nl/ictu/controller/v1/GetTokenController.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ public final class GetTokenController implements GetTokenApi, VersionOneControll
*/
@Override
public ResponseEntity<WsGetTokenResponse> getToken(final String callerOIN,
final WsGetTokenRequest wsGetTokenRequest) {
final WsGetTokenRequest wsGetTokenRequest) {

final var recipientOIN = wsGetTokenRequest.getRecipientOIN();
final var identifier = wsGetTokenRequest.getIdentifier();
final var wsGetTokenResponse = getTokenService.getWsGetTokenResponse(
recipientOIN, identifier);
recipientOIN, identifier);
return ResponseEntity.ok(wsGetTokenResponse);
}
}
17 changes: 9 additions & 8 deletions src/main/java/nl/ictu/service/ExchangeIdentifierService.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
package nl.ictu.service;

import static nl.ictu.pseudoniemenservice.generated.server.model.WsIdentifierTypes.BSN;
import static nl.ictu.pseudoniemenservice.generated.server.model.WsIdentifierTypes.ORGANISATION_PSEUDO;

import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import nl.ictu.pseudoniemenservice.generated.server.model.WsExchangeIdentifierRequest;
Expand All @@ -12,6 +9,9 @@
import nl.ictu.service.map.PseudoBsnMapper;
import org.springframework.stereotype.Service;

import static nl.ictu.pseudoniemenservice.generated.server.model.WsIdentifierTypes.BSN;
import static nl.ictu.pseudoniemenservice.generated.server.model.WsIdentifierTypes.ORGANISATION_PSEUDO;

@RequiredArgsConstructor
@Service
public final class ExchangeIdentifierService {
Expand All @@ -22,7 +22,8 @@ public final class ExchangeIdentifierService {
/**
* Processes the exchange of an identifier between different types based on specific mappings
* and returns the corresponding response.
* caller.
* caller.
*
* @param wsExchangeIdentifierForIdentifierRequest The request object containing details of the
* identifier to be exchanged, including its
* value, type, recipient OIN, and recipient
Expand All @@ -32,19 +33,19 @@ public final class ExchangeIdentifierService {
*/
@SneakyThrows
public WsExchangeIdentifierResponse exchangeIdentifier(
final WsExchangeIdentifierRequest wsExchangeIdentifierForIdentifierRequest) {
final WsExchangeIdentifierRequest wsExchangeIdentifierForIdentifierRequest) {

final var wsIdentifierRequest = wsExchangeIdentifierForIdentifierRequest.getIdentifier();
final var recipientOIN = wsExchangeIdentifierForIdentifierRequest.getRecipientOIN();
final var recipientIdentifierType = wsExchangeIdentifierForIdentifierRequest.getRecipientIdentifierType();
if (BSN.equals(wsIdentifierRequest.getType()) && ORGANISATION_PSEUDO.equals(
recipientIdentifierType)) {
recipientIdentifierType)) {
return bsnPseudoMapper.map(wsIdentifierRequest.getValue(), recipientOIN);
} else if (ORGANISATION_PSEUDO.equals(wsIdentifierRequest.getType()) && BSN.equals(
recipientIdentifierType)) {
recipientIdentifierType)) {
return pseudoBsnMapper.map(wsIdentifierRequest.getValue(), recipientOIN);
}
throw new InvalidWsIdentifierRequestTypeException(
"Invalid WsIdentifierRequest type cannot be processed.");
"Invalid WsIdentifierRequest type cannot be processed.");
}
}
20 changes: 10 additions & 10 deletions src/main/java/nl/ictu/service/ExchangeTokenService.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import nl.ictu.service.exception.InvalidOINException;
import nl.ictu.pseudoniemenservice.generated.server.model.WsExchangeTokenRequest;
import nl.ictu.pseudoniemenservice.generated.server.model.WsExchangeTokenResponse;
import nl.ictu.service.crypto.AesGcmCryptographerService;
import nl.ictu.service.crypto.TokenConverter;
import nl.ictu.service.exception.InvalidOINException;
import nl.ictu.service.exception.InvalidWsIdentifierTokenException;
import nl.ictu.crypto.AesGcmCryptographer;
import nl.ictu.crypto.TokenCoder;
import nl.ictu.service.map.BsnTokenMapper;
import nl.ictu.service.map.OrganisationPseudoTokenMapper;
import nl.ictu.service.validate.OINValidator;
Expand All @@ -19,8 +19,8 @@
@RestController
public final class ExchangeTokenService {

private final AesGcmCryptographer aesGcmCryptographer;
private final TokenCoder tokenCoder;
private final AesGcmCryptographerService aesGcmCryptographerService;
private final TokenConverter tokenConverter;
private final OINValidator oinValidator;
private final OrganisationPseudoTokenMapper organisationPseudoTokenMapper;
private final BsnTokenMapper bsnTokenMapper;
Expand All @@ -41,11 +41,11 @@ public final class ExchangeTokenService {
*/
@SneakyThrows
public WsExchangeTokenResponse exchangeToken(final String callerOIN,
final WsExchangeTokenRequest wsExchangeTokenForIdentifierRequest) {
final WsExchangeTokenRequest wsExchangeTokenForIdentifierRequest) {

final var encodedToken = aesGcmCryptographer.decrypt(
wsExchangeTokenForIdentifierRequest.getToken(), callerOIN);
final var token = tokenCoder.decode(encodedToken);
final var encodedToken = aesGcmCryptographerService.decrypt(
wsExchangeTokenForIdentifierRequest.getToken(), callerOIN);
final var token = tokenConverter.decode(encodedToken);
if (!oinValidator.isValid(callerOIN, token)) {
throw new InvalidOINException("CallerOIN and token are mismatched.");
}
Expand All @@ -57,7 +57,7 @@ public WsExchangeTokenResponse exchangeToken(final String callerOIN,
return organisationPseudoTokenMapper.map(callerOIN, token);
}
default -> throw new InvalidWsIdentifierTokenException(
"Invalid identifier cannot be processed.");
"Invalid identifier cannot be processed.");
}
}
}
2 changes: 1 addition & 1 deletion src/main/java/nl/ictu/service/GetTokenService.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public final class GetTokenService {
*/
@SneakyThrows
public WsGetTokenResponse getWsGetTokenResponse(final String recipientOIN,
final WsIdentifier identifier) {
final WsIdentifier identifier) {

final var creationDate = System.currentTimeMillis();
// check is callerOIN allowed to communicatie with sinkOIN
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package nl.ictu.service.crypto;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

public interface AesGcmCryptographerService {
String encrypt(String plaintext, String salt)
throws IllegalBlockSizeException,
BadPaddingException,
InvalidAlgorithmParameterException,
InvalidKeyException,
NoSuchAlgorithmException,
NoSuchPaddingException;

SecretKey createSecretKey(String salt);

String decrypt(String ciphertextWithIv, String salt)
throws NoSuchPaddingException,
NoSuchAlgorithmException,
InvalidAlgorithmParameterException,
InvalidKeyException,
IllegalBlockSizeException,
BadPaddingException;
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,5 @@
package nl.ictu.crypto;
package nl.ictu.service.crypto;

import static nl.ictu.utils.AesUtility.IV_LENGTH;

import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import lombok.RequiredArgsConstructor;
import nl.ictu.configuration.PseudoniemenServiceProperties;
import nl.ictu.utils.AesUtility;
Expand All @@ -21,12 +8,26 @@
import nl.ictu.utils.MessageDigestWrapper;
import org.springframework.stereotype.Component;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

import static nl.ictu.utils.AesUtility.IV_LENGTH;

/**
* Advanced Encryption Standard Galois/Counter Mode (AES-GCM).
*/
@Component
@RequiredArgsConstructor
public class AesGcmCryptographer {
public class AesGcmCryptographerServiceImpl implements AesGcmCryptographerService {

private final Base64Wrapper base64Wrapper;
private final MessageDigestWrapper messageDigestWrapper;
Expand All @@ -50,13 +51,14 @@ public class AesGcmCryptographer {
* available
* @throws NoSuchPaddingException if the requested padding scheme is not available
*/
@Override
public String encrypt(final String plaintext, final String salt)
throws IllegalBlockSizeException,
BadPaddingException,
InvalidAlgorithmParameterException,
InvalidKeyException,
NoSuchAlgorithmException,
NoSuchPaddingException {
throws IllegalBlockSizeException,
BadPaddingException,
InvalidAlgorithmParameterException,
InvalidKeyException,
NoSuchAlgorithmException,
NoSuchPaddingException {

if (plaintext == null || plaintext.isEmpty()) {
throw new IllegalArgumentException("Plaintext cannot be null or empty");
Expand All @@ -82,10 +84,11 @@ public String encrypt(final String plaintext, final String salt)
* key
* @return a SecretKey instance derived from the combined and hashed input
*/
private SecretKey createSecretKey(final String salt) {
@Override
public SecretKey createSecretKey(final String salt) {

final var keyBytes = base64Wrapper.decode(
pseudoniemenServiceProperties.getTokenPrivateKey());
pseudoniemenServiceProperties.getTokenPrivateKey());
final var saltBytes = salt.getBytes(StandardCharsets.UTF_8);
final var salterSecretBytes = ByteArrayUtil.concat(keyBytes, saltBytes);
final var key = messageDigestWrapper.getMessageDigestInstance().digest(salterSecretBytes);
Expand All @@ -110,19 +113,20 @@ private SecretKey createSecretKey(final String salt) {
* @throws BadPaddingException if there are issues with padding during
* decryption
*/
@Override
public String decrypt(final String ciphertextWithIv, final String salt)
throws NoSuchPaddingException,
NoSuchAlgorithmException,
InvalidAlgorithmParameterException,
InvalidKeyException,
IllegalBlockSizeException,
BadPaddingException {
throws NoSuchPaddingException,
NoSuchAlgorithmException,
InvalidAlgorithmParameterException,
InvalidKeyException,
IllegalBlockSizeException,
BadPaddingException {

final var cipher = AesUtility.createCipher();
final var encryptedWithIV = base64Wrapper.decode(ciphertextWithIv);
final var iv = Arrays.copyOfRange(encryptedWithIV, 0, IV_LENGTH);
final var ciphertext = Arrays.copyOfRange(encryptedWithIV, IV_LENGTH,
encryptedWithIV.length);
encryptedWithIV.length);
final var gcmParameterSpec = AesUtility.createIVfromValues(iv);
final var secretKey = createSecretKey(salt);
cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmParameterSpec);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package nl.ictu.service.crypto;

import lombok.SneakyThrows;
import nl.ictu.model.Identifier;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.params.AEADParameters;

import java.io.IOException;

public interface AesGcmSivCryptographerService {
AEADParameters createSecretKey(String salt);

String encrypt(Identifier identifier, String salt)
throws InvalidCipherTextException, IOException;

@SneakyThrows
Identifier decrypt(String ciphertextString, String salt);
}
Loading

0 comments on commit 2258f29

Please sign in to comment.