Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
thomasrichner-oviva committed Feb 5, 2024
1 parent 29e8a3b commit 2bd3d18
Show file tree
Hide file tree
Showing 14 changed files with 423 additions and 119 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JOSEObjectType;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSObject;
import com.nimbusds.jose.Payload;
import com.nimbusds.jose.crypto.ECDSASigner;
import com.nimbusds.jose.jwk.ECKey;
import com.nimbusds.jose.jwk.JWKSet;
import com.oviva.gesundheitsid.util.JsonCodec;
import java.time.Instant;
import java.util.List;

Expand Down Expand Up @@ -38,6 +47,25 @@ public static Builder create() {
return new Builder();
}

public JWSObject sign(ECKey key) {
try {
var signer = new ECDSASigner(key);

var h =
new JWSHeader.Builder(JWSAlgorithm.ES256)
.type(new JOSEObjectType(EntityStatementJWS.ENTITY_STATEMENT_TYP))
.keyID(key.getKeyID())
.build();

var jwsObject = new JWSObject(h, new Payload(JsonCodec.writeValueAsString(this)));
jwsObject.sign(signer);

return jwsObject;
} catch (JOSEException e) {
throw new IllegalArgumentException("failed to sign entity statement", e);
}
}

@JsonIgnoreProperties(ignoreUnknown = true)
public record OpenidProvider(
@JsonProperty("pushed_authorization_request_endpoint")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

public record EntityStatementJWS(JWSObject jws, EntityStatement body) implements TemporalValid {

private static final String ENTITY_STATEMENT_TYP = "entity-statement+jwt";
public static final String ENTITY_STATEMENT_TYP = "entity-statement+jwt";

public static EntityStatementJWS parse(String wire) {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.oviva.gesundheitsid.util;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
Expand All @@ -13,6 +14,7 @@ public class JsonCodec {
var om = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

om.registerModule(new JoseModule());
om.setSerializationInclusion(Include.NON_NULL);

JsonCodec.om = om;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.oviva.gesundheitsid.fedclient.api;

import static org.junit.jupiter.api.Assertions.*;

import com.oviva.gesundheitsid.test.ECKeyGenerator;
import org.junit.jupiter.api.Test;

class EntityStatementTest {

@Test
void sign_roundTrip() {

var key = ECKeyGenerator.example();

var sub = "hello world!";

// when
var jws = EntityStatement.create().sub(sub).build().sign(key);

// then
var got = EntityStatementJWS.parse(jws.serialize());
assertEquals(sub, got.body().sub());
}
}
6 changes: 6 additions & 0 deletions oidc-server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,12 @@
</dependency>
<!-- END wiremock -->

<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.oviva.gesundheitsid.relyingparty.cfg.Config;
import com.oviva.gesundheitsid.relyingparty.cfg.ConfigProvider;
import com.oviva.gesundheitsid.relyingparty.cfg.EnvConfigProvider;
import com.oviva.gesundheitsid.relyingparty.fed.FederationConfig;
import com.oviva.gesundheitsid.relyingparty.poc.GematikHeaderDecoratorHttpClient;
import com.oviva.gesundheitsid.relyingparty.svc.InMemoryCodeRepo;
import com.oviva.gesundheitsid.relyingparty.svc.InMemorySessionRepo;
Expand Down Expand Up @@ -81,12 +82,35 @@ public void run(ConfigProvider configProvider) throws ExecutionException, Interr
// configuration
// see: https://wiki.gematik.de/pages/viewpage.action?pageId=544316583
var fedmaster = URI.create("https://app-test.federationmaster.de");

// TODO replace with `baseUri`
var self = URI.create("https://idp-test.oviva.io/auth/realms/master/ehealthid");
var authFlow = buildAuthFlow(self, fedmaster);

var authFlow = buildAuthFlow(baseUri, fedmaster);

// TODO make path configurable
var relyingPartyEncryptionJwks = JwksUtils.load(Path.of("./relying-party-enc_jwks.json"));
var relyingPartySigningJwks = JwksUtils.load(Path.of("./relying-party-sig_jwks.json"));

var federationConfig =
FederationConfig.create()
.sub(baseUri)
.iss(baseUri)
.appName("Oviva Direkt")
.federationMaster(fedmaster)
.entitySigningKey(relyingPartySigningJwks.getKeys().get(0).toECKey())
.entitySigningKeys(relyingPartySigningJwks.toPublicJWKSet())
.relyingPartyEncKeys(relyingPartyEncryptionJwks.toPublicJWKSet())

// TODO: bump up to hours, once we're confident it's correct ;)
// the spec says ~1 day
.ttl(Duration.ofMinutes(5))
.redirectUris(List.of(baseUri.resolve("/auth/callback").toString()))
.build();

var instance =
SeBootstrap.start(
new App(config, sessionRepo, keyStore, tokenIssuer, authFlow),
new App(config, federationConfig, sessionRepo, keyStore, tokenIssuer, authFlow),
Configuration.builder().host("0.0.0.0").port(config.port()).build())
.toCompletableFuture()
.get();
Expand All @@ -100,12 +124,6 @@ public void run(ConfigProvider configProvider) throws ExecutionException, Interr

private AuthenticationFlow buildAuthFlow(URI selfIssuer, URI fedmaster) {

// this URI must be listed in your entity statement, configure as needed
var redirectUri = URI.create("http://localhost:8080");

// those _MUST_ be at most the ones you requested when handing in the entity statement
var scopes = List.of("openid", "urn:telematik:email", "urn:telematik:versicherter");

// path to the JWKS containing the private keys to decrypt ID tokens, the public part
// is in your entity configuration
var relyingPartyEncryptionJwks = JwksUtils.load(Path.of("./relying-party-enc_jwks.json"));
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package com.oviva.gesundheitsid.relyingparty.fed;

import com.nimbusds.jose.jwk.ECKey;
import com.nimbusds.jose.jwk.JWKSet;
import java.net.URI;
import java.time.Duration;
import java.util.List;

public record FederationConfig(
URI iss,
URI sub,
URI federationMaster,
JWKSet entitySigningKeys,

// the actual private key used for singing, _MUST_ be part of `entitySigningKeys`
ECKey entitySigningKey,
JWKSet relyingPartyEncKeys,
Duration ttl,
List<String> redirectUris,
String appName) {

public static Builder create() {
return new Builder();
}

public static final class Builder {

private URI iss;
private URI sub;
private URI federationMaster;

private ECKey entitySigningKey;
private JWKSet entitySigningKeys;

private JWKSet relyingPartyEncKeys;
private Duration ttl;
private List<String> redirectUris;
private String appName;

public Builder() {}

public Builder iss(URI iss) {
this.iss = iss;
return this;
}

public Builder sub(URI sub) {
this.sub = sub;
return this;
}

public Builder federationMaster(URI federationMaster) {
this.federationMaster = federationMaster;
return this;
}

public Builder entitySigningKey(ECKey signingKey) {
this.entitySigningKey = signingKey;
return this;
}

public Builder entitySigningKeys(JWKSet jwks) {
this.entitySigningKeys = jwks;
return this;
}

public Builder relyingPartyEncKeys(JWKSet jwks) {
this.relyingPartyEncKeys = jwks;
return this;
}

public Builder ttl(Duration ttl) {
this.ttl = ttl;
return this;
}

public Builder redirectUris(List<String> redirectUris) {
this.redirectUris = redirectUris;
return this;
}

public Builder appName(String appName) {
this.appName = appName;
return this;
}

public FederationConfig build() {
return new FederationConfig(
iss,
sub,
federationMaster,
entitySigningKeys,
entitySigningKey,
relyingPartyEncKeys,
ttl,
redirectUris,
appName);
}
}
}
Loading

0 comments on commit 2bd3d18

Please sign in to comment.