Skip to content

Commit

Permalink
changed getFeature method, prepared v0.6.0
Browse files Browse the repository at this point in the history
  • Loading branch information
tsenger committed Nov 2, 2024
1 parent 7019eeb commit 62a1d25
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 50 deletions.
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,19 @@ Here is a quick overview how to use the VDS parser and verifier.
When you have the decoded raw string or raw bytes from your favorite datamatrix decoder, just put them to the VDS Tools Dataparser like this:

```java
import de.tsenger.vdstools.DataParser;
import de.tsenger.vdstools.Verifier;
import de.tsenger.vdstools.seals.DigitalSeal;
import de.tsenger.vdstools.seals.VdsType;
import de.tsenger.vdstools.seals.Feature;
import de.tsenger.vdstools.vds.DigitalSeal;

...
DigitalSeal digitalSeal = DigitalSeal.fromByteArray(rawBytes);
String vdsType = digitalSeal.getVdsType()

// Depending on the returned VDS type you can access the seals content
String mrz = seal.getFeature("MRZ");
String azr = seal.getFeature("AZR");
// getFeature() returns an Optional<Feature> which can be used as follows
String mrz = digitalSeal.getFeature("MRZ").get().asString();
String azr = digitalSeal.getFeature("AZR").get().asString();
if (seal.getFeature("FACE_IMAGE").isPresent()) {
byte[] imgBytes = digitalSeal.getFeature("FACE_IMAGE").get().asByteArray();
}

// Get the VDS signer certificate reference
String signerCertRef = digitalSeal.getSignerCertRef();
Expand All @@ -43,7 +43,7 @@ import de.tsenger.vdstools.seals.Feature;

```

