diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml index ac85129..299d66b 100644 --- a/.github/workflows/maven-publish.yml +++ b/.github/workflows/maven-publish.yml @@ -58,12 +58,12 @@ jobs: - name: Run the Maven verify phase run: mvn --batch-mode --update-snapshots verify -# - name: Extract metadata (tags, labels) for Docker -# id: meta -# if: github.event_name != 'pull_request' -# uses: docker/metadata-action@v5 -# with: -# images: ghcr.io/ictu/pseudoniemenservice + # - name: Extract metadata (tags, labels) for Docker + # id: meta + # if: github.event_name != 'pull_request' + # uses: docker/metadata-action@v5 + # with: + # images: ghcr.io/ictu/pseudoniemenservice - name: Login to GHCR uses: docker/login-action@v3 @@ -95,26 +95,26 @@ jobs: --platform linux/${{ matrix.platform }} \ --report-output-dir ./report.toml \ --publish -# -# - name: Set up JDK -# uses: actions/setup-java@v4 -# with: -# java-version: '21' -# distribution: "liberica" -# cache: maven - -# - name: Build Native with Maven -# if: github.event_name != 'pull_request' -# run: mvn -ntp -B clean install spring-boot:build-image -Dspring-boot.build-image.imageName=ghcr.io/ictu/pseudoniemenservice:latest -# -# - name: Build with Maven -# if: github.event_name == 'pull_request' -# run: mvn -ntp -B clean install -# -# - name: Push docker image -# if: github.event_name != 'pull_request' -# run: | -# docker push --platform ${{ matrix.platform }} ghcr.io/ictu/pseudoniemenservice:lates + # + # - name: Set up JDK + # uses: actions/setup-java@v4 + # with: + # java-version: '21' + # distribution: "liberica" + # cache: maven + + # - name: Build Native with Maven + # if: github.event_name != 'pull_request' + # run: mvn -ntp -B clean install spring-boot:build-image -Dspring-boot.build-image.imageName=ghcr.io/ictu/pseudoniemenservice:latest + # + # - name: Build with Maven + # if: github.event_name == 'pull_request' + # run: mvn -ntp -B clean install + # + # - name: Push docker image + # if: github.event_name != 'pull_request' + # run: | + # docker push --platform ${{ matrix.platform }} ghcr.io/ictu/pseudoniemenservice:lates merge: runs-on: ubuntu-latest diff --git a/pom.xml b/pom.xml index c25a9fc..2444c81 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,6 @@ Demo project for Spring Boot 21 - 5.18.2 @@ -40,11 +39,6 @@ spring-boot-starter-test test - - org.webjars - swagger-ui - ${swaggerui.version} - diff --git a/src/main/java/nl/ictu/controller/IndexController.java b/src/main/java/nl/ictu/controller/IndexController.java index f5ef7cd..11dfa2b 100644 --- a/src/main/java/nl/ictu/controller/IndexController.java +++ b/src/main/java/nl/ictu/controller/IndexController.java @@ -24,7 +24,7 @@ public GitProperties getGitProperties() { @GetMapping("/") public String redirectToSwaggerUi(final HttpServletRequest httpServletRequest) { - return "redirect:webjars/swagger-ui/3.38.0/index.html?url=/v1/openapi.yaml"; + return "redirect:/swagger-ui/index.html"; } } diff --git a/src/main/java/nl/ictu/controller/v1/ExchangeIdentifier.java b/src/main/java/nl/ictu/controller/v1/ExchangeIdentifier.java index 4746b84..71251a4 100644 --- a/src/main/java/nl/ictu/controller/v1/ExchangeIdentifier.java +++ b/src/main/java/nl/ictu/controller/v1/ExchangeIdentifier.java @@ -1,8 +1,8 @@ package nl.ictu.controller.v1; import nl.ictu.psuedoniemenservice.generated.server.api.ExchangeIdentifierApi; +import nl.ictu.psuedoniemenservice.generated.server.model.WsExchangeIdentifierForIdentifierRequest; import nl.ictu.psuedoniemenservice.generated.server.model.WsExchangeTokenForIdentifier200Response; -import nl.ictu.psuedoniemenservice.generated.server.model.WsGetTokenRequest; import nl.ictu.psuedoniemenservice.generated.server.model.WsIdentifier; import nl.ictu.psuedoniemenservice.generated.server.model.WsIdentifierTypes; import org.springframework.http.ResponseEntity; @@ -11,8 +11,9 @@ @RestController public class ExchangeIdentifier implements ExchangeIdentifierApi, VersionOneController { + @Override - public ResponseEntity exchangeIdentifierForIdentifier(final WsGetTokenRequest wsGetTokenRequest) { + public ResponseEntity exchangeIdentifierForIdentifier(final WsExchangeIdentifierForIdentifierRequest wsExchangeIdentifierForIdentifierRequest) { final WsIdentifier wsIdentifier = new WsIdentifier() .identifierType(WsIdentifierTypes.BSN) diff --git a/src/main/java/nl/ictu/controller/v1/ExchangeToken.java b/src/main/java/nl/ictu/controller/v1/ExchangeToken.java index 89329bb..8c9e263 100644 --- a/src/main/java/nl/ictu/controller/v1/ExchangeToken.java +++ b/src/main/java/nl/ictu/controller/v1/ExchangeToken.java @@ -1,26 +1,41 @@ package nl.ictu.controller.v1; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; import nl.ictu.psuedoniemenservice.generated.server.api.ExchangeTokenApi; import nl.ictu.psuedoniemenservice.generated.server.model.WsExchangeTokenForIdentifier200Response; import nl.ictu.psuedoniemenservice.generated.server.model.WsExchangeTokenForIdentifierRequest; -import nl.ictu.psuedoniemenservice.generated.server.model.WsIdentifier; -import nl.ictu.psuedoniemenservice.generated.server.model.WsIdentifierTypes; +import nl.ictu.psuedoniemenservice.generated.server.model.WsGetTokenRequest; +import nl.ictu.service.Cryptographer; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RestController; +@RequiredArgsConstructor @RestController public class ExchangeToken implements ExchangeTokenApi, VersionOneController { + private final Cryptographer cryptographer; + + @SneakyThrows @Override public ResponseEntity exchangeTokenForIdentifier(final WsExchangeTokenForIdentifierRequest wsExchangeTokenForIdentifierRequest) { - final WsIdentifier wsIdentifier = new WsIdentifier() - .identifierType(WsIdentifierTypes.BSN) - .identifierValue("123456789"); + final String encodedToken = cryptographer.decrypt(wsExchangeTokenForIdentifierRequest.getToken()); + + final WsGetTokenRequest decodedToken = TokenHelper.decode(encodedToken); + + if (!decodedToken.getReceiverOin().equals(wsExchangeTokenForIdentifierRequest.getReceiverOin())) { + throw new RuntimeException("ReceiverOIN not the same"); + } + + if (!decodedToken.getRequesterOin().equals(wsExchangeTokenForIdentifierRequest.getRequesterOin())) { + throw new RuntimeException("RequesterOIN not the same"); + } + final WsExchangeTokenForIdentifier200Response wsExchangeTokenForIdentifier200Response = new WsExchangeTokenForIdentifier200Response(); - wsExchangeTokenForIdentifier200Response.identifier(wsIdentifier); + wsExchangeTokenForIdentifier200Response.setIdentifier(decodedToken.getIdentifier()); return ResponseEntity.ok(wsExchangeTokenForIdentifier200Response); diff --git a/src/main/java/nl/ictu/controller/v1/GetToken.java b/src/main/java/nl/ictu/controller/v1/GetToken.java index 2c0b2d0..95f126d 100644 --- a/src/main/java/nl/ictu/controller/v1/GetToken.java +++ b/src/main/java/nl/ictu/controller/v1/GetToken.java @@ -1,22 +1,30 @@ package nl.ictu.controller.v1; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; import nl.ictu.psuedoniemenservice.generated.server.api.GetTokenApi; import nl.ictu.psuedoniemenservice.generated.server.model.WsGetToken200Response; import nl.ictu.psuedoniemenservice.generated.server.model.WsGetTokenRequest; +import nl.ictu.service.Cryptographer; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RestController; -import java.util.UUID; - @RestController +@RequiredArgsConstructor public class GetToken implements GetTokenApi, VersionOneController { + + private final Cryptographer cryptographer; + + @SneakyThrows @Override public ResponseEntity getToken(final WsGetTokenRequest wsGetTokenRequest) { - final WsGetToken200Response wsGetToken200Response = new WsGetToken200Response(); - wsGetToken200Response.token(UUID.randomUUID().toString()); + final String plainTextToken = TokenHelper.encode(wsGetTokenRequest); + + wsGetToken200Response.token(cryptographer.encrypt(plainTextToken)); return ResponseEntity.ok(wsGetToken200Response); } + } diff --git a/src/main/java/nl/ictu/controller/v1/IdentifierHelper.java b/src/main/java/nl/ictu/controller/v1/IdentifierHelper.java new file mode 100644 index 0000000..00da8fa --- /dev/null +++ b/src/main/java/nl/ictu/controller/v1/IdentifierHelper.java @@ -0,0 +1,28 @@ +package nl.ictu.controller.v1; + +import nl.ictu.psuedoniemenservice.generated.server.model.WsIdentifier; +import nl.ictu.psuedoniemenservice.generated.server.model.WsIdentifierTypes; + +public final class IdentifierHelper { + + private final static String DELIMITER = ":"; + + public static String encode(final WsIdentifier wsIdentifier) { + return wsIdentifier.getIdentifierType().name() + DELIMITER + wsIdentifier.getIdentifierValue(); + } + + public static WsIdentifier decode(final String encoded) { + + final String[] parts = encoded.split(DELIMITER); + + final WsIdentifier wsIdentifier = new WsIdentifier(); + + wsIdentifier.identifierType(WsIdentifierTypes.fromValue(parts[0])); + + wsIdentifier.identifierValue(parts[1]); + + return wsIdentifier; + + } + +} diff --git a/src/main/java/nl/ictu/controller/v1/TokenHelper.java b/src/main/java/nl/ictu/controller/v1/TokenHelper.java new file mode 100644 index 0000000..32011b5 --- /dev/null +++ b/src/main/java/nl/ictu/controller/v1/TokenHelper.java @@ -0,0 +1,38 @@ +package nl.ictu.controller.v1; + +import nl.ictu.psuedoniemenservice.generated.server.model.WsGetTokenRequest; + +import java.util.StringJoiner; + +public final class TokenHelper { + + private static final String DELIMITER = "_"; + + public static String encode(final WsGetTokenRequest wsGetTokenRequest) { + + final StringJoiner joiner = new StringJoiner(DELIMITER); + + joiner.add(wsGetTokenRequest.getReceiverOin()); + joiner.add(wsGetTokenRequest.getIdentifier().getIdentifierType() + wsGetTokenRequest.getIdentifier().getIdentifierValue()); + + final String encodedToken = wsGetTokenRequest.getRequesterOin() + DELIMITER + IdentifierHelper.encode(wsGetTokenRequest.getIdentifier()) + DELIMITER + wsGetTokenRequest.getReceiverOin(); + + return encodedToken; + + } + + public static WsGetTokenRequest decode(final String encodedToken) { + + final String[] parts = encodedToken.split(DELIMITER); + + final WsGetTokenRequest wsGetTokenRequest = new WsGetTokenRequest(); + + wsGetTokenRequest.setRequesterOin(parts[0]); + wsGetTokenRequest.setIdentifier(IdentifierHelper.decode(parts[1])); + wsGetTokenRequest.setReceiverOin(parts[2]); + + return wsGetTokenRequest; + + } + +} diff --git a/src/main/java/nl/ictu/service/AESHelper.java b/src/main/java/nl/ictu/service/AESHelper.java new file mode 100644 index 0000000..c60e8b0 --- /dev/null +++ b/src/main/java/nl/ictu/service/AESHelper.java @@ -0,0 +1,49 @@ +package nl.ictu.service; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.spec.GCMParameterSpec; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +public final class AESHelper { + + final private static int KEY_LENGTH = 256; + + final public static int IV_LENGTH = 12; + + final private static int TAG_LENGTH = 128; + + final private static String CIPHER = "AES/GCM/NoPadding"; + + final private static SecureRandom secureRandom = new SecureRandom(); + + // Method to generate a random AES key + public static SecretKey generateKey() throws NoSuchAlgorithmException { + KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); + keyGenerator.init(KEY_LENGTH); // 128-bit AES encryption + return keyGenerator.generateKey(); + } + + // Method to generate a random Initialization Vector (IV) + public static GCMParameterSpec generateIV() { + byte[] iv = new byte[IV_LENGTH]; // AES block size is 16 bytes + secureRandom.nextBytes(iv); + + final GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(TAG_LENGTH, iv); + + return gcmParameterSpec; + } + + public static GCMParameterSpec createIVfromValues(byte[] iv) { + final GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(TAG_LENGTH, iv); + return gcmParameterSpec; + } + + public static Cipher createCipher() throws NoSuchPaddingException, NoSuchAlgorithmException { + return Cipher.getInstance(CIPHER); + } + +} \ No newline at end of file diff --git a/src/main/java/nl/ictu/service/Cryptographer.java b/src/main/java/nl/ictu/service/Cryptographer.java new file mode 100644 index 0000000..0fba1d2 --- /dev/null +++ b/src/main/java/nl/ictu/service/Cryptographer.java @@ -0,0 +1,15 @@ +package nl.ictu.service; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +public interface Cryptographer { + + String encrypt(String plaintext) throws IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException; + + String decrypt(String ciphertext) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException; +} diff --git a/src/main/java/nl/ictu/service/CryptographerImpl.java b/src/main/java/nl/ictu/service/CryptographerImpl.java new file mode 100644 index 0000000..a8aa342 --- /dev/null +++ b/src/main/java/nl/ictu/service/CryptographerImpl.java @@ -0,0 +1,78 @@ +package nl.ictu.service; + +import org.springframework.stereotype.Service; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.spec.GCMParameterSpec; +import java.nio.charset.StandardCharsets; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.Base64; + +import static nl.ictu.service.AESHelper.IV_LENGTH; + +@Service +public class CryptographerImpl implements Cryptographer { + + final static SecretKey secretKey; + + final static Base64.Encoder base64Encoder = Base64.getEncoder(); + + final static Base64.Decoder base64Decoder = Base64.getDecoder(); + + static { + try { + secretKey = AESHelper.generateKey(); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + @Override + public String encrypt(final String plaintext) throws IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException { + + final Cipher cipher = AESHelper.createCipher(); + + final GCMParameterSpec gcmParameterSpec = AESHelper.generateIV(); + + cipher.init(Cipher.ENCRYPT_MODE, secretKey, gcmParameterSpec); + + byte[] ciphertext = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8)); + + byte[] encryptedWithIV = new byte[IV_LENGTH + ciphertext.length]; + + System.arraycopy(gcmParameterSpec.getIV(), 0, encryptedWithIV, 0, IV_LENGTH); + System.arraycopy(ciphertext, 0, encryptedWithIV, IV_LENGTH, ciphertext.length); + +// System.out.println("encrytedCyhperText enc:" + new String(new byteciphertext)); + + return base64Encoder.encodeToString(encryptedWithIV); + } + + @Override + public String decrypt(final String ciphertextWithIv) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { + + final Cipher cipher = AESHelper.createCipher(); + + final byte[] encryptedWithIV = base64Decoder.decode(ciphertextWithIv); + + byte[] iv = Arrays.copyOfRange(encryptedWithIV, 0, IV_LENGTH); + byte[] ciphertext = Arrays.copyOfRange(encryptedWithIV, IV_LENGTH, encryptedWithIV.length); + + final GCMParameterSpec gcmParameterSpec = AESHelper.createIVfromValues(iv); + + cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmParameterSpec); + + byte[] decryptedText = cipher.doFinal(ciphertext); + + return new String(decryptedText); + + } + +} diff --git a/src/main/resources/public/swagger-ui/index.html b/src/main/resources/public/swagger-ui/index.html index 84ae62d..b85d5c4 100644 --- a/src/main/resources/public/swagger-ui/index.html +++ b/src/main/resources/public/swagger-ui/index.html @@ -1,19 +1,19 @@ - + Swagger UI - - - - - + + + + + - -
- - - - + +
+ + + + diff --git a/src/main/resources/public/swagger-ui/oauth2-redirect.html b/src/main/resources/public/swagger-ui/oauth2-redirect.html index 5640917..135044c 100644 --- a/src/main/resources/public/swagger-ui/oauth2-redirect.html +++ b/src/main/resources/public/swagger-ui/oauth2-redirect.html @@ -6,7 +6,8 @@