From 7019eebcd647eb5bb92074be53832bc0f1f31be5 Mon Sep 17 00:00:00 2001 From: Tobias Senger Date: Fri, 1 Nov 2024 13:30:01 +0100 Subject: [PATCH] added getFeatures(), fixed C40/MRZ parsing, fixed lengths in SealCodings --- .../de/tsenger/vdstools/FeatureConverter.java | 21 +++- .../de/tsenger/vdstools/vds/DigitalSeal.java | 7 +- .../de/tsenger/vdstools/vds/VdsMessage.java | 14 ++- src/main/resources/SealCodings.json | 24 ++--- .../vdstools/FeatureConverterTest.java | 2 +- src/test/resources/SealCodings.json | 95 +++++++------------ 6 files changed, 86 insertions(+), 77 deletions(-) diff --git a/src/main/java/de/tsenger/vdstools/FeatureConverter.java b/src/main/java/de/tsenger/vdstools/FeatureConverter.java index 04f2fdc..9b18c43 100644 --- a/src/main/java/de/tsenger/vdstools/FeatureConverter.java +++ b/src/main/java/de/tsenger/vdstools/FeatureConverter.java @@ -71,7 +71,7 @@ public Set getAvailableVdsFeatures() { return vdsFeatures; } - public String getFeature(String vdsType, DerTlv derTlv) { + public String getFeatureName(String vdsType, DerTlv derTlv) { if (!vdsTypes.containsKey(vdsType)) { Logger.warn("No seal type with name '" + vdsType + "' was found."); return null; @@ -156,10 +156,16 @@ private DerTlv encodeFeature(SealDto sealDto, String feature, T inputValue) @SuppressWarnings("unchecked") private T decodeFeature(SealDto sealDto, DerTlv derTlv) { - String coding = getCoding(sealDto, derTlv.getTag()); + byte tag = derTlv.getTag(); + String coding = getCoding(sealDto, tag); switch (coding) { case "C40": - return (T) DataParser.decodeC40(derTlv.getValue()).replace(' ', '<'); + String featureValue = DataParser.decodeC40(derTlv.getValue()); + String featureName = getFeatureName(sealDto, tag); + if (featureName.startsWith("MRZ")) { + featureValue = featureValue.replace(' ', '<'); + } + return (T) featureValue; case "ByteArray": return (T) derTlv.getValue(); case "Utf8String": @@ -205,6 +211,15 @@ private String getCoding(SealDto sealDto, byte tag) { return null; } + private FeaturesDto getFeatureDto(SealDto sealDto, byte tag) { + for (FeaturesDto featureDto : sealDto.features) { + if (featureDto.tag == tag) { + return featureDto; + } + } + return null; + } + private SealDto getSealDto(String vdsType) { for (SealDto sealDto : sealDtoList) { if (sealDto.documentType.equals(vdsType)) { diff --git a/src/main/java/de/tsenger/vdstools/vds/DigitalSeal.java b/src/main/java/de/tsenger/vdstools/vds/DigitalSeal.java index 3555027..8529619 100644 --- a/src/main/java/de/tsenger/vdstools/vds/DigitalSeal.java +++ b/src/main/java/de/tsenger/vdstools/vds/DigitalSeal.java @@ -11,6 +11,7 @@ import java.time.LocalDate; import java.util.ArrayList; import java.util.List; +import java.util.Map; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; @@ -106,8 +107,12 @@ public String getRawString() throws IOException { return DataEncoder.encodeBase256(getEncoded()); } + public Map getFeatures() { + return vdsMessage.getFeatures(); + } + public T getFeature(String feature) { - return vdsMessage.getDocumentFeature(feature); + return vdsMessage.getFeature(feature); } public static DigitalSeal fromRawString(String rawString) { diff --git a/src/main/java/de/tsenger/vdstools/vds/VdsMessage.java b/src/main/java/de/tsenger/vdstools/vds/VdsMessage.java index 3c38398..6d6b4d3 100644 --- a/src/main/java/de/tsenger/vdstools/vds/VdsMessage.java +++ b/src/main/java/de/tsenger/vdstools/vds/VdsMessage.java @@ -3,7 +3,9 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.tinylog.Logger; @@ -51,7 +53,17 @@ public List getDerTlvList() { return this.derTlvList; } - public T getDocumentFeature(String feature) { + public Map getFeatures() { + Map featureMap = new HashMap(); + for (DerTlv derTlv : derTlvList) { + T value = DataEncoder.getFeatureEncoder().decodeFeature(vdsType, derTlv); + String key = DataEncoder.getFeatureEncoder().getFeatureName(vdsType, derTlv); + featureMap.put(key, value); + } + return featureMap; + } + + public T getFeature(String feature) { T value = null; byte tag = DataEncoder.getFeatureEncoder().getTag(vdsType, feature); for (DerTlv derTlv : derTlvList) { diff --git a/src/main/resources/SealCodings.json b/src/main/resources/SealCodings.json index 0d2b028..b53c5c7 100644 --- a/src/main/resources/SealCodings.json +++ b/src/main/resources/SealCodings.json @@ -102,8 +102,8 @@ "tag": 2, "coding": "C40", "required": true, - "minLength": 44, - "maxLength": 44 + "minLength": 48, + "maxLength": 48 }, { "name": "PASSPORT_NUMBER", @@ -141,8 +141,8 @@ "tag": 2, "coding": "C40", "required": true, - "minLength": 44, - "maxLength": 44 + "minLength": 48, + "maxLength": 48 }, { "name": "PASSPORT_NUMBER", @@ -172,8 +172,8 @@ "tag": 2, "coding": "C40", "required": true, - "minLength": 44, - "maxLength": 44 + "minLength": 48, + "maxLength": 48 } ] }, @@ -327,7 +327,7 @@ }, { "documentType": "TEMP_PASSPORT", - "documentRef": "F60D", + "documentRef": "f60d", "version": 4, "features": [ { @@ -343,14 +343,14 @@ "tag": 2, "coding": "C40", "required": true, - "minLength": 48, - "maxLength": 48 + "minLength": 60, + "maxLength": 60 } ] }, { "documentType": "TEMP_PERSO", - "documentRef": "F70B", + "documentRef": "f70b", "version": 4, "features": [ { @@ -366,8 +366,8 @@ "tag": 2, "coding": "C40", "required": true, - "minLength": 44, - "maxLength": 44 + "minLength": 48, + "maxLength": 48 } ] } diff --git a/src/test/java/de/tsenger/vdstools/FeatureConverterTest.java b/src/test/java/de/tsenger/vdstools/FeatureConverterTest.java index a729ea0..f9ef312 100644 --- a/src/test/java/de/tsenger/vdstools/FeatureConverterTest.java +++ b/src/test/java/de/tsenger/vdstools/FeatureConverterTest.java @@ -37,7 +37,7 @@ public void testFeatureConverterString_notFound() throws FileNotFoundException { @Test public void testGetFeature_String() throws IOException { FeatureConverter featureConverter = new FeatureConverter(); - String feature = featureConverter.getFeature("FICTION_CERT", + String feature = featureConverter.getFeatureName("FICTION_CERT", DerTlv.fromByteArray(Hex.decode("0306d79519a65306"))); assertEquals("PASSPORT_NUMBER", feature); } diff --git a/src/test/resources/SealCodings.json b/src/test/resources/SealCodings.json index b37a6c8..b53c5c7 100644 --- a/src/test/resources/SealCodings.json +++ b/src/test/resources/SealCodings.json @@ -1,7 +1,7 @@ [ { "documentType": "ARRIVAL_ATTESTATION", - "documentRef": "FD02", + "documentRef": "fd02", "version": 3, "features": [ { @@ -9,22 +9,22 @@ "tag": 2, "coding": "C40", "required": true, - "minLength": 72, - "maxLength": 88 + "minLength": 48, + "maxLength": 48 }, { "name": "AZR", "tag": 3, "coding": "C40", "required": true, - "minLength": 9, - "maxLength": 12 + "minLength": 8, + "maxLength": 8 } ] }, { "documentType": "ADDRESS_STICKER_ID", - "documentRef": "F908", + "documentRef": "f908", "version": 4, "features": [ { @@ -55,7 +55,7 @@ }, { "documentType": "ADDRESS_STICKER_PASSPORT", - "documentRef": "F80A", + "documentRef": "f80a", "version": 4, "features": [ { @@ -86,7 +86,7 @@ }, { "documentType": "ALIENS_LAW", - "documentRef": "01FE", + "documentRef": "01fe", "version": 4, "features": [ { @@ -95,31 +95,31 @@ "coding": "ByteArray", "required": true, "minLength": 1, - "maxLength": 1500 + "maxLength": 1000 }, { "name": "MRZ", "tag": 2, "coding": "C40", "required": true, - "minLength": 72, - "maxLength": 72 + "minLength": 48, + "maxLength": 48 }, { "name": "PASSPORT_NUMBER", "tag": 3, "coding": "C40", "required": true, - "minLength": 1, - "maxLength": 9 + "minLength": 6, + "maxLength": 6 }, { "name": "AZR", "tag": 4, "coding": "C40", "required": true, - "minLength": 1, - "maxLength": 12 + "minLength": 8, + "maxLength": 8 } ] }, @@ -134,31 +134,31 @@ "coding": "ByteArray", "required": true, "minLength": 1, - "maxLength": 1500 + "maxLength": 1000 }, { "name": "MRZ", "tag": 2, "coding": "C40", "required": true, - "minLength": 72, - "maxLength": 72 + "minLength": 48, + "maxLength": 48 }, { "name": "PASSPORT_NUMBER", "tag": 3, "coding": "C40", "required": true, - "minLength": 1, - "maxLength": 9 + "minLength": 6, + "maxLength": 6 }, { "name": "AZR", "tag": 4, "coding": "C40", "required": true, - "minLength": 1, - "maxLength": 12 + "minLength": 8, + "maxLength": 8 } ] }, @@ -172,8 +172,8 @@ "tag": 2, "coding": "C40", "required": true, - "minLength": 72, - "maxLength": 72 + "minLength": 48, + "maxLength": 48 } ] }, @@ -242,7 +242,7 @@ }, { "documentType": "RESIDENCE_PERMIT", - "documentRef": "FB06", + "documentRef": "fb06", "version": 4, "features": [ { @@ -265,7 +265,7 @@ }, { "documentType": "SOCIAL_INSURANCE_CARD", - "documentRef": "FC04", + "documentRef": "fc04", "version": 4, "features": [ { @@ -304,7 +304,7 @@ }, { "documentType": "SUPPLEMENTARY_SHEET", - "documentRef": "FA06", + "documentRef": "fa06", "version": 4, "features": [ { @@ -327,7 +327,7 @@ }, { "documentType": "TEMP_PASSPORT", - "documentRef": "F60D", + "documentRef": "f60d", "version": 4, "features": [ { @@ -336,21 +336,21 @@ "coding": "ByteArray", "required": true, "minLength": 1, - "maxLength": 1500 + "maxLength": 1000 }, { "name": "MRZ", "tag": 2, "coding": "C40", "required": true, - "minLength": 48, - "maxLength": 48 + "minLength": 60, + "maxLength": 60 } ] }, { "documentType": "TEMP_PERSO", - "documentRef": "F70B", + "documentRef": "f70b", "version": 4, "features": [ { @@ -359,39 +359,16 @@ "coding": "ByteArray", "required": true, "minLength": 1, - "maxLength": 1500 + "maxLength": 1000 }, { "name": "MRZ", "tag": 2, "coding": "C40", "required": true, - "minLength": 44, - "maxLength": 44 - } - ] - }, - { - "documentType": "PERMANENT_RESIDENCE_CERT", - "documentRef": "F48F", - "version": 4, - "features": [ - { - "name": "DOCUMENT_NUMBER", - "tag": 1, - "coding": "C40", - "required": true, - "minLength": 1, - "maxLength": 8 - }, - { - "name": "PASSPORT_NUMBER", - "tag": 2, - "coding": "C40", - "required": true, - "minLength": 1, - "maxLength": 8 + "minLength": 48, + "maxLength": 48 } ] } -] +] \ No newline at end of file