Skip to content
Snippets Groups Projects
Commit f2c1d240 authored by Marco Holz's avatar Marco Holz
Browse files

Merge branch 'Pruefung-der-DVDV-Signatur' into 'main'

Prüfung DVDV-Signature

See merge request !123
parents 1a2ff8c6 6cc1755f
No related branches found
No related tags found
1 merge request!123Prüfung DVDV-Signature
...@@ -2,6 +2,10 @@ ...@@ -2,6 +2,10 @@
Das Format dieser Datei basiert auf [Keep a Changelog](https://keepachangelog.com/de). Das Format dieser Datei basiert auf [Keep a Changelog](https://keepachangelog.com/de).
## 2022-02-07
### Dokumentation
- Hinweise zur [Signaturprüfung der vom DVDV gelieferten Zustellpunkt-Parameter](./responsibilities/get-destination.mdx#checkDestinationParametersSignature) ergänzt
## 2022-02-03 ## 2022-02-03
### Zustelldienst 1.2.0 ### Zustelldienst 1.2.0
- Story: Der Zustelldienst schreibt nun zusätzliche Infos in die Events `submit-submission` und `notify-submission`. - Story: Der Zustelldienst schreibt nun zusätzliche Infos in die Events `submit-submission` und `notify-submission`.
......
...@@ -161,7 +161,7 @@ Der dekodierte Inhalt (Payload) der Adressierungsinformationen sieht beispielhaf ...@@ -161,7 +161,7 @@ Der dekodierte Inhalt (Payload) der Adressierungsinformationen sieht beispielhaf
} }
``` ```
### Signaturprüfung der vom Self-Service Portal (SSP) signierten Adressierungsinformationen (Parameter `destinationSignature`) ### Signaturprüfung der vom Self-Service Portal (SSP) signierten Adressierungsinformationen (Parameter `destinationSignature`) {#checkDestinationSignature}
Bei den signierten Adressierungsinformationen handelt es sich um signierte JSON Web Tokens (JWTs). Bei den signierten Adressierungsinformationen handelt es sich um signierte JSON Web Tokens (JWTs).
Um die Signatur der vom Self-Service-Portal signierten JWTs zu überprüfen, ist es notwendig, auf die verwendeten Schlüssel (im Format JSON Web Key, kurz JWK) zugreifen zu können. Um die Signatur der vom Self-Service-Portal signierten JWTs zu überprüfen, ist es notwendig, auf die verwendeten Schlüssel (im Format JSON Web Key, kurz JWK) zugreifen zu können.
Das Self-Service-Portal stellt ein JSON Web Key Set (JWKS) öffentlich zugänglich über den Endpunkt `/.well-known/jwks.json` bereit. Das Self-Service-Portal stellt ein JSON Web Key Set (JWKS) öffentlich zugänglich über den Endpunkt `/.well-known/jwks.json` bereit.
...@@ -273,6 +273,200 @@ Im folgenden Beispiel wird die Bibliothek [nimbus-jose-jwt](https://connect2id.c ...@@ -273,6 +273,200 @@ Im folgenden Beispiel wird die Bibliothek [nimbus-jose-jwt](https://connect2id.c
</Tabs> </Tabs>
### Signaturprüfung der vom DVDV gelieferten Zustellpunkt-Parameter (`destinationParameters` bzw. HTTP-Header `jws-signature`) {#checkDestinationParametersSignature}
Die vom DVDV gelieferten Daten enthalten den Zustellpunkt sowie dessen Signatur (`destinationParametersSignature`).
Die `destinationParametersSignature` wird in der Routing API im gleichnamigen Feld und in der API des DVDV über den HTTP-Header `jws-signature` zurückgegeben.
Bei der Signatur handelt es sich um eine JSON Web Signature (JWS) in der Compact Serialization gemäß [RFC 7515, Abschnitt 3.1](https://datatracker.ietf.org/doc/html/rfc7515#section-3.1).
Die Signatur liegt als Detached Signature gemäß [RFC 7515, Anlage F](https://datatracker.ietf.org/doc/html/rfc7515#appendix-F) vor.
Bei einer Detached Signature sind die eigentlichen Inhaltsdaten (Payload) nicht Teil der übertragenen Signatur, sondern werden separat übermittelt.
Für eine Signaturprüfung müssen die separat übermittelten Inhaltsdaten (Payload) daher zunächst wieder zur Signaur hinzugefügt werden (siehe unten).
Beispiel eines Zustellpunktes (`destinationParameters`):
```json
{
"encryptionKid": "NFNb7k84r61G9ayAAJItJCNGl7wKWif9HyBAgicJq_8",
"metadataVersions": ['1.0.0'],
"publicKeys":
{
"keys":
[
{
"alg": "RSA-OAEP-256",
"e": "AQAB",
"key_ops": ["wrapKey"],
"kid": "NFNb7k84r61G9ayAAJItJCNGl7wKWif9HyBAgicJq_8",
"kty": "RSA",
"n": "1f1070XZ4NpHN2WqdH5c8dBUBPH99TJEvVXSP_jjZdOEzRJztUwSpIabtAvgDnNGmPTLs-jLlVR3NQCyKwOwpHVi3FmudKmIPplBFpsEpZ9JYBGpg8_ZbDN9fwJhob0KjAlsSY9mBOTfqLCqqVIJrk4fxBjwNaroCLkSbS2RrfMtUEW5T5Vo1uw2lnYTKq1uyhr1PG02mvDCBb0LMAqcMXRR6bdme8GN55S3UNWhsaonpq04aa8_baVdjoJYTk03VLORMojnnrJjxyPPiHRs2Re9JQoaVPy6TUrbFV63zvt30XM8ZJnla09yhMmuBJXpdtyWXKnKyqj8m9D5Vg68xksQVeJozpCAoBlsJeAheE31XPQwCBvamy46K669ZCkfkdhQgoIJMt1AVSef0qcLDg__nQ-rfIuYxHrtn7jgI0NeCGFbscxmzl08_LSj3nlj2-ag2uVq4bbdH3tziNxy_rr84N-6AA5iQe5v1L_zYXYWxGzaAOUWzJt0QRiEC9pF6Zqfrn4mPHn5lm2jtdM9AlmgkZtmK92rByfcMzo5-yEK37K96NtqpDCsoABUkvC1TLiqaCkGkQd1DmGnfNyGJV_eNMwmZyotom8WLS-icbQD913F9YlSTRsQYhFzw78pDJHHo4AtldMiQcpUY4qoVVpfpPZlMWTq7idnq6iO4MM",
"x5c": [
"...(base64 encoded cert)...",
"...(base64 encoded intermediate cert)...",
"...(base64 encoded root cert)..."
]
},
{
"alg": "PS512",
"e": "AQAB",
"key_ops": ["verify"],
"kid": "QEIaM4Lz9KLSaPyDzis-6aqE1x8q82iGdTv8Gb64ves",
"kty": "RSA",
"n": "lvi7t8Xy9Ef36gooR-AcsL4BBXjBKO0wcpM_hwFyQAELRrC3l5TityJPGjhFZJo1HZSFWGFCvqG56KkPgT5UdqbHFN2watpSguNTalERFmr6cXeB65NdjCrglCsGBmMHVViyCZ2gaBlfppdpeo0BI_9gUqv0OhzzoJGunI7G2YwPQrCdyEWWRYNVLqd_4B1wPPyc5MONavF1pQ6e1Nlk4_c9nL4A51-LRXlmBODhQ41aAac1gjpLD-ht1bVIjGzTUKZku5THAOltsEcDhjYXyfEUTA83i3I8PY9KMoenDFbCrtSUwsX-usJbpOt3M85TNAGxJeWIMa8nIEDyp1vjjb6QcPwvQgfFkSzjTQchVO4cT3rQ-DzqaRso9PAqOS0J3xasaKsqdgcr0AYvAdME3Yw_Wg2YLsq4GKjVtCAdhZiHBxx1H6oYFNXjYsnrMs99u_TpipFaDhJ-iWqtY7Bo_aek2yEt3mAoKByFO6QoQZoOgYteqCvJVGKOzETYgR9OA3_CSzl9YdkF2J3BH3DeBSX80_hSD-aDHGuRClzQ8iMBnAPW4HiOZ9IBkll-Ma7pIcgIXWhAnwvUCxtnK7ZPk213uhcwPxdK8wxxldzjsvVisFZw3QeJ7t-XMpm5_0gL5jGjzq0BAyHcxFr8yJxCekBqrBLoEHjV5LP6dUN-i98",
"x5c": ["MIIFCTCCAvECBARRtXowDQYJKoZIhvcNAQENBQAwSTELMAkGA1UEBhMCREUxFTATBgNVBAoMDFRlc3RiZWhvZXJkZTEjMCEGA1UEAwwaRklUIENvbm5lY3QgVGVzdHplcnRpZmlrYXQwHhcNMjEwOTE2MTYwMTU1WhcNMzEwOTE0MTYwMTU1WjBJMQswCQYDVQQGEwJERTEVMBMGA1UECgwMVGVzdGJlaG9lcmRlMSMwIQYDVQQDDBpGSVQgQ29ubmVjdCBUZXN0emVydGlmaWthdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJb4u7fF8vRH9+oKKEfgHLC+AQV4wSjtMHKTP4cBckABC0awt5eU4rciTxo4RWSaNR2UhVhhQr6hueipD4E+VHamxxTdsGraUoLjU2pRERZq+nF3geuTXYwq4JQrBgZjB1VYsgmdoGgZX6aXaXqNASP/YFKr9Doc86CRrpyOxtmMD0KwnchFlkWDVS6nf+AdcDz8nOTDjWrxdaUOntTZZOP3PZy+AOdfi0V5ZgTg4UONWgGnNYI6Sw/obdW1SIxs01CmZLuUxwDpbbBHA4Y2F8nxFEwPN4tyPD2PSjKHpwxWwq7UlMLF/rrCW6TrdzPOUzQBsSXliDGvJyBA8qdb442+kHD8L0IHxZEs400HIVTuHE960Pg86mkbKPTwKjktCd8WrGirKnYHK9AGLwHTBN2MP1oNmC7KuBio1bQgHYWYhwccdR+qGBTV42LJ6zLPfbv06YqRWg4SfolqrWOwaP2npNshLd5gKCgchTukKEGaDoGLXqgryVRijsxE2IEfTgN/wks5fWHZBdidwR9w3gUl/NP4Ug/mgxxrkQpc0PIjAZwD1uB4jmfSAZJZfjGu6SHICF1oQJ8L1AsbZyu2T5Ntd7oXMD8XSvMMcZXc47L1YrBWcN0Hie7flzKZuf9IC+Yxo86tAQMh3MRa/MicQnpAaqwS6BB41eSz+nVDfovfAgMBAAEwDQYJKoZIhvcNAQENBQADggIBAD4iEnx80ouIv6AhENlQM2vSy8h3SeuKxKrzOiliLLyYnXcaLSGjb4i5xheOnSiKeLvd/pm+dQlXzDYHYBNWXNnWXocXQQGRhx8Vvi3hiR1pEPKCvZcGJYoLQ/9Qvoa0JTw+ikThdg7V8Q0qhvYrM3utf5SG4+P3M3xa8rsEqFW9BQchxxmPnHHoBT2bek1UOjqZp9FLUKI1dOX8XUl8ptVlA+YUU3HESDNcY981fp+fWqcEGDIlawXw4oNwZ+tbGdIJecb4FLbxVdKOWh4klLxgu+SBOtb0wXmmVcoRoxwu2/MCYF3j73FyhVw9YJbRAZyj2VraYjZRcanRxfVBfFBexSiTE7rwU3Uh1fZBvsKzq0KoebC/wQ32ANWS7OaEh6ryDxb2+etymnOplyrTX8KTEPePgT+MLdmhFkOTyYxw5naisSXNIZctHEKshtQOutlGoJyXbDu2t08/O1HlP0qjvFIYeN94ohdG29RydkecBu8ixrAd6YUkYEMLgv71xzx40RVVg6IKg4Rekmjx126oDAYAtUFqHK1MD0pkUOTvhD2G0u4FKeYxd/Wh0tcb9hfl6fYRXptYRq3dxFKOyhki0jwVftFtmJHkBzts1M1agR6v036A7eFA/nT6HRwGJ7B3P2SOGBbZSNKk9JUnK7d3o5lhs98tZ8oCRTi1e4Ht"]
}
]
},
"submissionSchemas": [
{
"mimeType": "application/xml",
"schemaUri": "urn:xoev-de:bmk:standard:xbau_2.2#baugenehmigung.antrag.0200"
}
],
"submissionUrl": "https://submission-api.fit-connect.example.org/v1",
}
```
Beispiel einer Detached JWS (`destinationParametersSignature`):
```
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjNTNjd6UVE3QWFMdHFuSkthV2Y3N0FjM1d3WTItTEtXVVZTM1dJRUsyY2sifQ..At8ecdCMaUHQIo-22FKHBIocZ7hFxIHQxo0u_61tMKvDUy_BStKt46Aqh7lt41OLfIwEu6b4ZjLmWTeMi5SV-KMEAdRnYp5_WAmVhJttDkpf3d32uBcql0xJChAd93mr4qUnMvo-p3ltYgiKkG6_IHt8lUPt6BaH_BqWvidmtM5_Hd1SyW92OkEm1d50eMIJMxwY2e7_4qGKWqnMTel-dmY4HXlfqwfkkwkfvzVxQgOLtw0pzPKx3qODxuiOx6CVzr_QTrs12ugt7YkVkXSuVT40vekPw006O6aDOyETITK6OmDGnAB3iRqEX_qcOco2GWT0o66J2IARbtd12Hd0OpI0seqLONmSGNxcIxMcQwp5SAQa92xKkGFHgW86zAi6DQGggAh-Pb7MMN-i20jv1iC5hEpcd_eSKFDAGp_paEJkjOy-WPyhsksssdpival9OkXCQmHQnGzqu-RS4CS5l3gkAL-q5HoZHGD-YbXaYXRac4nyCrvmYg5yDBFSOvi4v07bokXiIp52tVAhuagmishsIDPI52ke4hkhHP68mUIQaA8UBhekNxPoEpXWfNwBmJpMc4Aa17Ko5WGYPG01_w11EXZ2q80-T4eINWdfAZAhrrmJwSmPQwnsrnuwTTOpl89vSeXi17KM-VxAf9kGrvE5scAtwGEjTlr56LEUieI
```
#### Erzeugung der vollständigen Signatur inklusive Payload
Die Prüfung der Signatur kann nur auf einer vollständige Signatur erfolgen.
Für die Umwandlung einer Detached Signature in eine gewöhnliche JSON Web Signature muss der Payload (`destinationParameters`) als Base64-URL-Encoded String der Signature hinzugefügt werden.
Dabei ist für das JSON des Payload zu beachten, dass
- alle semantisch unbedeutenden nicht-druckbaren Zeichen (Leerzeichen, Tabs, Line Feed `\n`, Carriage Return `\r`) vor und nach den strukturierenden Zeichen ([, {, ], }, :, ,) aus dem JSON-Payload entfernt werden und
- die Attribute des JSON-Objekts in alphabetischer Reihenfolge sortiert werden.
<Tabs
defaultValue="java"
values={[
{ label: 'Java', value: 'java', },
]
}>
<TabItem value="java">
```java
int firstPoint = detachedSignature.indexOf(".");
Base64URL payload = Base64URL.encode(sortedAndCleanedPayloadJson):
SignedJWT signedJWT = new SignedJWT(
new Base64URL(headerParameterOfJWS.substring(0, firstPoint)),
payload,
new Base64URL(headerParameterOfJWS.substring(firstPoint + 2))
);
```
</TabItem>
</Tabs>
#### Prüfung der vollständigen Signatur
Um die Signatur zu überprüfen, ist es notwendig auf die verwendeten Schlüssel (im Format JSON Web Key, kurz JWK) zugreifen zu können.
Der Zustelldienst stellt ein JSON Web Key Set (JWKS) öffentlich zugänglich über den Endpunkt <ApiLink api="submission-api" to="/.well-known/jwks.json" /> bereit.
Da der Zustelldienst mit mehreren Instanzen betrieben werden kann, kann es auch mehrere Endpunkte zum JWKS geben.
Diese Endpunkte können dynamisch aus dem Payload (`Zustellpunkt`) erzeugt werden.
Dabei setzt sich der Endpunkt aus dem Attribut `submissionUrl` + '/.well-known/jwks.json' zusammen.
Vor der eigentlichen Signaturprüfung muss die Einhaltung einiger Grundvoraussetzungen geprüft werden:
1. Prüfung auf erlaubten Algorithmus `PS512` im Header des JWT gemäß den [Vorgaben für kryptographische Verfahren](../details/crypto.md).
2. Erweiterung der URL des zuständigen Zustelldienstes (`submissionUrl`) aus dem Payload des JWT (`destinationParameters`) um den Pfad `/.well-known/jwks.json`. Dabei muss geprüft werden, ob die URL zu einem vertrauenswürdigen Zustelldienst gehört.
3. Ermitteln des öffentlichen Schlüssel über die Key-ID (`kid`), die im Header des JSON Web Signature hinterlegt ist.
4. Prüfung, dass der öffentliche Schlüssel eine Länge von 4096 bit besitzt.
5. Prüfung, dass der öffentliche Schlüssel eine Verwendung des Algorithmus `PS512` erlaubt.
6. Prüfung der Signatur.
<Tabs
defaultValue="java"
values={[
{ label: 'Java', value: 'java', },
]
}>
<TabItem value="java">
```java
private final static int PUBLICKEYSIZE = 4096;
private final static String DVDV_SUBMISSIONURL_KEY = "submissionUrl";
private final static String KEYSTORE_URL_ENDING = ".well-known/jwks.json";
private final static String[] VALID_KEYSTORE_URLS = new String[]{ "https://submission-api-testing.fit-connect.fitko.dev/v1/.well-known/jwks.json"};
protected void validateBySignedJWT(SignedJWT signedJWT) throws BadJOSEException, JOSEException, IOException, ParseException
{
//1. Prüfung auf erlaubten Algorithmus PS512
if ( !JWSAlgorithm.PS512.equals(signedJWT.getHeader().getAlgorithm()) )
throw new RuntimeException("JWSAlgorithm should be PS512!");
//Key für den PublicKey aus dem Header des JWT ermitteln
String keyID = signedJWT.getHeader().getKeyID();
if ( keyID == null )
throw new RuntimeException("Header KeyId should not be null!");
//2. URL zum PublicKey Keystore ermitteln
Optional<URL> keyStoreUrl = getKeyStoreUrl(getBaseKeyStoreUrl(signedJWT));
if ( keyStoreUrl.isEmpty() )
throw new RuntimeException("No JWKSetUrl found in Payload!");
validateKeyStoreUrl(keyStoreUrl.get());
//KeyStore laden
JWKSet jwks = getJWKSet(keyStoreUrl.get());
//3. Public Key ermitteln
JWK publicKey = jwks.getKeyByKeyId(keyID);
if ( publicKey == null )
throw new RuntimeException("PublicKey should not be null!");
//4. Public Key muss eine Länge von 4096 bit haben!
if ( PUBLICKEYSIZE != publicKey.size() )
throw new RuntimeException("The key specified for signature verification is not of size 4096 bit.");
//5. Der PublicKey muss den Algorithmus PS512 verwenden!
if( !JWSAlgorithm.PS512.equals(publicKey.getAlgorithm()) )
throw new RuntimeException("The key specified for signature verification doesn't use/specify PS512 as algorithm.");
//6. Eigentliche Prüfung der Signatur
if (!signedJWT.verify(new RSASSAVerifier(publicKey.toRSAKey())))
throw new RuntimeException("Signature of payload not valid!");
}
```
```java
protected String getBaseKeyStoreUrl(SignedJWT signedJWT)
{
Map<String, Object> payload = signedJWT.getPayload().toJSONObject();
return String.valueOf(payload.get(DVDV_SUBMISSIONURL_KEY));
}
```
```java
protected Optional<URL> getKeyStoreUrl(String baseKeyStoreUrl)
{
if (baseKeyStoreUrl==null)
throw new RuntimeException("KeyStoreUrl not set!");
if (!baseKeyStoreUrl.endsWith("/"))
baseKeyStoreUrl+="/";
baseKeyStoreUrl+= KEYSTORE_URL_ENDING;
try
{
return Optional.of(new URL(baseKeyStoreUrl));
}
catch (MalformedURLException e)
{
throw new RuntimeException("KeyStoreUrl not valid! MalformedURLException for url " + baseKeyStoreUrl);
}
}
```
```java
public void validateKeyStoreUrl(URL url)
{
if ( !VALID_KEYSTORE_URLS.contains(String.valueOf(url)) )
throw new RuntimeException("KeyStoreUrl not valid!");
}
```
</TabItem>
</Tabs>
### Verwaltungspolitische Gebiete ermitteln ### Verwaltungspolitische Gebiete ermitteln
Falls für die Abfrage der `destinationId` kein amtlicher Gemeindeschlüssel oder ein amtlicher Regionalschlüssel bekannt ist, kann über den Endpunkt <ApiLink api="routing-api" to="/areas" /> nach passenden verwaltungspolitischen Gebieten gesucht werden. Falls für die Abfrage der `destinationId` kein amtlicher Gemeindeschlüssel oder ein amtlicher Regionalschlüssel bekannt ist, kann über den Endpunkt <ApiLink api="routing-api" to="/areas" /> nach passenden verwaltungspolitischen Gebieten gesucht werden.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment