diff --git a/api/pom.xml b/api/pom.xml index f1720e292779a455a2a74708fa65012de4b098ef..efa2b4ef25e21ccf1798aeb4217ff9addc408bef 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -25,6 +25,11 @@ <groupId>org.zalando</groupId> <artifactId>problem</artifactId> </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-annotations</artifactId> + </dependency> </dependencies> + </project> \ No newline at end of file diff --git a/api/src/main/java/fitconnect/api/data/Attachment.java b/api/src/main/java/fitconnect/api/data/Attachment.java deleted file mode 100644 index 7954f6e7b930ec5fb2b41281dfc4bea49fa252ad..0000000000000000000000000000000000000000 --- a/api/src/main/java/fitconnect/api/data/Attachment.java +++ /dev/null @@ -1,3 +0,0 @@ -package fitconnect.api.data; - -public record Attachment() { } diff --git a/api/src/main/java/fitconnect/api/data/Data.java b/api/src/main/java/fitconnect/api/data/Data.java deleted file mode 100644 index 0e65148af94752d02f66e77483823424b0abb2b3..0000000000000000000000000000000000000000 --- a/api/src/main/java/fitconnect/api/data/Data.java +++ /dev/null @@ -1,3 +0,0 @@ -package fitconnect.api.data; - -public record Data() { } diff --git a/api/src/main/java/fitconnect/api/data/Metadata.java b/api/src/main/java/fitconnect/api/data/Metadata.java deleted file mode 100644 index 0ce04e32d9427ecfd2551dcda5c4bba03f1317d5..0000000000000000000000000000000000000000 --- a/api/src/main/java/fitconnect/api/data/Metadata.java +++ /dev/null @@ -1,3 +0,0 @@ -package fitconnect.api.data; - -public record Metadata() { } diff --git a/api/src/main/java/fitconnect/api/domain/metadata/AdditionalReferenceInfo.java b/api/src/main/java/fitconnect/api/domain/metadata/AdditionalReferenceInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..b55e7df9eb812b101dcb59da588878e2d94eb65b --- /dev/null +++ b/api/src/main/java/fitconnect/api/domain/metadata/AdditionalReferenceInfo.java @@ -0,0 +1,30 @@ +package fitconnect.api.domain.metadata; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; + +import java.util.Map; + +/** + * Eine Struktur, um zusätzliche Informationen zu hinterlegen + */ + +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ + "senderReference", + "applicationDate" +}) +public record AdditionalReferenceInfo( + + @JsonProperty("senderReference") + String senderReference, + + @JsonProperty("applicationDate") + String applicationDate, + + @JsonIgnore + Map<String, Object> additionalProperties +) { } + diff --git a/api/src/main/java/fitconnect/api/domain/metadata/AuthenticationInformation.java b/api/src/main/java/fitconnect/api/domain/metadata/AuthenticationInformation.java new file mode 100644 index 0000000000000000000000000000000000000000..e20ceb3e3287d5c1e076a53c3e1bf5b40ab6598a --- /dev/null +++ b/api/src/main/java/fitconnect/api/domain/metadata/AuthenticationInformation.java @@ -0,0 +1,4 @@ +package fitconnect.api.domain.metadata; + +public record AuthenticationInformation() { +} diff --git a/api/src/main/java/fitconnect/api/domain/metadata/ContentStructure.java b/api/src/main/java/fitconnect/api/domain/metadata/ContentStructure.java new file mode 100644 index 0000000000000000000000000000000000000000..a25a9f89eaafbf0d8fd86a6cccf8c38e3cc39efa --- /dev/null +++ b/api/src/main/java/fitconnect/api/domain/metadata/ContentStructure.java @@ -0,0 +1,31 @@ +package fitconnect.api.domain.metadata; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import fitconnect.api.domain.metadata.attachment.Attachment; +import fitconnect.api.domain.metadata.data.Data; + +import java.util.List; +import java.util.Map; + +/** + * Beschreibt die Struktur der zusätzlichen Inhalte der Einreichung, wie Anlagen oder Fachdaten. + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ + "data", + "attachments" +}) +public record ContentStructure( + + @JsonProperty("data") + Data data, + + @JsonProperty("attachments") + List<Attachment> attachments, + + @JsonIgnore + Map<String, Object> additionalProperties +) { } diff --git a/api/src/main/java/fitconnect/api/domain/metadata/Metadata.java b/api/src/main/java/fitconnect/api/domain/metadata/Metadata.java new file mode 100644 index 0000000000000000000000000000000000000000..889f0c4656d5f64fe673ab43fb06bcce4b88fd15 --- /dev/null +++ b/api/src/main/java/fitconnect/api/domain/metadata/Metadata.java @@ -0,0 +1,42 @@ +package fitconnect.api.domain.metadata; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; + +import java.util.List; +import java.util.Map; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ + "contentStructure", + "publicServiceType", + "authenticationInformation", + "paymentInformation", + "replyChannel", + "additionalReferenceInfo" +}) +public record Metadata( + @JsonProperty("contentStructure") + ContentStructure contentStructure, + + @JsonProperty("publicServiceType") + PublicServiceType publicServiceType, + + @JsonProperty("authenticationInformation") + List<AuthenticationInformation> authenticationInformation, + + @JsonProperty("paymentInformation") + PaymentInformation paymentInformation, + + @JsonProperty("replyChannel") + ReplyChannel replyChannel, + + @JsonProperty("additionalReferenceInfo") + AdditionalReferenceInfo additionalReferenceInfo, + + @JsonIgnore + Map<String, Object> additionalProperties +) { } + diff --git a/api/src/main/java/fitconnect/api/domain/metadata/PaymentInformation.java b/api/src/main/java/fitconnect/api/domain/metadata/PaymentInformation.java new file mode 100644 index 0000000000000000000000000000000000000000..3e78e452ad3d6dee3beab22c2314d126023dda83 --- /dev/null +++ b/api/src/main/java/fitconnect/api/domain/metadata/PaymentInformation.java @@ -0,0 +1,4 @@ +package fitconnect.api.domain.metadata; + +public record PaymentInformation() { +} diff --git a/api/src/main/java/fitconnect/api/domain/metadata/PublicServiceType.java b/api/src/main/java/fitconnect/api/domain/metadata/PublicServiceType.java new file mode 100644 index 0000000000000000000000000000000000000000..d91252768f810e7e3b3f5d989002a2f6f3582f66 --- /dev/null +++ b/api/src/main/java/fitconnect/api/domain/metadata/PublicServiceType.java @@ -0,0 +1,37 @@ +package fitconnect.api.domain.metadata; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; + +import java.util.Map; + +/** + * Verwaltungsleistung + * <p> + * Beschreibung der Art der Verwaltungsleistung. Eine Verwaltungsleistung sollte immer mit einer LeiKa-Id beschrieben werden. + * <p> + * Ist für die gegebene Verwaltungsleistung keine LeiKa-Id vorhanden, kann die Verwaltungsleistung übergangsweise über die Angabe einer anderen eindeutigen Schema-URN beschrieben werden. + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ + "name", + "description", + "identifier" +}) +public record PublicServiceType( + + @JsonProperty("name") + String name, + + @JsonProperty("description") + String description, + + @JsonProperty("identifier") + String identifier, + + @JsonIgnore + Map<String, Object> additionalProperties +) { } + diff --git a/api/src/main/java/fitconnect/api/domain/metadata/ReplyChannel.java b/api/src/main/java/fitconnect/api/domain/metadata/ReplyChannel.java new file mode 100644 index 0000000000000000000000000000000000000000..9384433d09e0f1eff3c480f8d0f170efeaa6b1dc --- /dev/null +++ b/api/src/main/java/fitconnect/api/domain/metadata/ReplyChannel.java @@ -0,0 +1,4 @@ +package fitconnect.api.domain.metadata; + +public record ReplyChannel() { +} diff --git a/api/src/main/java/fitconnect/api/domain/metadata/attachment/Attachment.java b/api/src/main/java/fitconnect/api/domain/metadata/attachment/Attachment.java new file mode 100644 index 0000000000000000000000000000000000000000..a78b0be1e42d4b365e7aa32048b33d554a5951c0 --- /dev/null +++ b/api/src/main/java/fitconnect/api/domain/metadata/attachment/Attachment.java @@ -0,0 +1,53 @@ +package fitconnect.api.domain.metadata.attachment; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import fitconnect.api.domain.metadata.attachment.signature.Hash__1; +import fitconnect.api.domain.metadata.attachment.signature.Signature__1; + +import java.util.Map; +import java.util.UUID; + + +/** + * Eine in der Einreichung enthaltene Anlage. + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ + "hash", + "signature", + "purpose", + "filename", + "description", + "mimeType", + "attachmentId" +}) +public record Attachment( + + @JsonProperty("hash") + Hash__1 hash, + + @JsonProperty("signature") + Signature__1 signature, + + @JsonProperty("purpose") + Purpose purpose, + + @JsonProperty("filename") + String filename, + + @JsonProperty("description") + String description, + + @JsonProperty("mimeType") + String mimeType, + + @JsonProperty("attachmentId") + UUID attachmentId, + @JsonIgnore + Map<String, Object> additionalProperties +) { } + + diff --git a/api/src/main/java/fitconnect/api/domain/metadata/attachment/Purpose.java b/api/src/main/java/fitconnect/api/domain/metadata/attachment/Purpose.java new file mode 100644 index 0000000000000000000000000000000000000000..5505532561a231e694701c3b0a64f2cdd857ee35 --- /dev/null +++ b/api/src/main/java/fitconnect/api/domain/metadata/attachment/Purpose.java @@ -0,0 +1,52 @@ +package fitconnect.api.domain.metadata.attachment; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.HashMap; +import java.util.Map; + +/** + * Zweck/Art der Anlage + * - form: Automatisch generierte PDF-Repräsentation des vollständigen Antragsformulars + * - attachment: Anlage, die von einem Bürger hochgeladen wurde + * - report: Vom Onlinedienst, nachträglich erzeugte Unterlage + */ +public enum Purpose { + + FORM("form"), + ATTACHMENT("attachment"), + REPORT("report"); + private final String value; + private final static Map<String, Purpose> CONSTANTS = new HashMap<>(); + + static { + for (Purpose c : values()) { + CONSTANTS.put(c.value, c); + } + } + + Purpose(String value) { + this.value = value; + } + + @Override + public String toString() { + return this.value; + } + + @JsonValue + public String value() { + return this.value; + } + + @JsonCreator + public static Purpose fromValue(String value) { + Purpose constant = CONSTANTS.get(value); + if (constant == null) { + throw new IllegalArgumentException(value); + } else { + return constant; + } + } +} diff --git a/api/src/main/java/fitconnect/api/domain/metadata/attachment/signature/EidasAdesProfile.java b/api/src/main/java/fitconnect/api/domain/metadata/attachment/signature/EidasAdesProfile.java new file mode 100644 index 0000000000000000000000000000000000000000..c601b49bd25c537624ae9d258676a9961bbd0554 --- /dev/null +++ b/api/src/main/java/fitconnect/api/domain/metadata/attachment/signature/EidasAdesProfile.java @@ -0,0 +1,54 @@ +package fitconnect.api.domain.metadata.attachment.signature; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.HashMap; +import java.util.Map; + +/** + * Referenziert ein eindeutiges Profil einer AdES (advanced electronic signature/seal) gemäß eIDAS-Verordnung über eine URI gemäß [ETSI TS 119 192](https://www.etsi.org/deliver/etsi_ts/119100_119199/119192/01.01.01_60/ts_119192v010101p.pdf). + * + * Für die Details zur Verwendung und Validierung von Profilen siehe auch https://ec.europa.eu/cefdigital/DSS/webapp-demo/doc/dss-documentation.html#_signatures_profile_simplification + * + */ +public enum EidasAdesProfile { + + HTTP_URI_ETSI_ORG_ADES_191_X_2_LEVEL_BASELINE_B_B("http://uri.etsi.org/ades/191x2/level/baseline/B-B#"), + HTTP_URI_ETSI_ORG_ADES_191_X_2_LEVEL_BASELINE_B_T("http://uri.etsi.org/ades/191x2/level/baseline/B-T#"), + HTTP_URI_ETSI_ORG_ADES_191_X_2_LEVEL_BASELINE_B_LT("http://uri.etsi.org/ades/191x2/level/baseline/B-LT#"), + HTTP_URI_ETSI_ORG_ADES_191_X_2_LEVEL_BASELINE_B_LTA("http://uri.etsi.org/ades/191x2/level/baseline/B-LTA#"); + private final String value; + private final static Map<String, EidasAdesProfile> CONSTANTS = new HashMap<String, EidasAdesProfile>(); + + static { + for (EidasAdesProfile c: values()) { + CONSTANTS.put(c.value, c); + } + } + + EidasAdesProfile(String value) { + this.value = value; + } + + @Override + public String toString() { + return this.value; + } + + @JsonValue + public String value() { + return this.value; + } + + @JsonCreator + public static EidasAdesProfile fromValue(String value) { + EidasAdesProfile constant = CONSTANTS.get(value); + if (constant == null) { + throw new IllegalArgumentException(value); + } else { + return constant; + } + } + +} diff --git a/api/src/main/java/fitconnect/api/domain/metadata/attachment/signature/Hash__1.java b/api/src/main/java/fitconnect/api/domain/metadata/attachment/signature/Hash__1.java new file mode 100644 index 0000000000000000000000000000000000000000..8792354745bb9c905ea308675ec2471860738a7b --- /dev/null +++ b/api/src/main/java/fitconnect/api/domain/metadata/attachment/signature/Hash__1.java @@ -0,0 +1,33 @@ +package fitconnect.api.domain.metadata.attachment.signature; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; + +import java.util.Map; + + +/** + * Hashwert + * <p> + * Der Hashwert der unverschlüsselten Anlage. + * Die Angabe des Hashwertes dient der Integritätssicherung des Gesamtantrags und schützt vor einem Austausch der Anlage durch Systeme zwischen Sender und Subscriber (z.B. dem Zustelldienst). + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ + "type", + "content" +}) +public record Hash__1( + + @JsonProperty("type") + Type type, + + @JsonProperty("content") + String content, + + @JsonIgnore + Map<String, Object> additionalProperties +) { } + diff --git a/api/src/main/java/fitconnect/api/domain/metadata/attachment/signature/SignatureFormat.java b/api/src/main/java/fitconnect/api/domain/metadata/attachment/signature/SignatureFormat.java new file mode 100644 index 0000000000000000000000000000000000000000..9df5dd4ee9ae991cae390246458bf72c7d5d5fa4 --- /dev/null +++ b/api/src/main/java/fitconnect/api/domain/metadata/attachment/signature/SignatureFormat.java @@ -0,0 +1,53 @@ +package fitconnect.api.domain.metadata.attachment.signature; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.HashMap; +import java.util.Map; + +/** + * Beschreibt, welches Signaturformat die genutzte Signatur / das genutzte Siegel nutzt. Aktuell wird die Hinterlegung folgender Signaturformate unterstützt: CMS = Cryptographic Message Syntax, Asic = Associated Signature Containers, PDF = PDF Signatur, XML = XML-Signature, JSON = JSON Web Signature. + * + */ +public enum SignatureFormat { + + CMS("cms"), + XML("xml"), + PDF("pdf"), + ASIC("asic"), + JSON("json"); + private final String value; + private final static Map<String, SignatureFormat> CONSTANTS = new HashMap<>(); + + static { + for (SignatureFormat c: values()) { + CONSTANTS.put(c.value, c); + } + } + + SignatureFormat(String value) { + this.value = value; + } + + @Override + public String toString() { + return this.value; + } + + @JsonValue + public String value() { + return this.value; + } + + @JsonCreator + public static SignatureFormat fromValue(String value) { + SignatureFormat constant = CONSTANTS.get(value); + if (constant == null) { + throw new IllegalArgumentException(value); + } else { + return constant; + } + } + +} \ No newline at end of file diff --git a/api/src/main/java/fitconnect/api/domain/metadata/attachment/signature/Signature__1.java b/api/src/main/java/fitconnect/api/domain/metadata/attachment/signature/Signature__1.java new file mode 100644 index 0000000000000000000000000000000000000000..e35b63a102fb06baea712b7854583c09a0086035 --- /dev/null +++ b/api/src/main/java/fitconnect/api/domain/metadata/attachment/signature/Signature__1.java @@ -0,0 +1,39 @@ +package fitconnect.api.domain.metadata.attachment.signature; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; + +import java.util.Map; + + +/** + * Beschreibt das Signaturformt und Profile + * + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ + "signatureFormat", + "eidasAdesProfile", + "detachedSignature", + "content" +}) +public record Signature__1( + + @JsonProperty("signatureFormat") + SignatureFormat signatureFormat, + + @JsonProperty("eidasAdesProfile") + EidasAdesProfile eidasAdesProfile, + + @JsonProperty("detachedSignature") + Boolean detachedSignature, + + @JsonProperty("content") + String content, + + @JsonIgnore + Map<String, Object> additionalProperties +) { } + diff --git a/api/src/main/java/fitconnect/api/domain/metadata/attachment/signature/Type.java b/api/src/main/java/fitconnect/api/domain/metadata/attachment/signature/Type.java new file mode 100644 index 0000000000000000000000000000000000000000..e14d3aa86e80a82973a1f66c412902efeeef71ac --- /dev/null +++ b/api/src/main/java/fitconnect/api/domain/metadata/attachment/signature/Type.java @@ -0,0 +1,49 @@ +package fitconnect.api.domain.metadata.attachment.signature; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.HashMap; +import java.util.Map; + +/** + * Der verwendete Hash-Algorithmus. Derzeit ist nur `sha512` erlaubt. + * + */ +public enum Type { + + SHA_512("sha512"); + private final String value; + private final static Map<String, Type> CONSTANTS = new HashMap<>(); + + static { + for (Type c: values()) { + CONSTANTS.put(c.value, c); + } + } + + Type(String value) { + this.value = value; + } + + @Override + public String toString() { + return this.value; + } + + @JsonValue + public String value() { + return this.value; + } + + @JsonCreator + public static Type fromValue(String value) { + Type constant = CONSTANTS.get(value); + if (constant == null) { + throw new IllegalArgumentException(value); + } else { + return constant; + } + } + +} diff --git a/api/src/main/java/fitconnect/api/domain/metadata/data/Data.java b/api/src/main/java/fitconnect/api/domain/metadata/data/Data.java new file mode 100644 index 0000000000000000000000000000000000000000..d43ef300c8ca9158a68943f921c55f1d045d3798 --- /dev/null +++ b/api/src/main/java/fitconnect/api/domain/metadata/data/Data.java @@ -0,0 +1,34 @@ +package fitconnect.api.domain.metadata.data; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; + +import java.util.Map; + +/** + * Definiert das Schema und die Signatur(-art), die für die Fachdaten verwendet werden. + * + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ + "signature", + "hash", + "submissionSchema" +}) +public record Data( + + @JsonProperty("signature") + Signature signature, + + @JsonProperty("hash") + Hash hash, + + @JsonProperty("submissionSchema") + SubmissionSchema submissionSchema, + + @JsonIgnore + Map<String, Object> additionalProperties +) { } + diff --git a/api/src/main/java/fitconnect/api/domain/metadata/data/EidasAdesProfile.java b/api/src/main/java/fitconnect/api/domain/metadata/data/EidasAdesProfile.java new file mode 100644 index 0000000000000000000000000000000000000000..0fd71a987508c907262c061cba264c1a0b66d41e --- /dev/null +++ b/api/src/main/java/fitconnect/api/domain/metadata/data/EidasAdesProfile.java @@ -0,0 +1,55 @@ +package fitconnect.api.domain.metadata.data; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.HashMap; +import java.util.Map; + +/** + * Referenziert ein eindeutiges Profil einer AdES (advanced electronic signature/seal) gemäß eIDAS-Verordnung über eine URI gemäß [ETSI TS 119 192](https://www.etsi.org/deliver/etsi_ts/119100_119199/119192/01.01.01_60/ts_119192v010101p.pdf). + * + * Für die Details zur Verwendung und Validierung von Profilen siehe auch https://ec.europa.eu/cefdigital/DSS/webapp-demo/doc/dss-documentation.html#_signatures_profile_simplification + * + */ +public enum EidasAdesProfile { + + HTTP_URI_ETSI_ORG_ADES_191_X_2_LEVEL_BASELINE_B_B("http://uri.etsi.org/ades/191x2/level/baseline/B-B#"), + HTTP_URI_ETSI_ORG_ADES_191_X_2_LEVEL_BASELINE_B_T("http://uri.etsi.org/ades/191x2/level/baseline/B-T#"), + HTTP_URI_ETSI_ORG_ADES_191_X_2_LEVEL_BASELINE_B_LT("http://uri.etsi.org/ades/191x2/level/baseline/B-LT#"), + HTTP_URI_ETSI_ORG_ADES_191_X_2_LEVEL_BASELINE_B_LTA("http://uri.etsi.org/ades/191x2/level/baseline/B-LTA#"); + private final String value; + private final static Map<String, EidasAdesProfile> CONSTANTS = new HashMap<>(); + + static { + for (EidasAdesProfile c: values()) { + CONSTANTS.put(c.value, c); + } + } + + EidasAdesProfile(String value) { + this.value = value; + } + + @Override + public String toString() { + return this.value; + } + + @JsonValue + public String value() { + return this.value; + } + + @JsonCreator + public static EidasAdesProfile fromValue(String value) { + EidasAdesProfile constant = CONSTANTS.get(value); + if (constant == null) { + throw new IllegalArgumentException(value); + } else { + return constant; + } + } + +} + diff --git a/api/src/main/java/fitconnect/api/domain/metadata/data/Hash.java b/api/src/main/java/fitconnect/api/domain/metadata/data/Hash.java new file mode 100644 index 0000000000000000000000000000000000000000..7000552245dcc8c67209e8f51191ede5efaf1efe --- /dev/null +++ b/api/src/main/java/fitconnect/api/domain/metadata/data/Hash.java @@ -0,0 +1,32 @@ +package fitconnect.api.domain.metadata.data; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; + +import java.util.Map; + + +/** + * Hashwert + * <p> + * Der Hashwert der unverschlüsselten Fachdaten. Die Angabe des Hashwertes dient der Integritätssicherung des Gesamtantrags und schützt vor einem Austausch der Fachdaten durch Systeme zwischen Sender und Subscriber (z.B. dem Zustelldienst). + * + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ + "type", + "content" +}) +public record Hash( + + @JsonProperty("type") + Type type, + + @JsonProperty("content") + String content, + + @JsonIgnore + Map<String, Object> additionalProperties +){ } diff --git a/api/src/main/java/fitconnect/api/domain/metadata/data/MimeType.java b/api/src/main/java/fitconnect/api/domain/metadata/data/MimeType.java new file mode 100644 index 0000000000000000000000000000000000000000..6c3aa73762450abae72be7d288e144c13c48d1e6 --- /dev/null +++ b/api/src/main/java/fitconnect/api/domain/metadata/data/MimeType.java @@ -0,0 +1,50 @@ +package fitconnect.api.domain.metadata.data; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.HashMap; +import java.util.Map; + +/** + * Mimetype (z.B. application/json oder application/xml) des referenzierten Schemas (z.B. XSD- oder JSON-Schema). + * + */ +public enum MimeType { + + APPLICATION_JSON("application/json"), + APPLICATION_XML("application/xml"); + private final String value; + private final static Map<String, MimeType> CONSTANTS = new HashMap<>(); + + static { + for (MimeType c: values()) { + CONSTANTS.put(c.value, c); + } + } + + MimeType(String value) { + this.value = value; + } + + @Override + public String toString() { + return this.value; + } + + @JsonValue + public String value() { + return this.value; + } + + @JsonCreator + public static MimeType fromValue(String value) { + MimeType constant = CONSTANTS.get(value); + if (constant == null) { + throw new IllegalArgumentException(value); + } else { + return constant; + } + } + +} diff --git a/api/src/main/java/fitconnect/api/domain/metadata/data/Signature.java b/api/src/main/java/fitconnect/api/domain/metadata/data/Signature.java new file mode 100644 index 0000000000000000000000000000000000000000..8a7fe7278819a88baa340ee71e6828cd04b9a925 --- /dev/null +++ b/api/src/main/java/fitconnect/api/domain/metadata/data/Signature.java @@ -0,0 +1,37 @@ +package fitconnect.api.domain.metadata.data; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; + +import java.util.Map; + +/** + * Beschreibt das Signaturformt und Profile + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ + "signatureFormat", + "eidasAdesProfile", + "detachedSignature", + "content" +}) +public record Signature( + + @JsonProperty("signatureFormat") + SignatureFormat signatureFormat, + + @JsonProperty("eidasAdesProfile") + EidasAdesProfile eidasAdesProfile, + + @JsonProperty("detachedSignature") + Boolean detachedSignature, + + @JsonProperty("content") + String content, + @JsonIgnore + Map<String, Object> additionalProperties + +) { } + diff --git a/api/src/main/java/fitconnect/api/domain/metadata/data/SignatureFormat.java b/api/src/main/java/fitconnect/api/domain/metadata/data/SignatureFormat.java new file mode 100644 index 0000000000000000000000000000000000000000..87053dd923abbc9c032957100e032a6a283f2ac5 --- /dev/null +++ b/api/src/main/java/fitconnect/api/domain/metadata/data/SignatureFormat.java @@ -0,0 +1,53 @@ +package fitconnect.api.domain.metadata.data; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.HashMap; +import java.util.Map; + +/** + * Beschreibt, welches Signaturformat die genutzte Signatur / das genutzte Siegel nutzt. Aktuell wird die Hinterlegung folgender Signaturformate unterstützt: CMS = Cryptographic Message Syntax, Asic = Associated Signature Containers, PDF = PDF Signatur, XML = XML-Signature, JSON = JSON Web Signature. + * + */ +public enum SignatureFormat { + + CMS("cms"), + XML("xml"), + PDF("pdf"), + ASIC("asic"), + JSON("json"); + private final String value; + private final static Map<String, SignatureFormat> CONSTANTS = new HashMap<>(); + + static { + for (SignatureFormat c: values()) { + CONSTANTS.put(c.value, c); + } + } + + SignatureFormat(String value) { + this.value = value; + } + + @Override + public String toString() { + return this.value; + } + + @JsonValue + public String value() { + return this.value; + } + + @JsonCreator + public static SignatureFormat fromValue(String value) { + SignatureFormat constant = CONSTANTS.get(value); + if (constant == null) { + throw new IllegalArgumentException(value); + } else { + return constant; + } + } + +} \ No newline at end of file diff --git a/api/src/main/java/fitconnect/api/domain/metadata/data/SubmissionSchema.java b/api/src/main/java/fitconnect/api/domain/metadata/data/SubmissionSchema.java new file mode 100644 index 0000000000000000000000000000000000000000..b62f222e07f256030ba035a17a7a22d7cf37afd2 --- /dev/null +++ b/api/src/main/java/fitconnect/api/domain/metadata/data/SubmissionSchema.java @@ -0,0 +1,33 @@ +package fitconnect.api.domain.metadata.data; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; + +import java.net.URI; +import java.util.Map; + + +/** + * Fachdatenschema + * <p> + * Referenz auf ein Schema, das die Struktur der Fachdaten einer Einreichung beschreibt. + * + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ + "schemaUri", + "mimeType" +}) +public record SubmissionSchema( + + @JsonProperty("schemaUri") + URI schemaUri, + + @JsonProperty("mimeType") + MimeType mimeType, + + @JsonIgnore + Map<String, Object> additionalProperties +) { } diff --git a/api/src/main/java/fitconnect/api/domain/metadata/data/Type.java b/api/src/main/java/fitconnect/api/domain/metadata/data/Type.java new file mode 100644 index 0000000000000000000000000000000000000000..b6f5c1b686189042d6a708d1488128501eb2b651 --- /dev/null +++ b/api/src/main/java/fitconnect/api/domain/metadata/data/Type.java @@ -0,0 +1,49 @@ +package fitconnect.api.domain.metadata.data; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.HashMap; +import java.util.Map; + +/** + * Der verwendete Hash-Algorithmus. Derzeit ist nur `sha512` erlaubt. + * + */ +public enum Type { + + SHA_512("sha512"); + private final String value; + private final static Map<String, Type> CONSTANTS = new HashMap<>(); + + static { + for (Type c: values()) { + CONSTANTS.put(c.value, c); + } + } + + Type(String value) { + this.value = value; + } + + @Override + public String toString() { + return this.value; + } + + @JsonValue + public String value() { + return this.value; + } + + @JsonCreator + public static Type fromValue(String value) { + Type constant = CONSTANTS.get(value); + if (constant == null) { + throw new IllegalArgumentException(value); + } else { + return constant; + } + } + +} diff --git a/api/src/main/java/fitconnect/api/metadata/MetadataService.java b/api/src/main/java/fitconnect/api/metadata/MetadataService.java new file mode 100644 index 0000000000000000000000000000000000000000..e9d674575c767d7e038856084d835fe7a00cd2ff --- /dev/null +++ b/api/src/main/java/fitconnect/api/metadata/MetadataService.java @@ -0,0 +1,8 @@ +package fitconnect.api.metadata; + +import fitconnect.api.domain.metadata.Metadata; + +public interface MetadataService { + + Metadata createMetadata(); +} diff --git a/api/src/main/resources/schemas/metadata.schema.json b/api/src/main/resources/schemas/metadata.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..997aeb08a5fe08d460c0e9efc23f4229931d5b97 --- /dev/null +++ b/api/src/main/resources/schemas/metadata.schema.json @@ -0,0 +1 @@ +{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://schema.fitko.de/fit-connect/metadata/1.0.0/metadata.schema.json","type":"object","title":"Metadaten","description":"","required":["contentStructure"],"properties":{"contentStructure":{"description":"Beschreibt die Struktur der zusätzlichen Inhalte der Einreichung, wie Anlagen oder Fachdaten.","type":"object","required":["attachments"],"properties":{"data":{"description":"Definiert das Schema und die Signatur(-art), die für die Fachdaten verwendet werden.","type":"object","required":["hash","submissionSchema"],"properties":{"signature":{"type":"object","description":"Beschreibt das Signaturformt und Profile","examples":[],"properties":{"signatureFormat":{"type":"string","description":"Beschreibt, welches Signaturformat die genutzte Signatur / das genutzte Siegel nutzt. Aktuell wird die Hinterlegung folgender Signaturformate unterstützt: CMS = Cryptographic Message Syntax, Asic = Associated Signature Containers, PDF = PDF Signatur, XML = XML-Signature, JSON = JSON Web Signature. ","enum":["cms","xml","pdf","asic","json"]},"eidasAdesProfile":{"type":"string","description":"Referenziert ein eindeutiges Profil einer AdES (advanced electronic signature/seal) gemäß eIDAS-Verordnung über eine URI gemäß [ETSI TS 119 192](https://www.etsi.org/deliver/etsi_ts/119100_119199/119192/01.01.01_60/ts_119192v010101p.pdf).\n\nFür die Details zur Verwendung und Validierung von Profilen siehe auch https://ec.europa.eu/cefdigital/DSS/webapp-demo/doc/dss-documentation.html#_signatures_profile_simplification","enum":["http://uri.etsi.org/ades/191x2/level/baseline/B-B#","http://uri.etsi.org/ades/191x2/level/baseline/B-T#","http://uri.etsi.org/ades/191x2/level/baseline/B-LT#","http://uri.etsi.org/ades/191x2/level/baseline/B-LTA#"]},"detachedSignature":{"type":"boolean","description":"Beschreibt, ob die Signatur als seperate (detached) Signatur (`true`) oder als Teil des Fachdatensatzes bzw. der Anlage (`false`) übertragen wird. Wenn der Wert `true` ist, dann wird die Signatur Base64- oder Base64Url-kodiert im Feld `content` übertragen."},"content":{"type":"string","description":"Hier wird die Signatur im Falle einer Detached-Signatur als Base64- oder Base64Url-kodierte Zeichenkette hinterlegt. Eine Base64Url-Kodierung kommt nur bei Einsatz von JSON Web Signatures (JWS / JAdES) zum Einsatz.","pattern":"^[a-zA-Z0-9+/=]+|[a-zA-Z0-9_-]+$"}},"required":["signatureFormat","detachedSignature"]},"hash":{"title":"Hashwert","description":"Der Hashwert der unverschlüsselten Fachdaten. Die Angabe des Hashwertes dient der Integritätssicherung des Gesamtantrags und schützt vor einem Austausch der Fachdaten durch Systeme zwischen Sender und Subscriber (z.B. dem Zustelldienst).","type":"object","required":["type","content"],"properties":{"type":{"type":"string","description":"Der verwendete Hash-Algorithmus. Derzeit ist nur `sha512` erlaubt.","enum":["sha512"]},"content":{"type":"string","description":"Der Hex-kodierte Hashwert gemäß des angegebenen Algorithmus.","pattern":"^[a-f0-9]{128}$"}}},"submissionSchema":{"title":"Fachdatenschema","description":"Referenz auf ein Schema, das die Struktur der Fachdaten einer Einreichung beschreibt.","type":"object","required":["schemaUri","mimeType"],"properties":{"schemaUri":{"type":"string","format":"uri","description":"URI des Fachschemas. Wird hier eine URL verwendet, sollte das Schema unter der angegebenen URL abrufbar sein. Eine Verfügbarkeit des Schemas unter der angegebenen URL darf jedoch nicht vorausgesetzt werden."},"mimeType":{"type":"string","description":"Mimetype (z.B. application/json oder application/xml) des referenzierten Schemas (z.B. XSD- oder JSON-Schema).","enum":["application/json","application/xml"]}}}}},"attachments":{"type":"array","items":{"type":"object","description":"Eine in der Einreichung enthaltene Anlage.","required":["hash","purpose","mimeType","attachmentId"],"properties":{"hash":{"title":"Hashwert","description":"Der Hashwert der unverschlüsselten Anlage. Die Angabe des Hashwertes dient der Integritätssicherung des Gesamtantrags und schützt vor einem Austausch der Anlage durch Systeme zwischen Sender und Subscriber (z.B. dem Zustelldienst).","type":"object","required":["type","content"],"properties":{"type":{"type":"string","description":"Der verwendete Hash-Algorithmus. Derzeit ist nur `sha512` erlaubt.","enum":["sha512"]},"content":{"type":"string","description":"Der Hex-kodierte Hashwert gemäß des angegebenen Algorithmus.","pattern":"^[a-f0-9]{128}$"}}},"signature":{"$ref":"#/properties/contentStructure/properties/data/properties/signature"},"purpose":{"description":"Zweck/Art der Anlage\n- form: Automatisch generierte PDF-Repräsentation des vollständigen Antragsformulars\n- attachment: Anlage, die von einem Bürger hochgeladen wurde\n- report: Vom Onlinedienst, nachträglich erzeugte Unterlage","type":"string","enum":["form","attachment","report"]},"filename":{"type":"string","description":"Ursprünglicher Dateiname bei Erzeugung oder Upload"},"description":{"type":"string","description":"Optionale Beschreibung der Anlage"},"mimeType":{"type":"string","title":"MIME Type","description":"Internet Media Type gemäß RFC 2045, z. B. application/pdf.","examples":["application/xml"],"pattern":"^[-\\w.]+/[-\\w.+]+$"},"attachmentId":{"type":"string","description":"Innerhalb einer Einreichung eindeutige Id der Anlage im Format einer UUIDv4.","format":"uuid","minLength":32,"maxLength":36}}}}}},"publicServiceType":{"type":"object","title":"Verwaltungsleistung","description":"Beschreibung der Art der Verwaltungsleistung. Eine Verwaltungsleistung sollte immer mit einer LeiKa-Id beschrieben werden. Ist für die gegebene Verwaltungsleistung keine LeiKa-Id vorhanden, kann die Verwaltungsleistung übergangsweise über die Angabe einer anderen eindeutigen Schema-URN beschrieben werden.","properties":{"name":{"type":"string","description":"Name/Bezeichnung der Verwaltungsleistung"},"description":{"type":"string","description":"(Kurz-)Beschreibung der Verwaltungsleistung"},"identifier":{"title":"Leistungs-Identifikator","description":"URN einer Leistung. Im Falle einer Leistung aus dem Leistungskatalog sollte hier `urn:de:fim:leika:leistung:` vorangestellt werden.\n","type":"string","minLength":7,"maxLength":255,"pattern":"^urn:[a-z0-9][a-z0-9-]{0,31}:[a-z0-9()+,.:=@;$_!*'%/?#-]+$"}},"required":["identifier"]},"authenticationInformation":{"description":"Eine Liste aller Identifikationsnachweise der Einreichung.","type":"array","minItems":1,"items":{"type":"object","description":"Eine Struktur, die einen Identifikationsnachweis beschreibt.","properties":{"type":{"description":"Definiert die Art des Identifikationsnachweises.","type":"string","enum":["identificationReport"]},"version":{"type":"string","pattern":"^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)$","description":"semver kompatible Versionsangabe des genutzten Nachweistyps."},"content":{"type":"string","description":"Der Nachweis wird als Base64Url-kodierte Zeichenkette angegeben.","pattern":"^[a-zA-Z0-9_\\-.]+$"}},"required":["type","version","content"]}},"paymentInformation":{"description":"Dieses Objekt enthält die Informationen vom Bezahldienst.","type":"object","required":["transactionReference","transactionId","paymentMethod","status"],"properties":{"transactionUrl":{"type":"string","format":"uri","minLength":1,"examples":["https://payment.bundesland.zzzz/api/v1/paymenttransaction/12002312/MELD-ANT-FORM-4711/9xxd-432x-6543-xfd6-gfdx-fd27"],"description":"Die Rest-URL der Payment Transaction für die Statusabfrage."},"transactionId":{"type":"string","minLength":1,"maxLength":36,"pattern":"^[\\w\\d-]+$","examples":["9xxd-432x-6543-xfd6-gfdx-fd27"],"description":"Eine vom Bezahldienst vergebene Transaktions-Id."},"transactionReference":{"type":"string","description":"Bezahlreferenz bzw. Verwendungszweck, wie z. B. ein Kassenzeichen."},"transactionTimestamp":{"type":"string","format":"date-time","description":"Zeitstempel der erfolgreichen Durchführung der Bezahlung."},"paymentMethod":{"type":"string","enum":["GIROPAY","PAYDIRECT","CREDITCARD","PAYPAL","INVOICE","OTHER"],"examples":["CREDITCARD"],"description":"Die vom Benutzer ausgewählte Zahlart. Das Feld ist nur bei einer erfolgreichen Zahlung vorhanden / befüllt."},"paymentMethodDetail":{"type":"string","minLength":1,"maxLength":36,"pattern":"^[\\w\\d-]+$","examples":["Visa"],"description":"Weitere Erläuterung zur gewählten Zahlart."},"status":{"type":"string","enum":["INITIAL","BOOKED","FAILED","CANCELED"],"description":"- INITIAL - der Einreichung hat einen Payment-Request ausgelöst und eine Payment-Transaction wurde angelegt. Der Nutzer hat aber im Bezahldienst noch keine Wirkung erzeugt.\n- BOOKED - der Nutzer hat die Bezahlung im Bezahldienst autorisiert.\n- FAILED - der Vorgang wurde vom Bezahldienst aufgrund der Nutzereingaben abgebrochen.\n- CANCELED - der Nutzer hat die Bezahlung im Bezahldienst abgebrochen."},"grossAmount":{"type":"number","minimum":0.01,"multipleOf":0.01,"description":"Bruttobetrag"}}},"replyChannel":{"type":"object","minProperties":1,"maxProperties":1,"properties":{"eMail":{"type":"object","properties":{"address":{"type":"string","format":"email"},"pgpPublicKey":{"type":"string","description":"Hilfe zur Erstellung gibt es in der Dokumentation unter https://docs.fitko.de/fit-connect/details/pgp-export","pattern":"^-----BEGIN PGP PUBLIC KEY BLOCK-----\\n\\n"}},"required":["address"]},"deMail":{"type":"object","description":"Akkreditierte Anbieter siehe https://www.bsi.bund.de/DE/Themen/Oeffentliche-Verwaltung/Moderner-Staat/De-Mail/Akkreditierte-DMDA/akkreditierte-dmda_node.html","properties":{"address":{"type":"string","format":"email"}},"required":["address"]},"fink":{"type":"object","description":"Postfachadresse in einem interoperablen Servicekonto (FINK.PFISK)","properties":{"finkPostfachRef":{"type":"string","description":"FINK Postfachadresse","examples":["hh/by/12345"],"maxLength":150,"pattern":"^[-._a-z0-9~/]*$"},"host":{"type":"string","description":"URL des Servicekontos, in dem das Ziel-Postfach liegt","format":"uri","examples":["https://servicekonto1.example.com/"]}},"required":["finkPostfachRef"]},"elster":{"type":"object","description":"Siehe https://www.elster.de/elsterweb/infoseite/elstertransfer_hilfe_schnittstellen","properties":{"accountId":{"type":"string","pattern":"^\\d{10}$"},"lieferTicket":{"type":"string"},"geschaeftszeichen":{"type":"string","maxLength":10}},"required":["accountId"]}}},"additionalReferenceInfo":{"type":"object","description":"Eine Struktur, um zusätzliche Informationen zu hinterlegen","properties":{"senderReference":{"type":"string","description":"Eine Referenz zum Vorgang im sendenden System, um bei Problemen und Rückfragen außerhalb von FIT-Connect den Vorgang im dortigen System schneller zu identifizieren."},"applicationDate":{"type":"string","format":"date","description":"Das Datum der Antragstellung. Das Datum muss nicht zwingend identisch mit dem Datum der Einreichung des Antrags über FIT-Connect sein."}}}}} \ No newline at end of file diff --git a/impl/pom.xml b/impl/pom.xml index 183f8537e447a7ae877ed875daaa7093cc2c0039..4d1c6f1102f0006f1cf7ed86ea7906469547a68b 100644 --- a/impl/pom.xml +++ b/impl/pom.xml @@ -31,4 +31,6 @@ </dependency> </dependencies> + + </project> \ No newline at end of file diff --git a/impl/src/main/java/fitconnect/impl/SubmissionSender.java b/impl/src/main/java/fitconnect/impl/SubmissionSender.java index 36278545d876d0090f7ab99492dd9f39225efcda..7bdcde0f639912dae211f0310cebe38a4b994a0b 100644 --- a/impl/src/main/java/fitconnect/impl/SubmissionSender.java +++ b/impl/src/main/java/fitconnect/impl/SubmissionSender.java @@ -4,6 +4,8 @@ import com.nimbusds.jose.jwk.RSAKey; import fitconnect.api.auth.OAuthService; import fitconnect.api.auth.OAuthToken; import fitconnect.api.crypto.EncryptionService; +import fitconnect.api.domain.metadata.Metadata; +import fitconnect.api.metadata.MetadataService; import fitconnect.api.problems.EncryptionProblem; import java.util.Optional; @@ -11,7 +13,7 @@ import java.util.Optional; /** * A technical system that creates a submission via the FIT-Co Submission API. */ -public class SubmissionSender implements OAuthService, EncryptionService { +public class SubmissionSender implements OAuthService, EncryptionService, MetadataService { private final OAuthService authService; private final EncryptionService encryptionService; @@ -35,4 +37,9 @@ public class SubmissionSender implements OAuthService, EncryptionService { public String encryptBytes(RSAKey publicKey, byte[] unencryptedAttachment) throws EncryptionProblem { return encryptionService.encryptBytes(publicKey, unencryptedAttachment); } + + @Override + public Metadata createMetadata() { + throw new UnsupportedOperationException("not yet implemented"); + } } diff --git a/impl/src/main/java/fitconnect/impl/crypto/MetadataVerifier.java b/impl/src/main/java/fitconnect/impl/crypto/MetadataVerifier.java new file mode 100644 index 0000000000000000000000000000000000000000..804b822e764d61d26a90a06ab6636ec81dfd7ee5 --- /dev/null +++ b/impl/src/main/java/fitconnect/impl/crypto/MetadataVerifier.java @@ -0,0 +1,49 @@ +package fitconnect.impl.crypto; + +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +public class MetadataVerifier { + + private static final String DEFAULT_ALGORITHM = "SHA-512"; // Currently, only SHA-512 is supported. + + private final MessageDigest hasher; + + public MetadataVerifier() { + try { + this.hasher = MessageDigest.getInstance(DEFAULT_ALGORITHM); + } catch (NoSuchAlgorithmException e) { + //TODO use Problem Exception Type + throw new RuntimeException(e); + } + } + + public byte[] createHash(byte[] data) { + this.hasher.reset(); + return this.hasher.digest(data); + } + + public boolean verify(byte[] originalHash, byte[] data) { + byte[] newHash = createHash(data); + return compareHashes(originalHash, newHash); + } + + public static String toHexString(byte[] hash) { + return String.format("%0128x", new BigInteger(1, hash)); + } + + public byte[] fromHexString(String hexString) { + byte[] byteArray = new BigInteger(hexString, 16).toByteArray(); + return Arrays.copyOfRange(byteArray, 1, byteArray.length); + } + + private boolean compareHashes(byte[] originalHash, byte[] comparisonHash) { + int diff = originalHash.length ^ comparisonHash.length; + for (int i = 0; i < originalHash.length && i < comparisonHash.length; i++) { + diff |= originalHash[i] ^ comparisonHash[i]; + } + return diff == 0; + } +} diff --git a/impl/src/test/java/fitconnect/impl/crypto/MetadataVerifierTest.java b/impl/src/test/java/fitconnect/impl/crypto/MetadataVerifierTest.java new file mode 100644 index 0000000000000000000000000000000000000000..70dd51d9e145bd63b3255bbc5b13a30770b48ff0 --- /dev/null +++ b/impl/src/test/java/fitconnect/impl/crypto/MetadataVerifierTest.java @@ -0,0 +1,31 @@ +package fitconnect.impl.crypto; + +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.*; + +class MetadataVerifierTest { + + MetadataVerifier underTest = new MetadataVerifier(); + + @Test + public void testMessageDigestForDataAndAttachments() throws IOException { + + // Given (sender creates hash of (Fachdaten) or attachments (Anhänge)) + byte[] exampleFileContent = getExampleFileContent("/example.pdf"); + byte[] data = exampleFileContent; + byte[] hashFromSender = underTest.createHash(data); + + // When (subscriber verifies hash of data or attachments) + byte[] dataFromSender = exampleFileContent; + boolean verificationResult = underTest.verify(hashFromSender, dataFromSender); + assertTrue(verificationResult); + } + + private byte[] getExampleFileContent(final String path) throws IOException { + return MetadataVerifier.class.getResourceAsStream(path).readAllBytes(); + } + +} \ No newline at end of file diff --git a/impl/src/test/resources/example.pdf b/impl/src/test/resources/example.pdf new file mode 100644 index 0000000000000000000000000000000000000000..92aa8424e344f57d9206aa85becd6198255a44b9 Binary files /dev/null and b/impl/src/test/resources/example.pdf differ diff --git a/pom.xml b/pom.xml index 5caf81cb56aaa3ce9109031200c7f05f97cb087a..93289ee62500b2561fbd1ae80154197a4633d260 100644 --- a/pom.xml +++ b/pom.xml @@ -52,6 +52,12 @@ <artifactId>jackson-databind</artifactId> <version>2.12.3</version> </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-annotations</artifactId> + <version>2.13.3</version> + <scope>compile</scope> + </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId>