Skip to content

Commit

Permalink
v27.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Gematik-Entwicklung authored and RStaeber committed Mar 5, 2024
1 parent 5f4ab6e commit 6d84fbd
Show file tree
Hide file tree
Showing 82 changed files with 1,760 additions and 1,672 deletions.
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ or use docker compose:

```console
$ mvn clean install -pl idp-server -am -Dskip.unittests -Dskip.inttests
$ export appVersion=26.0.1
$ export appVersion=27.0.0
$ export serverLoglevel=info (default)
$ docker-compose --project-name myidp -f docker-compose-ref.yml up -d
```
Expand All @@ -68,6 +68,33 @@ $ curl http://localhost:8571/auth/realms/idp/.well-known/openid-configuration
You can modify the scopes that are supported by the IDP Server. All you have to is add, remove or
modify entries in the scopesConfiguration section of the idp-server's application.yml.

### Configuration of Server URL

The URL of the idp-server is required for many fields inside the discovery document of the server. For example, the authorization endpoint:

```
{
"authorization_endpoint": "https://server42/sign_response",
...
```

The idp-server determines the URL in the following priority order if it exists:

1. jvm arg: --idp.serverUrl=https://myServerUrlAsJvmArgument.de
2. environment variable: IDP_SERVER_URL=myServerUrlFromEnv:8080
3. spring boot configuration (application.yml):

```
idp:
serverUrl: "https://urlPreConfiguredUrl"
```

During development, it is recommended to set "severUrl" not in application.yml as some unit tests will fail then.
Background: serverUrl will be set several times in the discovery document and used from there in unit tests.
In unit tests, random (free) ports are used, and with that they are part of the serverUrl.

4. precompiled value: IdpConstants.DEFAULT_SERVER_URL

### Unittests

disable: `-Dskip.unittests`
Expand Down
7 changes: 7 additions & 0 deletions ReleaseNotes.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# Release 27.0.0

- idp-client login for ePA (wip)
- read server URL at server startup, remove code for extracting server URL from request
- update test certificates
- update dependencies

# Release 26.0.1

- add claim for family name
Expand Down
780 changes: 365 additions & 415 deletions doc/tokenFlowEgk.html

Large diffs are not rendered by default.

716 changes: 336 additions & 380 deletions doc/tokenFlowPs.html

Large diffs are not rendered by default.

1,314 changes: 625 additions & 689 deletions doc/tokenFlowSso.html

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions idp-client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
<parent>
<groupId>de.gematik.idp</groupId>
<artifactId>idp-global</artifactId>
<version>26.0.1</version>
<version>27.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>de.gematik.idp</groupId>
<artifactId>idp-client</artifactId>

<version>26.0.1</version>
<version>27.0.0</version>
<packaging>jar</packaging>