Also have a look at [DataParserTest.java](https://github.com/tsenger/vdstools/blob/main/src/test/java/de/tsenger/vdstools/DataParserTest.java) and [VerifierTest.java](https://github.com/tsenger/vdstools/blob/main/src/test/java/de/tsenger/vdstools/VerifierTest.java) for some more examples.
Also have a look at [DigitalSealTest.java](https://github.com/tsenger/vdstools/blob/main/src/test/java/de/tsenger/vdstools/vds/DigitalSealTest.java) and [VerifierTest.java](https://github.com/tsenger/vdstools/blob/main/src/test/java/de/tsenger/vdstools/VerifierTest.java) for some more examples.

## Build a new VDS
Since version 0.3.0 you can also generate VDS with this library. Here is an example on how to use the DateEncoder and Signer classes:
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<groupId>de.tsenger</groupId>
<artifactId>vdstools</artifactId>
<version>0.5.1</version>
<version>0.6.0</version>
<name>Visible Digital Seal Tools Java library</name>
<description>A Java library to encode/sign and decode/verify Visible Digital Seals</description>
<packaging>jar</packaging>
Expand Down
7 changes: 4 additions & 3 deletions src/main/java/de/tsenger/vdstools/vds/DigitalSeal.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;
Expand Down Expand Up @@ -107,11 +108,11 @@ public String getRawString() throws IOException {
return DataEncoder.encodeBase256(getEncoded());
}

public <T> Map<String, T> getFeatures() {
return vdsMessage.getFeatures();
public Map<String, Feature> getFeatureMap() {
return vdsMessage.getFeatureMap();
}

public <T> T getFeature(String feature) {
public Optional<Feature> getFeature(String feature) {
return vdsMessage.getFeature(feature);
}

Expand Down
33 changes: 33 additions & 0 deletions src/main/java/de/tsenger/vdstools/vds/Feature.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package de.tsenger.vdstools.vds;

import java.nio.charset.StandardCharsets;

public class Feature {
private final Object value;

public Feature(Object value) {
this.value = value;
}

public boolean isEmpty() {
return value == null;
}

public String asString() {
if (value instanceof String) {
return (String) value;
} else if (value instanceof byte[]) {
// Konvertiere byte[] in UTF-8-String
return new String((byte[]) value, StandardCharsets.UTF_8);
}
return null;
}

public byte[] asByteArray() {
return (byte[]) value;
}

public int asInteger() {
return ((byte[]) value)[0];
}
}
25 changes: 15 additions & 10 deletions src/main/java/de/tsenger/vdstools/vds/VdsMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.tinylog.Logger;

Expand Down Expand Up @@ -53,25 +54,29 @@ public List<DerTlv> getDerTlvList() {
return this.derTlvList;
}

public <T> Map<String, T> getFeatures() {
Map<String, T> featureMap = new HashMap<String, T>();
public Map<String, Feature> getFeatureMap() {
Map<String, Feature> featureMap = new HashMap<String, Feature>();
for (DerTlv derTlv : derTlvList) {
T value = DataEncoder.getFeatureEncoder().decodeFeature(vdsType, derTlv);
Object value = DataEncoder.getFeatureEncoder().decodeFeature(vdsType, derTlv);
String key = DataEncoder.getFeatureEncoder().getFeatureName(vdsType, derTlv);
featureMap.put(key, value);
if (value != null)
featureMap.put(key, new Feature(value));
}
return featureMap;
}

public <T> T getFeature(String feature) {
T value = null;
public Optional<Feature> getFeature(String feature) {
Object value = null;
byte tag = DataEncoder.getFeatureEncoder().getTag(vdsType, feature);
for (DerTlv derTlv : derTlvList) {
if (derTlv.getTag() == tag) {
value = DataEncoder.getFeatureEncoder().decodeFeature(vdsType, derTlv);
if (tag != 0) {
for (DerTlv derTlv : derTlvList) {
if (derTlv.getTag() == tag) {
value = DataEncoder.getFeatureEncoder().decodeFeature(vdsType, derTlv);
break;
}
}
}
return value;
return Optional.ofNullable(value != null ? new Feature(value) : null);
}

public static VdsMessage fromByteArray(byte[] rawBytes, String vdsType) {
Expand Down
72 changes: 44 additions & 28 deletions src/test/java/de/tsenger/vdstools/vds/DigitalSealTest.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package de.tsenger.vdstools.vds;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import java.io.FileInputStream;
Expand Down Expand Up @@ -47,89 +47,105 @@ public static void loadKeyStore() throws NoSuchAlgorithmException, CertificateEx
public void testParseSocialInsurranceCard() throws IOException {
DigitalSeal seal = DigitalSeal.fromByteArray(VdsRawBytes.socialInsurance);
assertEquals("SOCIAL_INSURANCE_CARD", seal.getVdsType());
assertEquals("65170839J003", seal.getFeature("SOCIAL_INSURANCE_NUMBER"));
assertEquals("Perschweiß", seal.getFeature("SURNAME"));
assertEquals("Oscar", seal.getFeature("FIRST_NAME"));
assertEquals("Jâcobénidicturius", seal.getFeature("BIRTH_NAME"));
assertEquals("65170839J003", seal.getFeature("SOCIAL_INSURANCE_NUMBER").get().asString());
assertEquals("Perschweiß", seal.getFeature("SURNAME").get().asString());
assertEquals("Oscar", seal.getFeature("FIRST_NAME").get().asString());
assertEquals("Jâcobénidicturius", seal.getFeature("BIRTH_NAME").get().asString());
}

@Test
public void testParseArrivalAttestationV02() throws IOException {
DigitalSeal seal = DigitalSeal.fromByteArray(VdsRawBytes.arrivalAttestationV02);
assertEquals("MED<<MANNSENS<<MANNY<<<<<<<<<<<<<<<<6525845096USA7008038M2201018<<<<<<06",
seal.getFeature("MRZ"));
assertEquals("ABC123456DEF", seal.getFeature("AZR"));
assertNull(seal.getFeature("FIRST_NAME"));
seal.getFeature("MRZ").get().asString());
assertEquals("ABC123456DEF", seal.getFeature("AZR").get().asString());
assertFalse(seal.getFeature("FIRST_NAME").isPresent());
}

@Test
public void testParseResidentPermit() throws IOException {
DigitalSeal seal = DigitalSeal.fromByteArray(VdsRawBytes.residentPermit);
assertEquals("ATD<<RESIDORCE<<ROLAND<<<<<<<<<<<<<<6525845096USA7008038M2201018<<<<<<06",
seal.getFeature("MRZ"));
assertEquals("UFO001979", seal.getFeature("PASSPORT_NUMBER"));
seal.getFeature("MRZ").get().asString());
assertEquals("UFO001979", seal.getFeature("PASSPORT_NUMBER").get().asString());
}

@Test
public void testParseSupplementSheet() throws IOException {
DigitalSeal seal = DigitalSeal.fromByteArray(VdsRawBytes.supplementSheet);
assertEquals("ATD<<RESIDORCE<<ROLAND<<<<<<<<<<<<<<6525845096USA7008038M2201018<<<<<<06",
seal.getFeature("MRZ"));
assertEquals("PA0000005", seal.getFeature("SHEET_NUMBER"));
seal.getFeature("MRZ").get().asString());
assertEquals("PA0000005", seal.getFeature("SHEET_NUMBER").get().asString());
}

@Test
public void testEmergencyTravelDoc() throws IOException {
DigitalSeal seal = DigitalSeal.fromByteArray(VdsRawBytes.emergenyTravelDoc);
assertEquals("I<GBRSUPAMANN<<MARY<<<<<<<<<<<<<<<<<6525845096USA7008038M2201018<<<<<<06",
seal.getFeature("MRZ"));
seal.getFeature("MRZ").get().asString());
}

@Test
public void testParseAddressStickerId() throws IOException {
DigitalSeal seal = DigitalSeal.fromByteArray(VdsRawBytes.addressStickerId);
assertEquals("T2000AK47", seal.getFeature("DOCUMENT_NUMBER"));
assertEquals("05314000", seal.getFeature("AGS"));
assertEquals("53175HEINEMANNSTR11", seal.getFeature("ADDRESS"));
assertEquals("T2000AK47", seal.getFeature("DOCUMENT_NUMBER").get().asString());
assertEquals("05314000", seal.getFeature("AGS").get().asString());
assertEquals("53175HEINEMANNSTR11", seal.getFeature("ADDRESS").get().asString());
}

@Test
public void testParseAddressStickerPassport() throws IOException {
DigitalSeal seal = DigitalSeal.fromByteArray(VdsRawBytes.addressStickerPassport);
assertEquals("PA5500K11", seal.getFeature("DOCUMENT_NUMBER"));
assertEquals("03359010", seal.getFeature("AGS"));
assertEquals("21614", seal.getFeature("POSTAL_CODE"));
assertEquals("PA5500K11", seal.getFeature("DOCUMENT_NUMBER").get().asString());
assertEquals("03359010", seal.getFeature("AGS").get().asString());
assertEquals("21614", seal.getFeature("POSTAL_CODE").get().asString());
}

@Test
public void testParseVisa() throws IOException {
DigitalSeal seal = DigitalSeal.fromByteArray(VdsRawBytes.visa_224bitSig);
assertEquals("VCD<<DENT<<ARTHUR<PHILIP<<<<<<<<<<<<1234567XY7GBR5203116M2005250", seal.getFeature("MRZ_MRVB"));
assertEquals("47110815P", seal.getFeature("PASSPORT_NUMBER"));
assertEquals("a00000", Hex.toHexString((byte[]) seal.getFeature("DURATION_OF_STAY")));
assertEquals("VCD<<DENT<<ARTHUR<PHILIP<<<<<<<<<<<<1234567XY7GBR5203116M2005250", seal.getFeature("MRZ_MRVB").get().asString());
assertEquals("47110815P", seal.getFeature("PASSPORT_NUMBER").get().asString());
assertEquals("a00000", Hex.toHexString(seal.getFeature("DURATION_OF_STAY").get().asByteArray()));
assertTrue(seal.getFeature("NUMBER_OF_ENTRIES").isEmpty());
}

@Test
public void testParseFictionCert() throws IOException {
DigitalSeal seal = DigitalSeal.fromByteArray(VdsRawBytes.fictionCert);
assertEquals("NFD<<MUSTERMANN<<CLEOPATRE<<<<<<<<<<L000000007TUR8308126F2701312T2611011",
seal.getFeature("MRZ"));
assertEquals("X98723021", seal.getFeature("PASSPORT_NUMBER"));
assertEquals("160113000085", seal.getFeature("AZR"));
seal.getFeature("MRZ").get().asString());
assertEquals("X98723021", seal.getFeature("PASSPORT_NUMBER").get().asString());
assertEquals("160113000085", seal.getFeature("AZR").get().asString());
}

@Test
public void testParseTempPerso() throws IOException {
DigitalSeal seal = DigitalSeal.fromByteArray(VdsRawBytes.tempPerso);
assertEquals("ITD<<MUSTERMANN<<ERIKA<<<<<<<<<<<<<<D000000001D<<8308126<2701312<<<<<<<0",
seal.getFeature("MRZ"));
seal.getFeature("MRZ").get().asString());
byte[] imgBytes = null;
if (seal.getFeature("FACE_IMAGE").isPresent()) {
imgBytes = seal.getFeature("FACE_IMAGE").get().asByteArray();
}
assertEquals(891, imgBytes.length);
}

@Test
public void testParseTempPassport() throws IOException {
DigitalSeal seal = DigitalSeal.fromByteArray(VdsRawBytes.tempPassport);
assertEquals("PPD<<MUSTERMANN<<ERIKA<<<<<<<<<<<<<<<<<<<<<<A000000000D<<8308126<2710316<<<<<<<<<<<<<<<8",
seal.getFeature("MRZ"));
seal.getFeature("MRZ").get().asString());
}

@Test
public void testGetFeatureMap() throws IOException {
DigitalSeal seal = DigitalSeal.fromByteArray(VdsRawBytes.fictionCert);
assertEquals("NFD<<MUSTERMANN<<CLEOPATRE<<<<<<<<<<L000000007TUR8308126F2701312T2611011",
seal.getFeature("MRZ").get().asString());
assertEquals(4, seal.getFeatureMap().size());
assertTrue(seal.getFeatureMap().containsKey("AZR"));
assertFalse(seal.getFeatureMap().containsKey("DURATION_OF_STAY"));
}

@Test
Expand Down Expand Up @@ -180,7 +196,7 @@ public void testgetRawString2() throws IOException {

@Test
public void testBuildDigitalSeal() throws IOException, KeyStoreException {
String mrz = "ATD<<RESIDORCE<<ROLAND<<<<<<<<<<<<<<" + "6525845096USA7008038M2201018<<<<<<06";
String mrz = "ATD<<RESIDORCE<<ROLAND<<<<<<<<<<<<<<6525845096USA7008038M2201018<<<<<<06";
String passportNumber = "UFO001979";
VdsMessage vdsMessage = new VdsMessage.Builder("RESIDENCE_PERMIT")
.addDocumentFeature("MRZ", mrz)
Expand Down
23 changes: 23 additions & 0 deletions src/test/resources/SealCodings.json
Original file line number Diff line number Diff line change
Expand Up @@ -370,5 +370,28 @@
"maxLength": 48
}
]
},
{
"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
}
]
}
]

0 comments on commit 62a1d25

Please sign in to comment.