Code development platform for open source projects from the European Union institutions :large_blue_circle: EU Login authentication by SMS has been phased out. To see alternatives please check here

Skip to content
Snippets Groups Projects
Commit da6168a5 authored by Flavio Ferraioli's avatar Flavio Ferraioli
Browse files

Merge branch 'release' into 'main'

release 0.8.0

See merge request !17
parents 257132cf 0dac71f9
Branches
Tags
2 merge requests!18Align to main and update version,!17release 0.8.0
Pipeline #240241 failed
Showing
with 346 additions and 22 deletions
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.8.0] - 2024-12-02
### Added
- Added an API endpoint to check the existence of a key pair in the `KeypairController`.
- Converted Helm hooks for cipher secret management into standard resources.
## [0.7.0] - 2024-11-11
### Added
- Initialized microservice.
- Implemented CSR and Keypair APIs.
classDiagram classDiagram
class Todo { class KeyPair {
+uuid id +uuid id
+byte content +byte privateKey
+byte publicKey
+timestamp creationTimestamp
} }
docs/logical_data_model.png

42.8 KiB | W: | H:

docs/logical_data_model.png

8.02 KiB | W: | H:

docs/logical_data_model.png
docs/logical_data_model.png
docs/logical_data_model.png
docs/logical_data_model.png
  • 2-up
  • Swipe
  • Onion skin
{
"openapi": "3.0.1",
"info": {
"title": "OpenAPI definition",
"version": "v0"
},
"servers": [
{
"url": "https://authentication-provider.authority.svc.cluster.local",
"description": "Generated server url"
}
],
"paths": {
"/keypair": {
"get": {
"tags": [
"keypair-controller"
],
"summary": "Get installed Key Pair",
"description": "ONBOARDER_M user get the public/private Key Pair",
"operationId": "getInstalledKeyPair",
"responses": {
"409": {
"description": "Conflict",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ErrorDTO"
}
}
}
},
"200": {
"description": "Key Pair successfully retrieved. The response body contains the details of the requested KeyPair.",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/KeyPairDTO"
}
}
}
},
"404": {
"description": "Key Pair not found. The requested KeyPair does not exist or is not accessible.",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/KeyPairDTO"
}
}
}
},
"401": {
"description": "Unauthorized",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/KeyPairDTO"
}
}
}
}
}
},
"post": {
"tags": [
"keypair-controller"
],
"summary": "Import Key Pair",
"description": "ONBOARDER_M user import a public/private Key Pair and store it to database",
"operationId": "importKeyPair",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ImportKeyPairDTO"
}
}
},
"required": true
},
"responses": {
"409": {
"description": "Conflict",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ErrorDTO"
}
}
}
},
"204": {
"description": "Key Pair generate and stored successfully"
},
"401": {
"description": "Unauthorized"
}
}
},
"head": {
"tags": [
"keypair-controller"
],
"summary": "Keypair Exists",
"description": "ONBOARDER_M checks whether a public/private Key Pair is stored in the database",
"operationId": "existsKeypair",
"responses": {
"409": {
"description": "Conflict",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ErrorDTO"
}
}
}
},
"200": {
"description": "KeyPair is present",
"content": {
"*/*": {
"schema": {
"type": "boolean"
}
}
}
},
"204": {
"description": "KeyPair is not present",
"content": {
"*/*": {
"schema": {
"type": "boolean"
}
}
}
},
"401": {
"description": "Unauthorized",
"content": {
"*/*": {
"schema": {
"type": "boolean"
}
}
}
}
}
}
},
"/keypair/generate": {
"post": {
"tags": [
"keypair-controller"
],
"summary": "Generate Key Pair",
"description": "ONBOARDER_M user generate a public/private Key Pair and store it to database",
"operationId": "generateKeyPair",
"responses": {
"409": {
"description": "Conflict",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ErrorDTO"
}
}
}
},
"204": {
"description": "Key Pair generate and stored successfully"
},
"401": {
"description": "Unauthorized"
}
}
}
},
"/csr/generate": {
"post": {
"tags": [
"csr-controller"
],
"operationId": "generateCSR",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DistinguishedNameDTO"
}
}
},
"required": true
},
"responses": {
"409": {
"description": "Conflict",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ErrorDTO"
}
}
}
},
"200": {
"description": "OK",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/StreamingResponseBody"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"ErrorDTO": {
"type": "object",
"properties": {
"error": {
"type": "string"
},
"elementName": {
"type": "string"
}
}
},
"ImportKeyPairDTO": {
"required": [
"privateKey",
"publicKey"
],
"type": "object",
"properties": {
"publicKey": {
"type": "string"
},
"privateKey": {
"type": "string"
}
}
},
"DistinguishedNameDTO": {
"required": [
"commonName",
"country",
"organization",
"organizationalUnit"
],
"type": "object",
"properties": {
"commonName": {
"type": "string"
},
"organization": {
"type": "string"
},
"organizationalUnit": {
"type": "string"
},
"country": {
"type": "string"
}
}
},
"StreamingResponseBody": {
"type": "object"
},
"KeyPairDTO": {
"type": "object",
"properties": {
"publicKey": {
"type": "string",
"format": "byte"
},
"privateKey": {
"type": "string",
"format": "byte"
}
}
}
}
}
}
PROJECT_VERSION_NUMBER="0.7.1" PROJECT_VERSION_NUMBER="0.8.0"
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.aruba.simpl</groupId> <groupId>com.aruba.simpl</groupId>
<artifactId>simpl-parent</artifactId> <artifactId>simpl-parent</artifactId>
<version>0.7.0</version> <version>0.8.0</version>
<relativePath/> <!-- lookup parent from repository --> <relativePath/> <!-- lookup parent from repository -->
</parent> </parent>
......
...@@ -33,6 +33,11 @@ public class KeypairController implements KeyPairExchange { ...@@ -33,6 +33,11 @@ public class KeypairController implements KeyPairExchange {
keyPairService.generateAndStoreKeyPair(); keyPairService.generateAndStoreKeyPair();
} }
@Override
public Boolean existsKeypair() {
return keyPairService.existsKeyPair();
}
@Operation( @Operation(
summary = "Import Key Pair", summary = "Import Key Pair",
description = "ONBOARDER_M user import a public/private Key Pair and store it to database", description = "ONBOARDER_M user import a public/private Key Pair and store it to database",
...@@ -64,6 +69,6 @@ public class KeypairController implements KeyPairExchange { ...@@ -64,6 +69,6 @@ public class KeypairController implements KeyPairExchange {
}) })
@Override @Override
public KeyPairDTO getInstalledKeyPair() { public KeyPairDTO getInstalledKeyPair() {
return keyPairService.getKeyPair(); return keyPairService.getInstalledKeyPair();
} }
} }
...@@ -7,7 +7,9 @@ public interface KeyPairService { ...@@ -7,7 +7,9 @@ public interface KeyPairService {
void generateAndStoreKeyPair() throws CipherException; void generateAndStoreKeyPair() throws CipherException;
KeyPairDTO getKeyPair() throws CipherException; KeyPairDTO getInstalledKeyPair() throws CipherException;
void importKeyPair(KeyPairDTO keypairImportDTO); void importKeyPair(KeyPairDTO keypairImportDTO);
Boolean existsKeyPair();
} }
...@@ -31,7 +31,7 @@ public class CSRServiceImpl implements CSRService { ...@@ -31,7 +31,7 @@ public class CSRServiceImpl implements CSRService {
@Override @Override
public void generateCSR(DistinguishedNameDTO distinguishedNameDTO, OutputStream output) public void generateCSR(DistinguishedNameDTO distinguishedNameDTO, OutputStream output)
throws IOException, CipherException { throws IOException, CipherException {
var keyPair = keyPairService.getKeyPair(); var keyPair = keyPairService.getInstalledKeyPair();
var csr = new EllipticCertificateSignRequest( var csr = new EllipticCertificateSignRequest(
distinguishedNameDTO, keyPair, simplProperties.certificate().san(), keyPairGenerationAlgorithm); distinguishedNameDTO, keyPair, simplProperties.certificate().san(), keyPairGenerationAlgorithm);
output.write(csr.getRawCsr().getBytes(StandardCharsets.UTF_8)); output.write(csr.getRawCsr().getBytes(StandardCharsets.UTF_8));
......
...@@ -18,6 +18,7 @@ import java.security.NoSuchAlgorithmException; ...@@ -18,6 +18,7 @@ import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec; import java.security.spec.X509EncodedKeySpec;
import java.util.Optional;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
...@@ -62,8 +63,7 @@ public class KeyPairServiceImpl implements KeyPairService { ...@@ -62,8 +63,7 @@ public class KeyPairServiceImpl implements KeyPairService {
var keyFactory = KeyFactory.getInstance(algorithm); var keyFactory = KeyFactory.getInstance(algorithm);
var keyPair = new KeyPair(keyFactory.generatePublic(publicKey), keyFactory.generatePrivate(privateKey)); var keyPair = new KeyPair(keyFactory.generatePublic(publicKey), keyFactory.generatePrivate(privateKey));
if (!keyPair.getPrivate().getAlgorithm().equals(algorithm) if (!keyPair.getPrivate().getAlgorithm().equals(algorithm)) {
&& !keyPair.getPrivate().getAlgorithm().equals(algorithm)) {
throw new InvalidKeyException(String.format( throw new InvalidKeyException(String.format(
"Invalid key value algorithm. Supported algorithm %s. Private key algorithm: %s, Public key algorithm %s", "Invalid key value algorithm. Supported algorithm %s. Private key algorithm: %s, Public key algorithm %s",
algorithm, privateKey.getAlgorithm(), publicKey.getAlgorithm())); algorithm, privateKey.getAlgorithm(), publicKey.getAlgorithm()));
...@@ -79,22 +79,32 @@ public class KeyPairServiceImpl implements KeyPairService { ...@@ -79,22 +79,32 @@ public class KeyPairServiceImpl implements KeyPairService {
} }
@Override @Override
public KeyPairDTO getKeyPair() { public Boolean existsKeyPair() {
var applicantKeyPair = keyPairRepository.findAllByOrderByCreationTimestampDesc().stream() return getKeyPair().isPresent();
.findFirst() }
.orElseThrow(KeyPairNotFoundException::new);
@Override
public KeyPairDTO getInstalledKeyPair() {
var applicantKeyPair = getKeyPair().orElseThrow(KeyPairNotFoundException::new);
return new KeyPairDTO( return new KeyPairDTO(
cryptoService.decrypt(applicantKeyPair.getPublicKey()), cryptoService.decrypt(applicantKeyPair.getPublicKey()),
cryptoService.decrypt(applicantKeyPair.getPrivateKey())); cryptoService.decrypt(applicantKeyPair.getPrivateKey()));
} }
private Optional<ApplicantKeyPair> getKeyPair() {
return keyPairRepository.findAllByOrderByCreationTimestampDesc().stream()
.findFirst();
}
private void storeKeyPair(KeyPair keyPair) throws CipherException { private void storeKeyPair(KeyPair keyPair) throws CipherException {
keyPairRepository.save(new ApplicantKeyPair( keyPairRepository.save(new ApplicantKeyPair(
cryptoService.encrypt(keyPair.getPublic().getEncoded()), cryptoService.encrypt(keyPair.getPublic().getEncoded()),
cryptoService.encrypt(keyPair.getPrivate().getEncoded()))); cryptoService.encrypt(keyPair.getPrivate().getEncoded())));
// TODO deleteCredentials is only a part of the steps required to disable the participant from the data-space // TODO deleteCredentials is only a part of the steps required to disable the participant from the data-space
// TODO: clean the caches of roles, identity attributes, ephemeral proof etc... // TODO: clean the caches of roles, identity attributes, ephemeral proof etc...
if (credentialExchange.hasCredential()) credentialExchange.delete(); if (credentialExchange.hasCredential()) {
credentialExchange.delete();
}
} }
private KeyPair generateKeyPair() { private KeyPair generateKeyPair() {
......
...@@ -112,21 +112,21 @@ class KeypairControllerTest { ...@@ -112,21 +112,21 @@ class KeypairControllerTest {
void shouldReturn200WithKeyPairWhenFound() { void shouldReturn200WithKeyPairWhenFound() {
// Given // Given
KeyPairDTO expectedKeyPair = Instancio.create(KeyPairDTO.class); KeyPairDTO expectedKeyPair = Instancio.create(KeyPairDTO.class);
when(keyPairService.getKeyPair()).thenReturn(expectedKeyPair); when(keyPairService.getInstalledKeyPair()).thenReturn(expectedKeyPair);
// When // When
KeyPairDTO result = keypairController.getInstalledKeyPair(); KeyPairDTO result = keypairController.getInstalledKeyPair();
// Then // Then
assertThat(result).isNotNull().isEqualTo(expectedKeyPair); assertThat(result).isNotNull().isEqualTo(expectedKeyPair);
verify(keyPairService, times(1)).getKeyPair(); verify(keyPairService, times(1)).getInstalledKeyPair();
} }
@Test @Test
@DisplayName("Should raise exception when no key pair is found") @DisplayName("Should raise exception when no key pair is found")
void shouldRaiseExceptionWhenNoKeyPairFound() { void shouldRaiseExceptionWhenNoKeyPairFound() {
// Given // Given
when(keyPairService.getKeyPair()).thenThrow(new KeyPairNotFoundException()); when(keyPairService.getInstalledKeyPair()).thenThrow(new KeyPairNotFoundException());
// When // When
assertThatThrownBy(() -> keypairController.getInstalledKeyPair()) assertThatThrownBy(() -> keypairController.getInstalledKeyPair())
......
...@@ -71,7 +71,7 @@ class CSRServiceImplTest { ...@@ -71,7 +71,7 @@ class CSRServiceImplTest {
var outputStream = new ByteArrayOutputStream(); var outputStream = new ByteArrayOutputStream();
when(keyPairService.getKeyPair()).thenReturn(keyPairDTO); when(keyPairService.getInstalledKeyPair()).thenReturn(keyPairDTO);
// Act // Act
csrService.generateCSR(distinguishedNameDTO, outputStream); csrService.generateCSR(distinguishedNameDTO, outputStream);
...@@ -84,7 +84,7 @@ class CSRServiceImplTest { ...@@ -84,7 +84,7 @@ class CSRServiceImplTest {
.contains("\n") .contains("\n")
.matches("-----BEGIN CERTIFICATE REQUEST-----\n[A-Za-z0-9+/=]+\n-----END CERTIFICATE REQUEST-----\n"); .matches("-----BEGIN CERTIFICATE REQUEST-----\n[A-Za-z0-9+/=]+\n-----END CERTIFICATE REQUEST-----\n");
verify(keyPairService).getKeyPair(); verify(keyPairService).getInstalledKeyPair();
} }
@Test @Test
...@@ -93,7 +93,7 @@ class CSRServiceImplTest { ...@@ -93,7 +93,7 @@ class CSRServiceImplTest {
var distinguishedNameDTO = new DistinguishedNameDTO(); var distinguishedNameDTO = new DistinguishedNameDTO();
var outputStream = new ByteArrayOutputStream(); var outputStream = new ByteArrayOutputStream();
when(keyPairService.getKeyPair()) when(keyPairService.getInstalledKeyPair())
.thenThrow(new CipherException("Failed to generate key pair", new RuntimeException())); .thenThrow(new CipherException("Failed to generate key pair", new RuntimeException()));
// Act & Assert // Act & Assert
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment