From e4073731ff9333192c8d13ab9831bcaf3342bbbb Mon Sep 17 00:00:00 2001 From: Eduard Thamm Date: Fri, 7 Jun 2024 06:36:06 +0200 Subject: [PATCH 1/3] Draft upgrade commands --- .gitignore | 1 + .../ehealthid/cli/KeyGeneratorCommand.java | 2 +- .../ehealthid/cli/MTlsRefreshCommand.java | 130 ++++++++++++++++++ .../com/oviva/ehealthid/cli/RootCommand.java | 6 +- 4 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 ehealthid-cli/src/main/java/com/oviva/ehealthid/cli/MTlsRefreshCommand.java diff --git a/.gitignore b/.gitignore index abcc636..43510ae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .idea/ .vscode/ target/ +local/ gesundheitsid/env.properties *.iml .flattened-pom.xml diff --git a/ehealthid-cli/src/main/java/com/oviva/ehealthid/cli/KeyGeneratorCommand.java b/ehealthid-cli/src/main/java/com/oviva/ehealthid/cli/KeyGeneratorCommand.java index 1f9a081..b07de02 100755 --- a/ehealthid-cli/src/main/java/com/oviva/ehealthid/cli/KeyGeneratorCommand.java +++ b/ehealthid-cli/src/main/java/com/oviva/ehealthid/cli/KeyGeneratorCommand.java @@ -35,7 +35,7 @@ public class KeyGeneratorCommand implements Callable { @CommandLine.Option( names = {"-i", "--iss", "--issuer-uri"}, description = "the issuer uri of the 'Fachdienst' identiy provider", - required = true) + required = false) private URI issuerUri; private static final Logger logger = LoggerFactory.getLogger(KeyGeneratorCommand.class); diff --git a/ehealthid-cli/src/main/java/com/oviva/ehealthid/cli/MTlsRefreshCommand.java b/ehealthid-cli/src/main/java/com/oviva/ehealthid/cli/MTlsRefreshCommand.java new file mode 100644 index 0000000..5df6523 --- /dev/null +++ b/ehealthid-cli/src/main/java/com/oviva/ehealthid/cli/MTlsRefreshCommand.java @@ -0,0 +1,130 @@ +package com.oviva.ehealthid.cli; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.jwk.*; +import com.nimbusds.jose.jwk.gen.ECKeyGenerator; +import com.nimbusds.jose.util.Base64; +import com.nimbusds.oauth2.sdk.id.Issuer; +import com.nimbusds.oauth2.sdk.util.X509CertificateUtils; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.cert.CertificateEncodingException; +import java.time.Duration; +import java.time.Instant; +import java.util.Date; +import java.util.List; +import java.util.concurrent.Callable; +import org.bouncycastle.operator.OperatorCreationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import picocli.CommandLine; + +@CommandLine.Command( + name = "tls-refresh", + mixinStandardHelpOptions = true, + description = "Generate or Refresh the mTLS certificates in the Signing Key.") +public class MTlsRefreshCommand implements Callable { + + @CommandLine.Option( + names = {"-i", "--iss", "--issuer-uri"}, + description = "the issuer uri of the 'Fachdienst' identiy provider", + required = false) + private URI issuerUri; + + @CommandLine.Option( + names = {"-e", "--existing"}, + description = "the existing signing key", + required = true) + private String existingKey; + + @CommandLine.Option( + names = {"-r", "--refresh"}, + description = "refresh the existing mTLS certificate", + defaultValue = "false") + private boolean refresh; + + private static final Logger logger = LoggerFactory.getLogger(MTlsRefreshCommand.class); + + public Integer call() throws Exception { + + var sigName = "sig"; + + logger.atInfo().log("using existing signing key '%s'".formatted(existingKey)); + var inputStream = Files.newInputStream(Path.of(existingKey)); + var jwks = JWKSet.load(inputStream); + var key = jwks.getKeys().get(0); + if (refresh) { + var newKey = generateCertificate(key); + System.out.println(newKey); + logger.atInfo().log("refreshing mTLS certificate"); + } else { + var newKey = generateCertificateFromExistingKey(key); + logger.atInfo().log("generating mTLS certificate"); + System.out.println(newKey); + } + return 0; + } + + private JWK generateCertificateFromExistingKey(JWK key) + throws JOSEException, IOException, OperatorCreationException, CertificateEncodingException { + // The idea here is to give users that currently have keys, that do not have + // the relevant x5c field, the ability to generate a certificate for mTLS + // without going through the fuzz of generating a new key and talking to + // Gematik/BfArM about it. Which just generates work for everyone. + + var now = Instant.now(); + var nbf = now.minus(Duration.ofDays(1)); + var exp = now.plus(Duration.ofDays(180)); + + var cert = + X509CertificateUtils.generateSelfSigned( + new Issuer(issuerUri), + Date.from(nbf), + Date.from(exp), + key.toECKey().toPublicKey(), + key.toECKey().toPrivateKey()); + + return new ECKey.Builder(key.toECKey()) + .x509CertChain(List.of(Base64.encode(cert.getEncoded()))) + .build(); + } + + private JWK generateCertificate(JWK key) + throws JOSEException, IOException, OperatorCreationException, CertificateEncodingException { + // TODO: This is one huge playground method + // Non of this is 'real' in the sense that it should work or is + // what the standard would require. + // My current understanding is that is that to keep in line with the + // standard and the intended process, we should generate a new certificate + // sign it with the existing key and then persist the new key and the new + // certificate for use with mTLS. + // Again, not what this currently does. + var certKey = + new ECKeyGenerator(Curve.P_256) + .keyUse(KeyUse.SIGNATURE) + .keyIDFromThumbprint(true) + .generate(); + + var now = Instant.now(); + var nbf = now.minus(Duration.ofDays(1)); + var exp = now.plus(Duration.ofDays(180)); + + var cert = + X509CertificateUtils.generateSelfSigned( + new Issuer(issuerUri), + Date.from(nbf), + Date.from(exp), + certKey.toPublicKey(), + certKey.toPrivateKey()); + + var chain = key.getX509CertChain(); + var newChain = new java.util.ArrayList<>(chain); + newChain.add(Base64.encode(cert.getEncoded())); + + // TODO also persist the new signing key + + return new ECKey.Builder(key.toECKey()).x509CertChain(newChain).build(); + } +} diff --git a/ehealthid-cli/src/main/java/com/oviva/ehealthid/cli/RootCommand.java b/ehealthid-cli/src/main/java/com/oviva/ehealthid/cli/RootCommand.java index e4ad0d4..e77a71f 100644 --- a/ehealthid-cli/src/main/java/com/oviva/ehealthid/cli/RootCommand.java +++ b/ehealthid-cli/src/main/java/com/oviva/ehealthid/cli/RootCommand.java @@ -9,7 +9,11 @@ name = "ehealthid-cli", mixinStandardHelpOptions = true, version = "0.1", - subcommands = {FedRegistrationCommand.class, KeyGeneratorCommand.class}) + subcommands = { + FedRegistrationCommand.class, + KeyGeneratorCommand.class, + MTlsRefreshCommand.class + }) public class RootCommand { private static final Logger logger = LoggerFactory.getLogger(RootCommand.class); From 2d37ea269a7cd253394c03e66caebd7afad9db89 Mon Sep 17 00:00:00 2001 From: eduardOrthopy <132349360+eduardOrthopy@users.noreply.github.com> Date: Wed, 12 Jun 2024 14:02:35 +0200 Subject: [PATCH 2/3] Update ehealthid-cli/src/main/java/com/oviva/ehealthid/cli/MTlsRefreshCommand.java Co-authored-by: Thomas Richner --- .../main/java/com/oviva/ehealthid/cli/MTlsRefreshCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ehealthid-cli/src/main/java/com/oviva/ehealthid/cli/MTlsRefreshCommand.java b/ehealthid-cli/src/main/java/com/oviva/ehealthid/cli/MTlsRefreshCommand.java index 5df6523..f352804 100644 --- a/ehealthid-cli/src/main/java/com/oviva/ehealthid/cli/MTlsRefreshCommand.java +++ b/ehealthid-cli/src/main/java/com/oviva/ehealthid/cli/MTlsRefreshCommand.java @@ -30,7 +30,7 @@ public class MTlsRefreshCommand implements Callable { @CommandLine.Option( names = {"-i", "--iss", "--issuer-uri"}, description = "the issuer uri of the 'Fachdienst' identiy provider", - required = false) + required = true) private URI issuerUri; @CommandLine.Option( From abec8e4763cbf9a6afaed01243e9b423189987c0 Mon Sep 17 00:00:00 2001 From: eduardOrthopy <132349360+eduardOrthopy@users.noreply.github.com> Date: Wed, 12 Jun 2024 14:40:41 +0200 Subject: [PATCH 3/3] Update KeyGeneratorCommand.java --- .../main/java/com/oviva/ehealthid/cli/KeyGeneratorCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ehealthid-cli/src/main/java/com/oviva/ehealthid/cli/KeyGeneratorCommand.java b/ehealthid-cli/src/main/java/com/oviva/ehealthid/cli/KeyGeneratorCommand.java index b07de02..1f9a081 100755 --- a/ehealthid-cli/src/main/java/com/oviva/ehealthid/cli/KeyGeneratorCommand.java +++ b/ehealthid-cli/src/main/java/com/oviva/ehealthid/cli/KeyGeneratorCommand.java @@ -35,7 +35,7 @@ public class KeyGeneratorCommand implements Callable { @CommandLine.Option( names = {"-i", "--iss", "--issuer-uri"}, description = "the issuer uri of the 'Fachdienst' identiy provider", - required = false) + required = true) private URI issuerUri; private static final Logger logger = LoggerFactory.getLogger(KeyGeneratorCommand.class);