diff --git a/pom.xml b/pom.xml index de68716..5fffcbc 100644 --- a/pom.xml +++ b/pom.xml @@ -31,12 +31,13 @@ UTF-8 1.3.1 - 6.5.3 - 2.6.8 - 5.3.19 + 7.1.1 + 2.7.1 + 2021.0.3 + 5.3.22 5.7.1 5.6.5.Final - 1.18.22 + 1.18.24 4.9.1 1.2.0 1.6.21 @@ -106,6 +107,13 @@ pom import + + org.springframework.cloud + spring-cloud-dependencies + ${spring.cloud.version} + pom + import + org.projectlombok lombok @@ -185,6 +193,14 @@ org.liquibase liquibase-core + + org.springframework.cloud + spring-cloud-starter-bootstrap + + + org.springframework.cloud + spring-cloud-starter-vault-config + org.junit.jupiter junit-jupiter-api diff --git a/src/main/java/eu/europa/ec/dgc/issuance/service/impl/SigningServiceImpl.java b/src/main/java/eu/europa/ec/dgc/issuance/service/impl/SigningServiceImpl.java index ef4922d..4c440b8 100644 --- a/src/main/java/eu/europa/ec/dgc/issuance/service/impl/SigningServiceImpl.java +++ b/src/main/java/eu/europa/ec/dgc/issuance/service/impl/SigningServiceImpl.java @@ -15,9 +15,12 @@ import org.bouncycastle.crypto.signers.PSSSigner; import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util; import org.bouncycastle.jce.spec.ECParameterSpec; -import org.springframework.stereotype.Component; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; -@Component + +@Service +@Profile("!vault") public class SigningServiceImpl implements SigningService { @Override public byte[] signHash(byte[] hashBytes, PrivateKey privateKey) { diff --git a/src/main/java/eu/europa/ec/dgc/issuance/service/impl/VaultSigningServiceImpl.java b/src/main/java/eu/europa/ec/dgc/issuance/service/impl/VaultSigningServiceImpl.java new file mode 100644 index 0000000..e68e177 --- /dev/null +++ b/src/main/java/eu/europa/ec/dgc/issuance/service/impl/VaultSigningServiceImpl.java @@ -0,0 +1,48 @@ +package eu.europa.ec.dgc.issuance.service.impl; + +import eu.europa.ec.dgc.issuance.service.SigningService; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.PrivateKey; +import java.security.interfaces.RSAPrivateCrtKey; +import java.util.Base64; +import lombok.RequiredArgsConstructor; +import org.bouncycastle.crypto.CryptoException; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.crypto.engines.RSABlindedEngine; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters; +import org.bouncycastle.crypto.signers.ECDSASigner; +import org.bouncycastle.crypto.signers.PSSSigner; +import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util; +import org.bouncycastle.jce.spec.ECParameterSpec; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; +import org.springframework.vault.core.VaultTemplate; +import org.springframework.vault.support.Plaintext; + + +@Service +@RequiredArgsConstructor +@Profile("vault") +public class VaultSigningServiceImpl implements SigningService { + + private final VaultTemplate vaultTemplate; + + @Value("${dgc.signKey:issuer-key}") + private String signKey; + + @Override + public byte[] signHash(byte[] hash, PrivateKey privateKey) { + String hashBase64 = Base64.getEncoder().encodeToString(hash); + + String signature = vaultTemplate.opsForTransit().sign(signKey, Plaintext.of(hashBase64)).getSignature(); + if (signature.startsWith("vault:v1:")) { + signature = signature.substring(9); + } + return Base64.getDecoder().decode(signature); + } +} diff --git a/src/main/resources/bootstrap-cloud.yaml b/src/main/resources/bootstrap-cloud.yaml new file mode 100644 index 0000000..de153fc --- /dev/null +++ b/src/main/resources/bootstrap-cloud.yaml @@ -0,0 +1,23 @@ +--- +spring: + application: + name: cwa-dcc-rules + cloud: + vault: + ssl: + trust-store: file:${SSL_VAULT_TRUSTSTORE_PATH} + trust-store-password: ${SSL_VAULT_TRUSTSTORE_PASSWORD} + enabled: true + generic: + enabled: false + fail-fast: true + authentication: KUBERNETES + kubernetes: + role: ${VAULT_ROLE} + kubernetes-path: kubernetes + service-account-token-file: /var/run/secrets/kubernetes.io/serviceaccount/token + uri: ${VAULT_URI} + connection-timeout: 5000 + read-timeout: 15000 + config: + order: -10 diff --git a/src/main/resources/bootstrap-vaultlocal.yaml b/src/main/resources/bootstrap-vaultlocal.yaml new file mode 100644 index 0000000..300bd62 --- /dev/null +++ b/src/main/resources/bootstrap-vaultlocal.yaml @@ -0,0 +1,14 @@ +spring: + cloud: + vault: + enabled: true + # use for local vault transit test (change the access token) + token: hvs.s4nIVAiASHG6FNETWBFevasa + scheme: http + fail-fast: true + # use for local vault transit test (change the uri) + uri: http://127.0.0.1:8200 + connection-timeout: 5000 + read-timeout: 15000 + config: + order: -10 diff --git a/src/main/resources/bootstrap.yaml b/src/main/resources/bootstrap.yaml new file mode 100644 index 0000000..4eed0ce --- /dev/null +++ b/src/main/resources/bootstrap.yaml @@ -0,0 +1,5 @@ +--- +spring: + cloud: + vault: + enabled: false diff --git a/src/test/java/eu/europa/ec/dgc/issuance/service/impl/VaultSigningServiceImplTest.java b/src/test/java/eu/europa/ec/dgc/issuance/service/impl/VaultSigningServiceImplTest.java new file mode 100644 index 0000000..8b0e8be --- /dev/null +++ b/src/test/java/eu/europa/ec/dgc/issuance/service/impl/VaultSigningServiceImplTest.java @@ -0,0 +1,56 @@ +package eu.europa.ec.dgc.issuance.service.impl; + +import java.util.Base64; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Profile; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.vault.core.VaultTemplate; +import org.springframework.vault.core.VaultTransitOperations; +import org.springframework.vault.support.Plaintext; +import org.springframework.vault.support.Signature; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@SpringBootTest +@ActiveProfiles("vault") +class VaultSigningServiceImplTest { + + @Autowired + private VaultSigningServiceImpl vaultSigningService; + @MockBean + private VaultTemplate vaultTemplate; + + @Test + void signHash() { + byte[] hash = Base64.getDecoder().decode("dGVzdA=="); + + VaultTransitOperations vaultTransitOperation = mock(VaultTransitOperations.class); + when(vaultTemplate.opsForTransit()).thenReturn(vaultTransitOperation); + when(vaultTransitOperation.sign("issuer-key", Plaintext.of("dGVzdA=="))) + .thenReturn(Signature.of("vault:v1:dGVzdA==")); + + byte[] result = vaultSigningService.signHash(hash, null); + assertNotNull(result); + } + + @Test + void signHash_noVaultPrefix() { + byte[] hash = Base64.getDecoder().decode("dGVzdA=="); + + VaultTransitOperations vaultTransitOperation = mock(VaultTransitOperations.class); + when(vaultTemplate.opsForTransit()).thenReturn(vaultTransitOperation); + when(vaultTransitOperation.sign("issuer-key", Plaintext.of("dGVzdA=="))) + .thenReturn(Signature.of("dGVzdA==")); + + byte[] result = vaultSigningService.signHash(hash, null); + assertNotNull(result); + } + +}