Skip to content

Commit

Permalink
add exchange token
Browse files Browse the repository at this point in the history
add Siv
  • Loading branch information
bhuism committed Dec 2, 2024
1 parent 8a3b0c7 commit 7366d62
Show file tree
Hide file tree
Showing 17 changed files with 446 additions and 104 deletions.
14 changes: 14 additions & 0 deletions src/main/java/nl/ictu/Identifier.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package nl.ictu;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public final class Identifier {

@edu.umd.cs.findbugs.annotations.SuppressFBWarnings("SS_SHOULD_BE_STATIC")
private String version = "v1";
private String bsn;

}
6 changes: 6 additions & 0 deletions src/main/java/nl/ictu/PseudoniemenServiceApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,22 @@


import lombok.NoArgsConstructor;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.security.NoSuchAlgorithmException;
import java.security.Security;

@SuppressWarnings("HideUtilityClassConstructor")
@SpringBootApplication
@NoArgsConstructor
public class PseudoniemenServiceApplication {

static {
Security.addProvider(new BouncyCastleProvider());
}

public static void main(final String[] args) throws NoSuchAlgorithmException {
SpringApplication.run(PseudoniemenServiceApplication.class, args);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,20 @@

import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "pseudoniemenservice")
@Getter
@Setter
@Accessors(chain = true)
public class PseudoniemenServiceProperties {

private String tokenPrivateKey;

private String identifierPrivateKey;

}

56 changes: 54 additions & 2 deletions src/main/java/nl/ictu/controller/v1/ExchangeIdentifier.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,74 @@
package nl.ictu.controller.v1;

import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import nl.ictu.Identifier;
import nl.ictu.pseudoniemenservice.generated.server.api.ExchangeIdentifierApi;
import nl.ictu.pseudoniemenservice.generated.server.model.WsExchangeIdentifierForIdentifierRequest;
import nl.ictu.pseudoniemenservice.generated.server.model.WsExchangeTokenForIdentifier200Response;
import nl.ictu.pseudoniemenservice.generated.server.model.WsIdentifier;
import nl.ictu.pseudoniemenservice.generated.server.model.WsIdentifierTypes;
import nl.ictu.service.AesGcmSivCryptographer;
import nl.ictu.service.IdentifierConverter;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;

import static org.springframework.http.HttpStatus.NOT_IMPLEMENTED;
import java.io.IOException;

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;

@RequiredArgsConstructor
@RestController
public final class ExchangeIdentifier implements ExchangeIdentifierApi, VersionOneController {

private final IdentifierConverter identifierConverter;

private final AesGcmSivCryptographer aesGcmSivCryptographer;

@Override
@SneakyThrows
public ResponseEntity<WsExchangeTokenForIdentifier200Response> exchangeIdentifierForIdentifier(final String callerOIN, final WsExchangeIdentifierForIdentifierRequest wsExchangeIdentifierForIdentifierRequest) {

final WsIdentifier wsIdentifierRequest = wsExchangeIdentifierForIdentifierRequest.getIdentifier();

final String recipientOIN = wsExchangeIdentifierForIdentifierRequest.getRecipientOIN();

final WsIdentifierTypes recipientIdentifierType = wsExchangeIdentifierForIdentifierRequest.getRecipientIdentifierType();

if (BSN.equals(wsIdentifierRequest.getType()) && ORGANISATION_PSEUDO.equals(recipientIdentifierType)) {
// from BSN to Org Pseudo
return ResponseEntity.ok(convertBsnToPseudo(wsIdentifierRequest.getValue(), recipientOIN));

} else {
return ResponseEntity.status(UNPROCESSABLE_ENTITY).build();
}


}

private WsExchangeTokenForIdentifier200Response convertBsnToPseudo(final String bsn, final String recipientOIN) throws IOException, InvalidCipherTextException {

final Identifier identifier = new Identifier();

identifier.setBsn(bsn);

final String encode = identifierConverter.encode(identifier);

final String oinNencyptedIdentifier = aesGcmSivCryptographer.encrypt(encode, recipientOIN);

final WsExchangeTokenForIdentifier200Response wsExchangeTokenForIdentifier200Response = new WsExchangeTokenForIdentifier200Response();

final WsIdentifier wsIdentifierResponse = new WsIdentifier();

wsIdentifierResponse.setType(ORGANISATION_PSEUDO);
wsIdentifierResponse.setValue(oinNencyptedIdentifier);

wsExchangeTokenForIdentifier200Response.setIdentifier(wsIdentifierResponse);

return ResponseEntity.status(NOT_IMPLEMENTED).build();
return wsExchangeTokenForIdentifier200Response;


}
Expand Down
39 changes: 32 additions & 7 deletions src/main/java/nl/ictu/controller/v1/ExchangeToken.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,46 @@
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import nl.ictu.Identifier;
import nl.ictu.Token;
import nl.ictu.pseudoniemenservice.generated.server.api.ExchangeTokenApi;
import nl.ictu.pseudoniemenservice.generated.server.model.WsExchangeTokenForIdentifier200Response;
import nl.ictu.pseudoniemenservice.generated.server.model.WsExchangeTokenForIdentifierRequest;
import nl.ictu.pseudoniemenservice.generated.server.model.WsIdentifier;
import nl.ictu.service.Cryptographer;
import nl.ictu.service.AesGcmCryptographer;
import nl.ictu.service.AesGcmSivCryptographer;
import nl.ictu.service.IdentifierConverter;
import nl.ictu.service.TokenConverter;
import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
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.SECTOR_PSEUDO;

@Slf4j
@RequiredArgsConstructor
@RestController
public final class ExchangeToken implements ExchangeTokenApi, VersionOneController {

private final Cryptographer cryptographer;
private final AesGcmCryptographer aesGcmCryptographer;

private final AesGcmSivCryptographer aesGcmSivCryptographer;

private final TokenConverter tokenConverter;

private final ObjectMapper objectMapper;

private final Environment environment;

private final IdentifierConverter identifierConverter;

@Override
@SneakyThrows
public ResponseEntity<WsExchangeTokenForIdentifier200Response> exchangeTokenForIdentifier(final String callerOIN, final WsExchangeTokenForIdentifierRequest wsExchangeTokenForIdentifierRequest) {

final String encodedToken = cryptographer.decrypt(wsExchangeTokenForIdentifierRequest.getToken(), callerOIN);
final String encodedToken = aesGcmCryptographer.decrypt(wsExchangeTokenForIdentifierRequest.getToken(), callerOIN);

final Token token = tokenConverter.decode(encodedToken);

Expand All @@ -52,11 +59,29 @@ public ResponseEntity<WsExchangeTokenForIdentifier200Response> exchangeTokenForI

final WsIdentifier wsIdentifier = new WsIdentifier();

if (StringUtils.hasText(token.getBsn())) {
wsIdentifier.setType(BSN);
wsIdentifier.setValue(token.getBsn());
switch (wsExchangeTokenForIdentifierRequest.getIdentifierType()) {
case BSN -> {
wsIdentifier.setType(BSN);
wsIdentifier.setValue(token.getBsn());
}
case ORGANISATION_PSEUDO -> {

final Identifier identifier = new Identifier();

identifier.setBsn(token.getBsn());

final String encode = identifierConverter.encode(identifier);

final String encrypt = aesGcmSivCryptographer.encrypt(encode, callerOIN);

wsIdentifier.setType(SECTOR_PSEUDO);
wsIdentifier.setValue(encrypt);


}
}


wsExchangeTokenForIdentifier200Response.setIdentifier(wsIdentifier);

return ResponseEntity.ok(wsExchangeTokenForIdentifier200Response);
Expand Down
29 changes: 23 additions & 6 deletions src/main/java/nl/ictu/controller/v1/GetToken.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import nl.ictu.Identifier;
import nl.ictu.Token;
import nl.ictu.pseudoniemenservice.generated.server.api.GetTokenApi;
import nl.ictu.pseudoniemenservice.generated.server.model.WsGetToken200Response;
import nl.ictu.pseudoniemenservice.generated.server.model.WsGetTokenRequest;
import nl.ictu.pseudoniemenservice.generated.server.model.WsIdentifierTypes;
import nl.ictu.service.Cryptographer;
import nl.ictu.service.AesGcmCryptographer;
import nl.ictu.service.AesGcmSivCryptographer;
import nl.ictu.service.IdentifierConverter;
import nl.ictu.service.TokenConverter;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
Expand All @@ -16,10 +18,14 @@
@RequiredArgsConstructor
public final class GetToken implements GetTokenApi, VersionOneController {

private final Cryptographer cryptographer;
private final AesGcmCryptographer aesGcmCryptographer;

private final AesGcmSivCryptographer aesGcmSivCryptographer;

private final TokenConverter tokenConverter;

private final IdentifierConverter identifierConverter;

@SneakyThrows
@Override
public ResponseEntity<WsGetToken200Response> getToken(final String callerOIN, final WsGetTokenRequest wsGetTokenRequest) {
Expand All @@ -34,14 +40,25 @@ public ResponseEntity<WsGetToken200Response> getToken(final String callerOIN, fi
token.setRecipientOIN(wsGetTokenRequest.getRecipientOIN());

if (wsGetTokenRequest.getIdentifier() != null) {
if (WsIdentifierTypes.BSN.equals(wsGetTokenRequest.getIdentifier().getType())) {
token.setBsn(wsGetTokenRequest.getIdentifier().getValue());
switch (wsGetTokenRequest.getIdentifier().getType()) {
case BSN -> token.setBsn(wsGetTokenRequest.getIdentifier().getValue());
case ORGANISATION_PSEUDO -> {

final String orgPseudoEncryptedString = wsGetTokenRequest.getIdentifier().getValue();

final String orgPseudoString = aesGcmSivCryptographer.decrypt(orgPseudoEncryptedString, wsGetTokenRequest.getRecipientOIN());

final Identifier decodedIdentifier = identifierConverter.decode(orgPseudoString);

token.setBsn(decodedIdentifier.getBsn());

}
}
}

final String plainTextToken = tokenConverter.encode(token);

wsGetToken200Response.token(cryptographer.encrypt(plainTextToken, wsGetTokenRequest.getRecipientOIN()));
wsGetToken200Response.token(aesGcmCryptographer.encrypt(plainTextToken, wsGetTokenRequest.getRecipientOIN()));

return ResponseEntity.ok(wsGetToken200Response);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

public interface Cryptographer {
public interface AesGcmCryptographer {

String encrypt(String plaintext, String salt) throws IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@

import static nl.ictu.service.AESHelper.IV_LENGTH;

/**
* Advanced Encryption Standard Galois/Counter Mode (AES-GCM).
*/

@SuppressWarnings("DesignForExtension")
@Service
public class CryptographerImpl implements Cryptographer {
public class AesGcmCryptographerImpl implements AesGcmCryptographer {

//private SecretKey secretKey;

Expand All @@ -38,14 +42,18 @@ public class CryptographerImpl implements Cryptographer {
private final PseudoniemenServiceProperties pseudoniemenServiceProperties;

@SuppressFBWarnings("CT_CONSTRUCTOR_THROW")
public CryptographerImpl(final PseudoniemenServiceProperties pseudoniemenServicePropertiesArg) throws NoSuchAlgorithmException {
public AesGcmCryptographerImpl(final PseudoniemenServiceProperties pseudoniemenServicePropertiesArg) {

this.pseudoniemenServiceProperties = pseudoniemenServicePropertiesArg;
pseudoniemenServiceProperties = pseudoniemenServicePropertiesArg;

this.sha256Digest = MessageDigest.getInstance("SHA-256");
try {
sha256Digest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}

if (!StringUtils.hasText(pseudoniemenServiceProperties.getTokenPrivateKey())) {
throw new RuntimeException("Please set a private key");
throw new RuntimeException("Please set a private token key");
}

}
Expand All @@ -71,7 +79,7 @@ public String encrypt(final String plaintext, final String salt) throws IllegalB
return base64Encoder.encodeToString(encryptedWithIV);
}

private SecretKey createSecretKey(final String salt) throws NoSuchAlgorithmException {
private SecretKey createSecretKey(final String salt) {

byte[] keyBytes = base64Decoder.decode(pseudoniemenServiceProperties.getTokenPrivateKey());

Expand Down
10 changes: 10 additions & 0 deletions src/main/java/nl/ictu/service/AesGcmSivCryptographer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package nl.ictu.service;

import org.bouncycastle.crypto.InvalidCipherTextException;

public interface AesGcmSivCryptographer {

String encrypt(String plaintext, String salt) throws InvalidCipherTextException;

String decrypt(String ciphertext, String salt) throws InvalidCipherTextException;
}
Loading

0 comments on commit 7366d62

Please sign in to comment.