From 9062e947c95a20a2bbbe4ecfb5f766bc465c21e3 Mon Sep 17 00:00:00 2001 From: Bas Huisman Date: Tue, 3 Dec 2024 16:19:56 +0100 Subject: [PATCH] make Cryptographers speak Business talk --- .../controller/v1/ExchangeIdentifier.java | 12 +++----- .../nl/ictu/controller/v1/ExchangeToken.java | 8 ++--- .../java/nl/ictu/controller/v1/GetToken.java | 9 +++--- .../ictu/service/AesGcmCryptographerImpl.java | 8 ++--- .../ictu/service/AesGcmSivCryptographer.java | 8 +++-- .../service/AesGcmSivCryptographerImpl.java | 30 ++++++++++++------- .../service/TestAesGcmSivCryptographer.java | 23 ++++++++++---- 7 files changed, 56 insertions(+), 42 deletions(-) diff --git a/src/main/java/nl/ictu/controller/v1/ExchangeIdentifier.java b/src/main/java/nl/ictu/controller/v1/ExchangeIdentifier.java index aa03a33..a2cc841 100644 --- a/src/main/java/nl/ictu/controller/v1/ExchangeIdentifier.java +++ b/src/main/java/nl/ictu/controller/v1/ExchangeIdentifier.java @@ -18,7 +18,7 @@ import static nl.ictu.pseudoniemenservice.generated.server.model.WsIdentifierTypes.BSN; import static nl.ictu.pseudoniemenservice.generated.server.model.WsIdentifierTypes.ORGANISATION_PSEUDO; -import static org.springframework.http.HttpStatus.NOT_IMPLEMENTED; +import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY; @RequiredArgsConstructor @RestController @@ -47,7 +47,7 @@ public ResponseEntity exchangeIdentifierForIdentif return ResponseEntity.ok(convertPseudoToBEsn(wsIdentifierRequest.getValue(), recipientOIN)); } else { - return ResponseEntity.status(NOT_IMPLEMENTED).build(); + return ResponseEntity.status(UNPROCESSABLE_ENTITY).build(); } @@ -59,9 +59,7 @@ private WsExchangeIdentifierResponse convertBsnToPseudo(final String bsn, final identifier.setBsn(bsn); - final String encode = identifierConverter.encode(identifier); - - final String oinNencyptedIdentifier = aesGcmSivCryptographer.encrypt(encode, oin); + final String oinNencyptedIdentifier = aesGcmSivCryptographer.encrypt(identifier, oin); final WsExchangeIdentifierResponse wsExchangeTokenForIdentifier200Response = new WsExchangeIdentifierResponse(); @@ -78,9 +76,7 @@ private WsExchangeIdentifierResponse convertBsnToPseudo(final String bsn, final private WsExchangeIdentifierResponse convertPseudoToBEsn(final String pseudo, final String oin) throws IOException, InvalidCipherTextException { - final String encodedIdentifier = aesGcmSivCryptographer.decrypt(pseudo, oin); - - final Identifier identifier = identifierConverter.decode(encodedIdentifier); + final Identifier identifier = aesGcmSivCryptographer.decrypt(pseudo, oin); final WsExchangeIdentifierResponse wsExchangeTokenForIdentifier200Response = new WsExchangeIdentifierResponse(); diff --git a/src/main/java/nl/ictu/controller/v1/ExchangeToken.java b/src/main/java/nl/ictu/controller/v1/ExchangeToken.java index e0d661b..4329a64 100644 --- a/src/main/java/nl/ictu/controller/v1/ExchangeToken.java +++ b/src/main/java/nl/ictu/controller/v1/ExchangeToken.java @@ -13,12 +13,12 @@ import nl.ictu.service.AesGcmSivCryptographer; import nl.ictu.service.IdentifierConverter; import nl.ictu.service.TokenConverter; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RestController; import static nl.ictu.pseudoniemenservice.generated.server.model.WsIdentifierTypes.BSN; import static nl.ictu.pseudoniemenservice.generated.server.model.WsIdentifierTypes.ORGANISATION_PSEUDO; +import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY; @Slf4j @RequiredArgsConstructor @@ -60,16 +60,14 @@ public ResponseEntity exchangeTokenForIdentifier(final identifier.setBsn(token.getBsn()); - final String encode = identifierConverter.encode(identifier); - - final String encrypt = aesGcmSivCryptographer.encrypt(encode, callerOIN); + final String encrypt = aesGcmSivCryptographer.encrypt(identifier, callerOIN); wsIdentifier.setType(ORGANISATION_PSEUDO); wsIdentifier.setValue(encrypt); } default -> { - return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).build(); + return ResponseEntity.status(UNPROCESSABLE_ENTITY).build(); } } diff --git a/src/main/java/nl/ictu/controller/v1/GetToken.java b/src/main/java/nl/ictu/controller/v1/GetToken.java index d052bf9..5c0870c 100644 --- a/src/main/java/nl/ictu/controller/v1/GetToken.java +++ b/src/main/java/nl/ictu/controller/v1/GetToken.java @@ -11,10 +11,11 @@ import nl.ictu.service.AesGcmSivCryptographer; import nl.ictu.service.IdentifierConverter; import nl.ictu.service.TokenConverter; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RestController; +import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY; + @RestController @RequiredArgsConstructor public final class GetToken implements GetTokenApi, VersionOneController { @@ -47,15 +48,13 @@ public ResponseEntity getToken(final String callerOIN, final final String orgPseudoEncryptedString = wsGetTokenRequest.getIdentifier().getValue(); - final String orgPseudoString = aesGcmSivCryptographer.decrypt(orgPseudoEncryptedString, wsGetTokenRequest.getRecipientOIN()); - - final Identifier decodedIdentifier = identifierConverter.decode(orgPseudoString); + final Identifier decodedIdentifier = aesGcmSivCryptographer.decrypt(orgPseudoEncryptedString, wsGetTokenRequest.getRecipientOIN()); token.setBsn(decodedIdentifier.getBsn()); } default -> { - return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).build(); + return ResponseEntity.status(UNPROCESSABLE_ENTITY).build(); } } } diff --git a/src/main/java/nl/ictu/service/AesGcmCryptographerImpl.java b/src/main/java/nl/ictu/service/AesGcmCryptographerImpl.java index efd8216..48df88c 100644 --- a/src/main/java/nl/ictu/service/AesGcmCryptographerImpl.java +++ b/src/main/java/nl/ictu/service/AesGcmCryptographerImpl.java @@ -1,6 +1,7 @@ package nl.ictu.service; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import lombok.SneakyThrows; import nl.ictu.configuration.PseudoniemenServiceProperties; import nl.ictu.utils.ByteArrayUtils; import org.springframework.stereotype.Service; @@ -42,15 +43,12 @@ public class AesGcmCryptographerImpl implements AesGcmCryptographer { private final PseudoniemenServiceProperties pseudoniemenServiceProperties; @SuppressFBWarnings("CT_CONSTRUCTOR_THROW") + @SneakyThrows public AesGcmCryptographerImpl(final PseudoniemenServiceProperties pseudoniemenServicePropertiesArg) { pseudoniemenServiceProperties = pseudoniemenServicePropertiesArg; - try { - sha256Digest = MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } + sha256Digest = MessageDigest.getInstance("SHA-256"); if (!StringUtils.hasText(pseudoniemenServiceProperties.getTokenPrivateKey())) { throw new RuntimeException("Please set a private token key"); diff --git a/src/main/java/nl/ictu/service/AesGcmSivCryptographer.java b/src/main/java/nl/ictu/service/AesGcmSivCryptographer.java index 87fea6d..56872fe 100644 --- a/src/main/java/nl/ictu/service/AesGcmSivCryptographer.java +++ b/src/main/java/nl/ictu/service/AesGcmSivCryptographer.java @@ -1,10 +1,14 @@ package nl.ictu.service; +import com.fasterxml.jackson.core.JsonProcessingException; +import nl.ictu.Identifier; import org.bouncycastle.crypto.InvalidCipherTextException; +import java.io.IOException; + public interface AesGcmSivCryptographer { - String encrypt(String plaintext, String salt) throws InvalidCipherTextException; + String encrypt(Identifier identifier, String salt) throws InvalidCipherTextException, IOException; - String decrypt(String ciphertext, String salt) throws InvalidCipherTextException; + Identifier decrypt(String ciphertext, String salt) throws InvalidCipherTextException, JsonProcessingException; } diff --git a/src/main/java/nl/ictu/service/AesGcmSivCryptographerImpl.java b/src/main/java/nl/ictu/service/AesGcmSivCryptographerImpl.java index 31d12dc..33f1184 100644 --- a/src/main/java/nl/ictu/service/AesGcmSivCryptographerImpl.java +++ b/src/main/java/nl/ictu/service/AesGcmSivCryptographerImpl.java @@ -1,7 +1,10 @@ package nl.ictu.service; +import com.fasterxml.jackson.core.JsonProcessingException; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import nl.ictu.Identifier; import nl.ictu.configuration.PseudoniemenServiceProperties; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.engines.AESEngine; @@ -11,9 +14,9 @@ import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.Base64; @@ -38,18 +41,17 @@ public class AesGcmSivCryptographerImpl implements AesGcmSivCryptographer { private final MessageDigest sha256Digest; + private final IdentifierConverter identifierConverter; + @SuppressFBWarnings("CT_CONSTRUCTOR_THROW") - public AesGcmSivCryptographerImpl(final PseudoniemenServiceProperties pseudoniemenServicePropertiesArg) { + @SneakyThrows + public AesGcmSivCryptographerImpl(final PseudoniemenServiceProperties pseudoniemenServicePropertiesArg, final IdentifierConverter identifierConverterArg) { pseudoniemenServiceProperties = pseudoniemenServicePropertiesArg; + identifierConverter = identifierConverterArg; aesEngine = new AESEngine(); - - try { - this.sha256Digest = MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } + sha256Digest = MessageDigest.getInstance("SHA-256"); if (!StringUtils.hasText(pseudoniemenServiceProperties.getIdentifierPrivateKey())) { throw new RuntimeException("Please set a private identifier key"); @@ -74,7 +76,9 @@ private AEADParameters createSecretKey(final String salt) { } @Override - public String encrypt(final String plaintext, final String salt) throws InvalidCipherTextException { + public String encrypt(final Identifier identifier, final String salt) throws InvalidCipherTextException, IOException { + + final String plaintext = identifierConverter.encode(identifier); final GCMSIVBlockCipher cipher = new GCMSIVBlockCipher(aesEngine); @@ -95,7 +99,7 @@ public String encrypt(final String plaintext, final String salt) throws InvalidC } @Override - public String decrypt(final String ciphertextString, final String salt) throws InvalidCipherTextException { + public Identifier decrypt(final String ciphertextString, final String salt) throws InvalidCipherTextException, JsonProcessingException { final GCMSIVBlockCipher cipher = new GCMSIVBlockCipher(aesEngine); @@ -111,7 +115,11 @@ public String decrypt(final String ciphertextString, final String salt) throws I cipher.reset(); - return new String(plaintext, StandardCharsets.UTF_8); + final String encodedIdentifier = new String(plaintext, StandardCharsets.UTF_8); + + final Identifier identifier = identifierConverter.decode(encodedIdentifier); + + return identifier; } diff --git a/src/test/java/nl/ictu/service/TestAesGcmSivCryptographer.java b/src/test/java/nl/ictu/service/TestAesGcmSivCryptographer.java index d35d4de..6c0d1c5 100644 --- a/src/test/java/nl/ictu/service/TestAesGcmSivCryptographer.java +++ b/src/test/java/nl/ictu/service/TestAesGcmSivCryptographer.java @@ -1,7 +1,9 @@ package nl.ictu.service; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; +import nl.ictu.Identifier; import nl.ictu.configuration.PseudoniemenServiceProperties; import org.junit.jupiter.api.Test; import org.springframework.test.context.ActiveProfiles; @@ -20,7 +22,10 @@ @ActiveProfiles("test") public class TestAesGcmSivCryptographer { - private AesGcmSivCryptographer aesGcmSivCryptographer = new AesGcmSivCryptographerImpl(new PseudoniemenServiceProperties().setIdentifierPrivateKey("QTBtVEhLN3EwMHJ3QXN1ZUFqNzVrT3hDQTBIWWNIZTU=")); + private AesGcmSivCryptographer aesGcmSivCryptographer = new AesGcmSivCryptographerImpl( + new PseudoniemenServiceProperties().setIdentifierPrivateKey("QTBtVEhLN3EwMHJ3QXN1ZUFqNzVrT3hDQTBIWWNIZTU="), + new IdentifierConverterImpl(new ObjectMapper()) + ); private Set testStrings = new HashSet<>(Arrays.asList("a", "bb", "dsv", "ghad", "dhaht", "uDg5Av", "d93fdvv", "dj83hzHo", "38iKawKv9", "dk(gkzm)Mh", "gjk)s3$g9cQ")); @@ -30,9 +35,12 @@ public void testEncyptDecryptForDifferentStringLengths() { testStrings.forEach(plain -> { try { - final String crypted = aesGcmSivCryptographer.encrypt(plain, "helloHowAreyo12345678"); - final String actual = aesGcmSivCryptographer.decrypt(crypted, "helloHowAreyo12345678"); - assertThat(actual).isEqualTo(plain); + final Identifier identifier = new Identifier(); + identifier.setBsn(plain); + + final String crypted = aesGcmSivCryptographer.encrypt(identifier, "helloHowAreyo12345678"); + final Identifier actual = aesGcmSivCryptographer.decrypt(crypted, "helloHowAreyo12345678"); + assertThat(actual.getBsn()).isEqualTo(plain); } catch (final Exception e) { throw new RuntimeException(e); } @@ -48,8 +56,11 @@ public void testCiphertextIsTheSameForSamePlaintext() throws Exception { // The same plaintext message String plaintext = "This is a test message to ensure ciphertext is different!"; - String encryptedMessage1 = aesGcmSivCryptographer.encrypt(plaintext, "aniceSaltGorYu"); - String encryptedMessage2 = aesGcmSivCryptographer.encrypt(plaintext, "aniceSaltGorYu"); + final Identifier identifier = new Identifier(); + identifier.setBsn(plaintext); + + String encryptedMessage1 = aesGcmSivCryptographer.encrypt(identifier, "aniceSaltGorYu"); + String encryptedMessage2 = aesGcmSivCryptographer.encrypt(identifier, "aniceSaltGorYu"); // Assert that the two ciphertexts are different assertThat(encryptedMessage1).isEqualTo(encryptedMessage2);