From f009e74ce314dfee5a73ea72005ea1276d13bb77 Mon Sep 17 00:00:00 2001 From: Tobias Senger Date: Sun, 17 Nov 2024 21:13:47 +0100 Subject: [PATCH] return formated MRZ with new line at correct position --- .idea/.gitignore | 3 ++ .idea/compiler.xml | 13 +++++ .idea/encodings.xml | 7 +++ .idea/jarRepositories.xml | 20 +++++++ .idea/misc.xml | 12 +++++ .idea/vcs.xml | 6 +++ pom.xml | 6 ++- .../de/tsenger/vdstools/FeatureConverter.java | 52 +++++++++++-------- .../de/tsenger/vdstools/vds/VdsMessage.java | 20 +++---- .../tsenger/vdstools/vds/dto/FeaturesDto.java | 1 + src/main/resources/SealCodings.json | 24 +++++++++ .../vdstools/FeatureConverterTest.java | 31 ++++++----- .../tsenger/vdstools/vds/DigitalSealTest.java | 20 +++---- src/test/resources/SealCodings.json | 24 +++++++++ 14 files changed, 180 insertions(+), 59 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/compiler.xml create mode 100644 .idea/encodings.xml create mode 100644 .idea/jarRepositories.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..25ec4fd --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..aa00ffa --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..712ab9d --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..f5eeb1d --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 656ff5d..dc67ad5 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ de.tsenger vdstools - 0.6.0 + 0.6.1 Visible Digital Seal Tools Java library A Java library to encode/sign and decode/verify Visible Digital Seals jar @@ -11,7 +11,7 @@ tsenger.de - https://tsenger.de + https://www.tsenger.de @@ -45,6 +45,8 @@ 3.13.0 true + 11 + 11 diff --git a/src/main/java/de/tsenger/vdstools/FeatureConverter.java b/src/main/java/de/tsenger/vdstools/FeatureConverter.java index 9b18c43..b580f82 100644 --- a/src/main/java/de/tsenger/vdstools/FeatureConverter.java +++ b/src/main/java/de/tsenger/vdstools/FeatureConverter.java @@ -1,24 +1,17 @@ package de.tsenger.vdstools; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import de.tsenger.vdstools.vds.dto.FeaturesDto; +import de.tsenger.vdstools.vds.dto.SealDto; +import org.tinylog.Logger; + import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; - -import org.tinylog.Logger; - -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; - -import de.tsenger.vdstools.vds.dto.FeaturesDto; -import de.tsenger.vdstools.vds.dto.SealDto; +import java.util.*; public class FeatureConverter { @@ -56,10 +49,11 @@ public FeatureConverter(InputStream is) { } public Set getAvailableVdsTypes() { - return new TreeSet(vdsTypes.keySet()); + return new TreeSet<>(vdsTypes.keySet()); } public int getDocumentRef(String vdsType) { + if (vdsTypes.get(vdsType) == null) throw new IllegalArgumentException("Could find seal type "+vdsType+" in "+DEFAULT_SEAL_CODINGS); return vdsTypes.get(vdsType); } @@ -83,6 +77,22 @@ public String getFeatureName(String vdsType, DerTlv derTlv) { return getFeatureName(sealDto, derTlv.getTag()); } + public int getFeatureLength(String vdsType, byte tag) { + if (!vdsTypes.containsKey(vdsType)) { + Logger.warn("No seal type with name '" + vdsType + "' was found."); + return -1; + } + SealDto sealDto = getSealDto(vdsType); + if (sealDto == null) { + return -1; + } + FeaturesDto featureDto = getFeatureDto(sealDto, tag); + if(featureDto == null) { + return -1; + } + return featureDto.decodedLength; + } + public byte getTag(String vdsType, String feature) { if (!vdsTypes.containsKey(vdsType)) { Logger.warn("No VdsSeal type with name '" + vdsType + "' was found."); @@ -132,7 +142,7 @@ private DerTlv encodeFeature(SealDto sealDto, String feature, T inputValue) throw new IllegalArgumentException("VdsType: " + sealDto.documentType + " has no Feature " + feature); } String coding = getCoding(sealDto, feature); - byte[] value = null; + byte[] value; switch (coding) { case "C40": String valueStr = ((String) inputValue).replaceAll("\r", "").replaceAll("\n", ""); @@ -142,12 +152,8 @@ private DerTlv encodeFeature(SealDto sealDto, String feature, T inputValue) value = (byte[]) inputValue; break; case "Utf8String": - try { - value = ((String) inputValue).getBytes("UTF-8"); - } catch (UnsupportedEncodingException e) { - Logger.error("Couldn't encode String " + (String) inputValue + " to UTF-8 bytes: " + e.getMessage()); - } - break; + value = ((String) inputValue).getBytes(StandardCharsets.UTF_8); + break; default: value = (byte[]) inputValue; } @@ -162,7 +168,7 @@ private T decodeFeature(SealDto sealDto, DerTlv derTlv) { case "C40": String featureValue = DataParser.decodeC40(derTlv.getValue()); String featureName = getFeatureName(sealDto, tag); - if (featureName.startsWith("MRZ")) { + if (featureName.startsWith("MRZ")) { featureValue = featureValue.replace(' ', '<'); } return (T) featureValue; diff --git a/src/main/java/de/tsenger/vdstools/vds/VdsMessage.java b/src/main/java/de/tsenger/vdstools/vds/VdsMessage.java index ad3e478..f208919 100644 --- a/src/main/java/de/tsenger/vdstools/vds/VdsMessage.java +++ b/src/main/java/de/tsenger/vdstools/vds/VdsMessage.java @@ -1,18 +1,13 @@ package de.tsenger.vdstools.vds; -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 java.util.Optional; - -import org.tinylog.Logger; - import de.tsenger.vdstools.DataEncoder; import de.tsenger.vdstools.DataParser; import de.tsenger.vdstools.DerTlv; +import org.tinylog.Logger; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.*; public class VdsMessage { @@ -76,6 +71,11 @@ public Optional getFeature(String feature) { } } } + if (value!=null && (feature.equals("MRZ") || feature.equals("MRZ_MRVA") || feature.equals("MRZ_MRVB"))) { + int mrzLength = DataEncoder.getFeatureEncoder().getFeatureLength(vdsType, tag); + String newMrz = String.format("%1$-"+mrzLength+"s", value).replace(' ', '<'); + value = newMrz.substring(0, mrzLength / 2) + "\n" + newMrz.substring(mrzLength / 2); + } return Optional.ofNullable(value != null ? new Feature(value) : null); } diff --git a/src/main/java/de/tsenger/vdstools/vds/dto/FeaturesDto.java b/src/main/java/de/tsenger/vdstools/vds/dto/FeaturesDto.java index 494ee22..de183d3 100644 --- a/src/main/java/de/tsenger/vdstools/vds/dto/FeaturesDto.java +++ b/src/main/java/de/tsenger/vdstools/vds/dto/FeaturesDto.java @@ -4,6 +4,7 @@ public class FeaturesDto { public String name; public int tag; public String coding; + public int decodedLength; public boolean required; public int minLength; public int maxLength; diff --git a/src/main/resources/SealCodings.json b/src/main/resources/SealCodings.json index b53c5c7..725ce4d 100644 --- a/src/main/resources/SealCodings.json +++ b/src/main/resources/SealCodings.json @@ -8,6 +8,7 @@ "name": "MRZ", "tag": 2, "coding": "C40", + "decodedLength": 72, "required": true, "minLength": 48, "maxLength": 48 @@ -16,6 +17,7 @@ "name": "AZR", "tag": 3, "coding": "C40", + "decodedLength": 12, "required": true, "minLength": 8, "maxLength": 8 @@ -31,6 +33,7 @@ "name": "DOCUMENT_NUMBER", "tag": 1, "coding": "C40", + "decodedLength": 9, "required": true, "minLength": 6, "maxLength": 6 @@ -39,6 +42,7 @@ "name": "AGS", "tag": 2, "coding": "C40", + "decodedLength": 9, "required": true, "minLength": 6, "maxLength": 6 @@ -62,6 +66,7 @@ "name": "DOCUMENT_NUMBER", "tag": 1, "coding": "C40", + "decodedLength": 9, "required": true, "minLength": 6, "maxLength": 6 @@ -70,6 +75,7 @@ "name": "AGS", "tag": 2, "coding": "C40", + "decodedLength": 9, "required": true, "minLength": 6, "maxLength": 6 @@ -78,6 +84,7 @@ "name": "POSTAL_CODE", "tag": 3, "coding": "C40", + "decodedLength": 5, "required": true, "minLength": 4, "maxLength": 4 @@ -101,6 +108,7 @@ "name": "MRZ", "tag": 2, "coding": "C40", + "decodedLength": 72, "required": true, "minLength": 48, "maxLength": 48 @@ -109,6 +117,7 @@ "name": "PASSPORT_NUMBER", "tag": 3, "coding": "C40", + "decodedLength": 9, "required": true, "minLength": 6, "maxLength": 6 @@ -117,6 +126,7 @@ "name": "AZR", "tag": 4, "coding": "C40", + "decodedLength": 12, "required": true, "minLength": 8, "maxLength": 8 @@ -140,6 +150,7 @@ "name": "MRZ", "tag": 2, "coding": "C40", + "decodedLength": 72, "required": true, "minLength": 48, "maxLength": 48 @@ -148,6 +159,7 @@ "name": "PASSPORT_NUMBER", "tag": 3, "coding": "C40", + "decodedLength": 9, "required": true, "minLength": 6, "maxLength": 6 @@ -156,6 +168,7 @@ "name": "AZR", "tag": 4, "coding": "C40", + "decodedLength": 12, "required": true, "minLength": 8, "maxLength": 8 @@ -171,6 +184,7 @@ "name": "MRZ", "tag": 2, "coding": "C40", + "decodedLength": 72, "required": true, "minLength": 48, "maxLength": 48 @@ -186,6 +200,7 @@ "name": "MRZ_MRVA", "tag": 1, "coding": "C40", + "decodedLength": 88, "required": true, "minLength": 48, "maxLength": 48 @@ -195,6 +210,7 @@ "tag": 2, "coding": "C40", "required": true, + "decodedLength": 72, "minLength": 44, "maxLength": 44 }, @@ -218,6 +234,7 @@ "name": "PASSPORT_NUMBER", "tag": 5, "coding": "C40", + "decodedLength": 9, "required": true, "minLength": 6, "maxLength": 6 @@ -249,6 +266,7 @@ "name": "MRZ", "tag": 2, "coding": "C40", + "decodedLength": 72, "required": true, "minLength": 48, "maxLength": 48 @@ -257,6 +275,7 @@ "name": "PASSPORT_NUMBER", "tag": 3, "coding": "C40", + "decodedLength": 9, "required": true, "minLength": 6, "maxLength": 6 @@ -272,6 +291,7 @@ "name": "SOCIAL_INSURANCE_NUMBER", "tag": 1, "coding": "C40", + "decodedLength": 12, "required": true, "minLength": 8, "maxLength": 8 @@ -311,6 +331,7 @@ "name": "MRZ", "tag": 4, "coding": "C40", + "decodedLength": 72, "required": true, "minLength": 48, "maxLength": 48 @@ -319,6 +340,7 @@ "name": "SHEET_NUMBER", "tag": 5, "coding": "C40", + "decodedLength": 9, "required": true, "minLength": 6, "maxLength": 6 @@ -342,6 +364,7 @@ "name": "MRZ", "tag": 2, "coding": "C40", + "decodedLength": 88, "required": true, "minLength": 60, "maxLength": 60 @@ -365,6 +388,7 @@ "name": "MRZ", "tag": 2, "coding": "C40", + "decodedLength": 72, "required": true, "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 f9ef312..19f84a9 100644 --- a/src/test/java/de/tsenger/vdstools/FeatureConverterTest.java +++ b/src/test/java/de/tsenger/vdstools/FeatureConverterTest.java @@ -1,37 +1,35 @@ package de.tsenger.vdstools; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import org.bouncycastle.util.encoders.Hex; +import org.junit.Test; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; -import org.bouncycastle.util.encoders.Hex; -import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; public class FeatureConverterTest { @Test - public void testFeatureConverter() throws FileNotFoundException { - assertNotNull(new FeatureConverter()); + public void testFeatureConverter() { + new FeatureConverter(); } @Test public void testFeatureConverterString() throws FileNotFoundException { File fe = new File("src/test/resources/SealCodings.json"); FileInputStream fis = new FileInputStream(fe); - assertNotNull(new FeatureConverter(fis)); + new FeatureConverter(fis); } @Test(expected = FileNotFoundException.class) public void testFeatureConverterString_notFound() throws FileNotFoundException { File fe = new File("src/test/resources/Codings.json"); FileInputStream fis = new FileInputStream(fe); - assertNull(new FeatureConverter(fis)); + new FeatureConverter(fis); } @Test @@ -43,7 +41,7 @@ public void testGetFeature_String() throws IOException { } @Test - public void testGetTag_String() throws FileNotFoundException { + public void testGetTag_String() { FeatureConverter featureConverter = new FeatureConverter(); byte tag = featureConverter.getTag("ALIENS_LAW", "AZR"); assertEquals(4, tag); @@ -60,7 +58,7 @@ public void testDecodeFeature_String() throws IOException { @Test public void testEncodeFeature_String() throws IOException { FeatureConverter featureConverter = new FeatureConverter(); - String mrz = "ATD<