<dependencies>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2024 gematik GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package de.gematik.idp.client;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class AuthorizationCodeResult {
private String authorizationCode;
private String state;
private String redirectUri;
}
77 changes: 68 additions & 9 deletions idp-client/src/main/java/de/gematik/idp/client/IdpClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,16 @@ public class IdpClient implements IIdpClient {
private String fixedIdpHost;
private DiscoveryDocumentResponse discoveryDocumentResponse;

private static UnaryOperator<byte[]> getContentSigner(final PkiIdentity pkiIdentity) {
return tbsData -> {
if (pkiIdentity.getPrivateKey() instanceof RSAPrivateKey) {
return RsaSignerUtility.createRsaSignature(tbsData, pkiIdentity.getPrivateKey());
} else {
return EcSignerUtility.createEcSignature(tbsData, pkiIdentity.getPrivateKey());
}
};
}

@SneakyThrows
private String signServerChallenge(
final String challengeToSign,
Expand Down Expand Up @@ -185,15 +195,7 @@ private byte[] getSignatureBytes(
@Override
public IdpTokenResult login(final PkiIdentity idpIdentity) {
assertThatIdpIdentityIsValid(idpIdentity);
return login(
idpIdentity.getCertificate(),
tbsData -> {
if (idpIdentity.getPrivateKey() instanceof RSAPrivateKey) {
return RsaSignerUtility.createRsaSignature(tbsData, idpIdentity.getPrivateKey());
} else {
return EcSignerUtility.createEcSignature(tbsData, idpIdentity.getPrivateKey());
}
});
return login(idpIdentity.getCertificate(), getContentSigner(idpIdentity));
}

public IdpTokenResult login(
Expand Down Expand Up @@ -270,6 +272,63 @@ public IdpTokenResult login(
afterTokenCallback);
}

public AuthorizationCodeResult login(
final PkiIdentity smcbIdentity,
final String codeChallenge,
final String state,
final String nonce) {

final X509Certificate certificate = smcbIdentity.getCertificate();

LOGGER.debug(
"Performing Authorization with remote-URL '{}'",
discoveryDocumentResponse.getAuthorizationEndpoint());
final AuthorizationResponse authorizationResponse =
authorizationResponseMapper.apply(
authenticatorClient.doAuthorizationRequest(
AuthorizationRequest.builder()
.clientId(clientId)
.link(discoveryDocumentResponse.getAuthorizationEndpoint())
.codeChallenge(codeChallenge)
.codeChallengeMethod(codeChallengeMethod)
.redirectUri(redirectUrl)
.state(state)
.scopes(scopes)
.nonce(nonce)
.build(),
beforeAuthorizationMapper,
afterAuthorizationCallback));

// Authentication
LOGGER.debug(
"Performing Authentication with remote-URL '{}'",
discoveryDocumentResponse.getAuthorizationEndpoint());
final AuthenticationResponse authenticationResponse =
authenticationResponseMapper.apply(
authenticatorClient.performAuthentication(
AuthenticationRequest.builder()
.authenticationEndpointUrl(discoveryDocumentResponse.getAuthorizationEndpoint())
.signedChallenge(
new IdpJwe(
signServerChallenge(
authorizationResponse
.getAuthenticationChallenge()
.getChallenge()
.getRawString(),
certificate,
getContentSigner(smcbIdentity))))
.build(),
beforeAuthenticationMapper,
afterAuthenticationCallback));

final String location = authenticationResponse.getLocation();
return AuthorizationCodeResult.builder()
.authorizationCode(UriUtils.extractParameterValue(location, "code"))
.redirectUri(UriUtils.extractBaseUri(authenticationResponse.getLocation()))
.state(UriUtils.extractParameterValue(location, "state"))
.build();
}

public IdpTokenResult loginWithSsoToken(final IdpJwe ssoToken) {
assertThatClientIsInitialized();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package de.gematik.idp.client;

import static de.gematik.idp.IdpConstants.DEFAULT_SERVER_URL;
import static de.gematik.idp.IdpConstants.EREZEPT;
import static de.gematik.idp.IdpConstants.OPENID;
import static de.gematik.idp.field.ClaimName.FAMILY_NAME;
Expand All @@ -24,7 +25,6 @@
import static de.gematik.idp.field.ClaimName.ORGANIZATION_NAME;
import static de.gematik.idp.field.ClaimName.PROFESSION_OID;

import de.gematik.idp.IdpConstants;
import de.gematik.idp.authentication.AuthenticationChallenge;
import de.gematik.idp.authentication.AuthenticationChallengeBuilder;
import de.gematik.idp.authentication.AuthenticationResponse;
Expand Down Expand Up @@ -79,7 +79,7 @@ public class MockIdpClient implements IIdpClient {
private final String clientId;
private final boolean produceTokensWithInvalidSignature;
private final boolean produceOnlyExpiredTokens;
@Builder.Default private final String uriIdpServer = IdpConstants.DEFAULT_SERVER_URL;
@Builder.Default private final String uriIdpServer = DEFAULT_SERVER_URL;
private final HashMap<String, String> scopeToAudienceUrls = new HashMap<>();
private AccessTokenBuilder accessTokenBuilder;
private AuthenticationResponseBuilder authenticationResponseBuilder;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package de.gematik.idp.client;

import static de.gematik.idp.IdpConstants.DEFAULT_SERVER_URL;
import static de.gematik.idp.field.ClaimName.ALGORITHM;
import static de.gematik.idp.field.ClaimName.ISSUER;
import static org.assertj.core.api.Assertions.assertThat;
Expand All @@ -37,7 +38,7 @@
@ExtendWith(PkiKeyResolver.class)
class MockIdpClientTest {

private static final String URI_IDP_SERVER = "https://idp.zentral.idp.splitdns.ti-dienste.de";
private static final String URI_IDP_SERVER = DEFAULT_SERVER_URL;
private static final String CLIENT_ID_E_REZEPT_APP = "eRezeptApp";
private MockIdpClient mockIdpClient;
private PkiIdentity serverIdentity;
Expand Down Expand Up @@ -127,7 +128,7 @@ void expiredTokens_verifyShouldFail() {

@Test
void verifyTokenWithEcClientCertificate(
@PkiKeyResolver.Filename("833621999741600_c.hci.aut-apo-ecc.p12")
@PkiKeyResolver.Filename("833621999741600-2_c.hci.aut-apo-ecc.p12")
final PkiIdentity eccClientIdentity) {
Assertions.assertDoesNotThrow(
() ->
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
8 changes: 4 additions & 4 deletions idp-commons/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
<parent>
<groupId>de.gematik.idp</groupId>
<artifactId>idp-global</artifactId>
<version>26.0.1</version>
<version>27.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>idp-commons</artifactId>

<version>26.0.1</version>
<version>27.0.0</version>

<dependencies>

Expand Down Expand Up @@ -49,7 +49,7 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.13.0</version>
<version>${version.commons-lang3}</version>
</dependency>

<dependency>
Expand All @@ -66,7 +66,7 @@
<dependency>
<groupId>org.bitbucket.b_c</groupId>
<artifactId>jose4j</artifactId>
<version>0.9.4</version>
<version>0.9.5</version>
</dependency>
</dependencies>

Expand Down
2 changes: 1 addition & 1 deletion idp-commons/src/main/java/de/gematik/idp/IdpConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public final class IdpConstants {
public static final String TOKEN_ENDPOINT = "/token";
public static final String PAIRING_ENDPOINT = "/pairings";
public static final String THIRD_PARTY_ENDPOINT = "/extauth";
public static final String DEFAULT_SERVER_URL = "https://idp.zentral.idp.splitdns.ti-dienste.de";
public static final String DEFAULT_SERVER_URL = "https://idp.dev.gematik.solutions";
public static final String EIDAS_LOA_HIGH = "gematik-ehealth-loa-high";
public static final int JTI_LENGTH = 16;
public static final String AMR_FAST_TRACK = "mfa";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,15 @@ public static String extractParameterValue(final String uri, final String param)
() ->
new RuntimeException("Could not find '" + param + "' parameter in '" + uri + "'"));
}

public static String extractBaseUri(final String uriStr) {
final URI uri;
try {
uri = new URI(uriStr);
} catch (final URISyntaxException e) {
throw new IdpRuntimeException(e);
}

return uri.getScheme() + "://" + uri.getHost() + uri.getPath();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ class AuthenticationChallengeVerifierTest {
public void init(
@PkiKeyResolver.Filename("1_C.SGD-HSM.AUT_oid_sgd1_hsm_ecc.p12")
final PkiIdentity serverIdentity,
@PkiKeyResolver.Filename("109500969_X114428530_c.ch.aut-ecc.p12")
@PkiKeyResolver.Filename("109500969_X114428530-2_c.ch.aut-ecc.p12")
final PkiIdentity clientIdentity,
@PkiKeyResolver.Filename("833621999741600_c.hci.aut-apo-rsa.p12")
@PkiKeyResolver.Filename("833621999741600-2_c.hci.aut-apo-rsa.p12")
final PkiIdentity rsaClientIdentity) {
this.clientIdentity = clientIdentity;
this.serverIdentity = serverIdentity;
Expand Down Expand Up @@ -144,7 +144,7 @@ void extractClaims_shouldBePresent() {

@Test
void checkSignatureNjwt_certMismatch(
@PkiKeyResolver.Filename("833621999741600_c.hci.aut-apo-ecc.p12")
@PkiKeyResolver.Filename("833621999741600-2_c.hci.aut-apo-ecc.p12")
final PkiIdentity otherServerIdentity) {
authenticationChallengeBuilder =
AuthenticationChallengeBuilder.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class AuthenticationTokenBuilderTest {
@BeforeEach
public void init(
final PkiIdentity ecc,
@Filename("109500969_X114428530_c.ch.aut-ecc") final PkiIdentity clientIdentity) {
@Filename("109500969_X114428530-2_c.ch.aut-ecc") final PkiIdentity clientIdentity) {

encryptionKey = new SecretKeySpec(DigestUtils.sha256("fdsa"), "AES");
authenticationTokenBuilder =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class IdpJwtProcessorTest {
new HashMap<>(
Map.ofEntries(
entry(ISSUED_AT.getJoseName(), ZonedDateTime.now().toEpochSecond()),
entry(ISSUER.getJoseName(), "https://idp.zentral.idp.splitdns.ti-dienste.de"),
entry(ISSUER.getJoseName(), "https://myIdp.de"),
entry(RESPONSE_TYPE.getJoseName(), "code"),
entry(SCOPE.getJoseName(), "openid e-rezept"),
entry(CLIENT_ID.getJoseName(), "ZXJlemVwdC1hcHA"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ private static FederationPubKey getFederationPubKeyWithX5c() throws IOException
Optional.ofNullable(
CryptoLoader.getCertificateFromP12(
FileUtils.readFileToByteArray(
new File("src/test/resources/109500969_X114428530_c.ch.aut-ecc.p12")),
new File("src/test/resources/109500969_X114428530-2_c.ch.aut-ecc.p12")),
"00")));
federationPubKey.setPublicKey(Optional.empty());
federationPubKey.setKeyId("anyKeyId");
Expand Down
Loading

0 comments on commit 6d84fbd

Please sign in to comment.