summaryrefslogtreecommitdiffstats
path: root/src/org/uic/barcode
diff options
context:
space:
mode:
authorCGantert345 <57003061+CGantert345@users.noreply.github.com>2020-07-28 17:57:25 +0200
committerCGantert345 <57003061+CGantert345@users.noreply.github.com>2020-07-28 17:57:25 +0200
commitb17ce13cc31abd088088bf8ab26cc924a6a36585 (patch)
tree5588e1ccc212213097500f4cb43fa0e3e68e323b /src/org/uic/barcode
parentDraft of the new DOSIPAS included (diff)
downloadUIC-barcode-b17ce13cc31abd088088bf8ab26cc924a6a36585.tar
UIC-barcode-b17ce13cc31abd088088bf8ab26cc924a6a36585.tar.gz
UIC-barcode-b17ce13cc31abd088088bf8ab26cc924a6a36585.tar.bz2
UIC-barcode-b17ce13cc31abd088088bf8ab26cc924a6a36585.tar.lz
UIC-barcode-b17ce13cc31abd088088bf8ab26cc924a6a36585.tar.xz
UIC-barcode-b17ce13cc31abd088088bf8ab26cc924a6a36585.tar.zst
UIC-barcode-b17ce13cc31abd088088bf8ab26cc924a6a36585.zip
Diffstat (limited to 'src/org/uic/barcode')
-rw-r--r--src/org/uic/barcode/Decoder.java232
-rw-r--r--src/org/uic/barcode/Encoder.java259
-rw-r--r--src/org/uic/barcode/dynamicFrame/Constants.java38
-rw-r--r--src/org/uic/barcode/dynamicFrame/DataType.java95
-rw-r--r--src/org/uic/barcode/dynamicFrame/DynamicFrame.java263
-rw-r--r--src/org/uic/barcode/dynamicFrame/Level1DataType.java218
-rw-r--r--src/org/uic/barcode/dynamicFrame/Level2DataType.java98
-rw-r--r--src/org/uic/barcode/dynamicFrame/SequenceOfDataType.java26
-rw-r--r--src/org/uic/barcode/dynamicFrame/headerSpec.asn119
-rw-r--r--src/org/uic/barcode/dynamicFrame/package.html9
-rw-r--r--src/org/uic/barcode/package.html21
-rw-r--r--src/org/uic/barcode/staticFrame/DataRecord.java199
-rw-r--r--src/org/uic/barcode/staticFrame/GENERICDataRecord.java62
-rw-r--r--src/org/uic/barcode/staticFrame/StaticFrame.java762
-rw-r--r--src/org/uic/barcode/staticFrame/UFLEXDataRecord.java90
-rw-r--r--src/org/uic/barcode/staticFrame/UHEADDataRecord.java269
-rw-r--r--src/org/uic/barcode/staticFrame/UTLAYDataRecord.java272
-rw-r--r--src/org/uic/barcode/staticFrame/package.html21
-rw-r--r--src/org/uic/barcode/staticHeader/ticketLayoutBarcode/FormatType.java34
-rw-r--r--src/org/uic/barcode/staticHeader/ticketLayoutBarcode/LayoutElement.java50
-rw-r--r--src/org/uic/barcode/staticHeader/ticketLayoutBarcode/TicketLayout.java61
-rw-r--r--src/org/uic/barcode/test/DynamicFrameDoubleSignatureTest.java212
-rw-r--r--src/org/uic/barcode/test/DynamicFrameLOwLevelTest.java116
-rw-r--r--src/org/uic/barcode/test/DynamicFrameSimpleTest.java171
-rw-r--r--src/org/uic/barcode/test/SignatureSplitTest.java76
-rw-r--r--src/org/uic/barcode/test/StaticFrameBarcodeTest.java193
-rw-r--r--src/org/uic/barcode/test/TicketLayoutTest.java45
-rw-r--r--src/org/uic/barcode/test/utils/Level2TestDataFactory.java20
-rw-r--r--src/org/uic/barcode/test/utils/SimpleDynamicFrameTestBarcode.java80
-rw-r--r--src/org/uic/barcode/test/utils/SimpleTestTicketLayout.java33
-rw-r--r--src/org/uic/barcode/test/utils/SimpleUICTestTicket.java254
-rw-r--r--src/org/uic/barcode/test/utils/TestUtils.java38
-rw-r--r--src/org/uic/barcode/utils/AlgorithmNameResolver.java173
-rw-r--r--src/org/uic/barcode/utils/package.html20
34 files changed, 4629 insertions, 0 deletions
diff --git a/src/org/uic/barcode/Decoder.java b/src/org/uic/barcode/Decoder.java
new file mode 100644
index 0000000..2ce417c
--- /dev/null
+++ b/src/org/uic/barcode/Decoder.java
@@ -0,0 +1,232 @@
+package org.uic.barcode;
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.util.zip.DataFormatException;
+
+import org.uic.barcode.dynamicFrame.Constants;
+import org.uic.barcode.dynamicFrame.DataType;
+import org.uic.barcode.dynamicFrame.DynamicFrame;
+import org.uic.barcode.dynamicFrame.Level1DataType;
+import org.uic.barcode.dynamicFrame.Level2DataType;
+import org.uic.barcode.dynamicFrame.SequenceOfDataType;
+import org.uic.barcode.staticFrame.StaticFrame;
+import org.uic.barcode.staticFrame.UFLEXDataRecord;
+import org.uic.barcode.staticFrame.UTLAYDataRecord;
+import org.uic.barcode.staticHeader.ticketLayoutBarcode.TicketLayout;
+import org.uic.ticket.EncodingFormatException;
+import org.uic.ticket.UicRailTicketCoder;
+import org.uic.ticket.api.spec.IUicRailTicket;
+
+
+/**
+ * The Class Decoder.
+ *
+ * signature validation and decoding of UIC bar codes
+ *
+ */
+public class Decoder {
+
+
+ /** The dynamic frame. */
+ private DynamicFrame dynamicFrame = null;
+
+ /** The static frame. */
+ private StaticFrame staticFrame = null;
+
+ /** The uic ticket coder. */
+ private UicRailTicketCoder uicTicketCoder = null;
+
+ /** The uic ticket. */
+ private IUicRailTicket uicTicket = null;
+
+ /** The layout. */
+ private TicketLayout layout = null;
+
+ /** The data. */
+ byte[] data = null;
+
+
+ /**
+ * Instantiates a new decoder.
+ *
+ * @param data the data
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ * @throws DataFormatException the data format exception
+ */
+ public Decoder (byte[] data) throws IOException, EncodingFormatException, DataFormatException {
+ this.data = data;
+ decode(data);
+ }
+
+ /**
+ * Validate level 1.
+ *
+ * @param key the public key
+ * @param signingAlg the signing algorithm OID
+ * @return the return code indicating errors
+ * @throws InvalidKeyException the invalid key exception
+ * @throws NoSuchAlgorithmException the no such algorithm exception
+ * @throws SignatureException the signature exception
+ * @throws IllegalArgumentException the illegal argument exception
+ * @throws UnsupportedOperationException the unsupported operation exception
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ public int validateLevel1(PublicKey key, String signingAlg) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IllegalArgumentException, UnsupportedOperationException, IOException, EncodingFormatException {
+ if (!isStaticHeader(data)) {
+ return dynamicFrame.validateLevel1(key) ;
+ } else {
+ if (staticFrame.verifyByAlgorithmOid(key,signingAlg)) {
+ return Constants.LEVEL1_VALIDATION_OK;
+ } else {
+ return Constants.LEVEL1_VALIDATION_FRAUD;
+ }
+ }
+ }
+
+ /**
+ * Validate level 2.
+ *
+ * @return the return code indicating errors
+ */
+ public int validateLevel2() {
+ if (!isStaticHeader(data)) {
+ return dynamicFrame.validateLevel2() ;
+ } else {
+ return Constants.LEVEL2_VALIDATION_NO_SIGNATURE;
+ }
+ }
+
+ /**
+ * Decode.
+ *
+ * @param data the byte array raw data
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ * @throws DataFormatException the data format exception
+ */
+ public void decode(byte[] data) throws IOException, EncodingFormatException, DataFormatException {
+
+ if (!isStaticHeader(data)) {
+
+ dynamicFrame = DynamicFrame.decode(data);
+
+ Level2DataType level2 = dynamicFrame.getLevel2SignedData();
+
+ Level1DataType level1 = level2.getLevel1Data();
+
+ SequenceOfDataType dataList = level1.getData();
+
+ for (DataType level1Content : dataList) {
+
+ uicTicketCoder = new UicRailTicketCoder();
+ if (level1Content.getFormat().equals("FCB1")) {
+ uicTicket = uicTicketCoder.decodeFromAsn(level1Content.getByteData(), 1);
+ } else if (level1Content.getFormat().equals("FCB2")) {
+ uicTicket = uicTicketCoder.decodeFromAsn(level1Content.getByteData(), 2);
+ }
+ }
+
+ } else if (isStaticHeader(data)){
+
+ staticFrame = new StaticFrame();
+
+ staticFrame.decode(data);
+
+ UFLEXDataRecord flex = staticFrame.getuFlex();
+
+ if (flex != null) {
+ uicTicket = flex.getTicket();
+ }
+
+ UTLAYDataRecord tlay = staticFrame.getuTlay();
+
+ if (tlay != null) {
+ layout = tlay.getLayout();
+ }
+ }
+ }
+
+
+ /**
+ * Checks if is static header.
+ *
+ * @param data the data
+ * @return true, if is static header
+ */
+ private boolean isStaticHeader(byte[] data) {
+ byte[] start = "#UT".getBytes();
+ if (start[0] != data[0] || start[1]!= start[1] || start[2] != data[2]) return false;
+ return true;
+ }
+
+ /**
+ * Gets the uic ticket.
+ *
+ * @return the uic ticket
+ */
+ public IUicRailTicket getUicTicket() {
+ return uicTicket;
+ }
+
+ /**
+ * Gets the layout.
+ *
+ * @return the layout
+ */
+ public TicketLayout getLayout() {
+ return layout;
+ }
+
+ /**
+ * Gets the dynamic header.
+ *
+ * @return the dynamic header
+ */
+ public DynamicFrame getDynamicHeader() {
+ return dynamicFrame;
+ }
+
+ /**
+ * Sets the dynamic header.
+ *
+ * @param dynamicHeader the new dynamic header
+ */
+ public void setDynamicHeader(DynamicFrame dynamicHeader) {
+ this.dynamicFrame = dynamicHeader;
+ }
+
+ /**
+ * Gets the static frame.
+ *
+ * @return the static frame
+ */
+ public StaticFrame getStaticFrame() {
+ return staticFrame;
+ }
+
+ /**
+ * Sets the static frame.
+ *
+ * @param staticFrame the new static frame
+ */
+ public void setStaticFrame(StaticFrame staticFrame) {
+ this.staticFrame = staticFrame;
+ }
+
+ public DataType getLevel2Data() {
+ if (!isStaticHeader(data) && dynamicFrame.getLevel2SignedData() != null) {
+ return dynamicFrame.getLevel2SignedData().getLevel2Data();
+ }
+ return null;
+ }
+
+
+
+
+}
diff --git a/src/org/uic/barcode/Encoder.java b/src/org/uic/barcode/Encoder.java
new file mode 100644
index 0000000..301b7c5
--- /dev/null
+++ b/src/org/uic/barcode/Encoder.java
@@ -0,0 +1,259 @@
+package org.uic.barcode;
+
+import java.io.IOException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+
+import org.uic.barcode.dynamicFrame.Constants;
+import org.uic.barcode.dynamicFrame.DataType;
+import org.uic.barcode.dynamicFrame.DynamicFrame;
+import org.uic.barcode.dynamicFrame.Level1DataType;
+import org.uic.barcode.dynamicFrame.Level2DataType;
+import org.uic.barcode.dynamicFrame.SequenceOfDataType;
+import org.uic.barcode.staticFrame.StaticFrame;
+import org.uic.barcode.staticFrame.UFLEXDataRecord;
+import org.uic.barcode.staticFrame.UHEADDataRecord;
+import org.uic.barcode.staticFrame.UTLAYDataRecord;
+import org.uic.barcode.staticHeader.ticketLayoutBarcode.TicketLayout;
+import org.uic.ticket.EncodingFormatException;
+import org.uic.ticket.UicRailTicketCoder;
+import org.uic.ticket.api.spec.IUicRailTicket;
+
+import net.gcdc.asn1.datatypesimpl.OctetString;
+
+
+/**
+ * The Class Encoder.
+ *
+ * signing and encoding of UIC bar codes
+ *
+ *
+ */
+public class Encoder {
+
+ /** The dynamic frame. */
+ private DynamicFrame dynamicFrame = null;
+
+ /** The static frame. */
+ private StaticFrame staticFrame = null;
+
+
+ /** The UIC bar code type classic. */
+ public static String UIC_BARCODE_TYPE_CLASSIC = "UIC_CLASSIC";
+
+ /** The UIC bar code type DOSIPAS. */
+ public static String UIC_BARCODE_TYPE_DOSIPAS = "UIC_DOSIPAS";
+
+ /**
+ * Instantiates a new encoder.
+ *
+ * @param ticket the ticket
+ * @param layout the layout
+ * @param barcodeType the bar code type
+ * @param version the version of the bar code
+ * @param fcbVersion the fcb version
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ public Encoder(IUicRailTicket ticket, TicketLayout layout, String barcodeType, int version, int fcbVersion) throws IOException, EncodingFormatException {
+
+ if (barcodeType == UIC_BARCODE_TYPE_CLASSIC) {
+
+ staticFrame = new StaticFrame();
+ staticFrame.setVersion(version);
+
+ if (layout != null) {
+
+ UHEADDataRecord head = new UHEADDataRecord();
+ head.setVersionId("01");
+ staticFrame.setHeaderRecord(head);
+
+ UTLAYDataRecord tlay = new UTLAYDataRecord();
+ tlay.setLayout(layout);
+ tlay.setVersionId("01");
+ staticFrame.setuTlay(tlay);
+ }
+
+ if (ticket != null) {
+
+ UFLEXDataRecord flex = new UFLEXDataRecord();
+ flex.setTicket(ticket);
+ flex.setVersionId(String.format("%02d",fcbVersion));
+ staticFrame.setuFlex(flex);
+ }
+
+
+ } else if (barcodeType == UIC_BARCODE_TYPE_DOSIPAS) {
+
+ dynamicFrame = new DynamicFrame();
+ dynamicFrame.setLevel2SignedData(new Level2DataType());
+ dynamicFrame.getLevel2SignedData().setLevel1Data(new Level1DataType());
+ dynamicFrame.getLevel2SignedData().getLevel1Data().setData(new SequenceOfDataType());
+
+ if (ticket != null) {
+
+ if (version == 1) {
+ dynamicFrame.setFormat("U1");
+ }
+
+ DataType ticketData = new DataType();
+
+ UicRailTicketCoder uicTicketCoder = new UicRailTicketCoder();
+ ticketData.setFormat(Constants.DATA_TYPE_FCB_VERSION_1);
+ ticketData.setData(new OctetString(uicTicketCoder.encode(ticket, fcbVersion)));
+ dynamicFrame.getLevel2SignedData().getLevel1Data().getData().add(ticketData);
+
+ }
+ }
+ }
+
+
+
+ /**
+ * Signing level 2 of a dynamic bar code
+ *
+ * @param key the key
+ * @throws Exception the exception
+ */
+ public void signLevel2(PrivateKey key) throws Exception {
+ if (dynamicFrame != null) {
+ dynamicFrame.signLevel2(key);
+ }
+ }
+
+ /**
+ * Sets the level 1 algorithm Is.
+ *
+ * @param level1SigningAlg the level 1 signing algorithm (OID)
+ * @param level1KeyAlg the level 1 key algorithm (OID)
+ */
+ public void setLevel1Algs(String level1SigningAlg, String level1KeyAlg) {
+ if (dynamicFrame != null) {
+ dynamicFrame.getLevel2SignedData().getLevel1Data().setLevel1SigningAlg(level1SigningAlg);
+ dynamicFrame.getLevel2SignedData().getLevel1Data().setLevel1KeyAlg(level1KeyAlg);
+ }
+ }
+
+ /**
+ * Sets the level 2 algorithm Is.
+ *
+ * @param level2SigningAlg the level 2 signing algorithm (OID)
+ * @param level2KeyAlg the level 2 key algorithm (OID)
+ * @param publicKey the public key of the level 2 signature
+ */
+ public void setLevel2Algs(String level2SigningAlg, String level2KeyAlg, PublicKey publicKey) {
+ if (dynamicFrame != null) {
+ if (dynamicFrame.getLevel2SignedData() == null) {
+ dynamicFrame.setLevel2SignedData(new Level2DataType());
+ }
+ if (dynamicFrame.getLevel2SignedData().getLevel1Data() == null) {
+ dynamicFrame.getLevel2SignedData().setLevel1Data(new Level1DataType());
+ }
+ dynamicFrame.getLevel2SignedData().getLevel1Data().setLevel2SigningAlg(level2SigningAlg);
+ dynamicFrame.getLevel2SignedData().getLevel1Data().setLevel2KeyAlg(level2KeyAlg);
+ if (publicKey != null) {
+ dynamicFrame.getLevel2SignedData().getLevel1Data().setLevel2publicKey(new OctetString(publicKey.getEncoded()));
+ }
+ }
+ }
+
+ public void setLevel2Data(DataType level2data) {
+ if (dynamicFrame != null) {
+ if (dynamicFrame.getLevel2SignedData() == null) {
+ dynamicFrame.setLevel2SignedData(new Level2DataType());
+ }
+ dynamicFrame.getLevel2SignedData().setLevel2Data(level2data);
+ }
+ }
+
+ public DataType getLevel2Data() {
+ if (dynamicFrame != null && dynamicFrame.getLevel2SignedData() != null) {
+ return dynamicFrame.getLevel2SignedData().getLevel2Data();
+ }
+ return null;
+ }
+
+ /**
+ * Sign level 1 of a dynamic bar code or a static bar code.
+ *
+ * @param securityProvider the security provider
+ * @param key the key
+ * @param signingAlg the signing algorithm (OID)
+ * @param keyId the key id
+ * @throws Exception the exception
+ */
+ public void signLevel1(String securityProvider,PrivateKey key,String signingAlg, String keyId) throws Exception {
+ if (dynamicFrame != null) {
+ dynamicFrame.getLevel2SignedData().getLevel1Data().setSecurityProvider(securityProvider);
+ dynamicFrame.getLevel2SignedData().getLevel1Data().setLevel1SigningAlg(signingAlg);
+ dynamicFrame.getLevel2SignedData().getLevel1Data().setKeyId(Long.parseLong(keyId));
+ dynamicFrame.getLevel2SignedData().signLevel1(key);
+ } else if (staticFrame != null) {
+ staticFrame.setSignatureKey(keyId);
+ staticFrame.setSecurityProvider(securityProvider);
+ if (staticFrame.getHeaderRecord()!= null && staticFrame.getHeaderRecord().getIssuer() == null) {
+ staticFrame.getHeaderRecord().setIssuer(securityProvider);
+ }
+ staticFrame.signByAlgorithmOID(key,signingAlg);
+ }
+ }
+
+ /**
+ * Sets the static header parameter.
+ *
+ * @param ticketId the ticket id
+ * @param language the language
+ */
+ public void setStaticHeaderParams(String ticketId,String language) {
+ if (staticFrame != null && staticFrame.getHeaderRecord()!= null) {
+ staticFrame.getHeaderRecord().setIdentifier(ticketId);
+ staticFrame.getHeaderRecord().setLanguage(language);
+ }
+ }
+
+ /**
+ * Gets the dynamic frame.
+ *
+ * @return the dynamic frame
+ */
+ public DynamicFrame getDynamicFrame() {
+ return dynamicFrame;
+ }
+
+
+
+ /**
+ * Gets the static frame.
+ *
+ * @return the static frame
+ */
+ public StaticFrame getStaticFrame() {
+ return staticFrame;
+ }
+
+
+
+ /**
+ * Encodes the signed bar code data
+ *
+ * @return the byte[]
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws Exception the exception
+ */
+ public byte[] encode() throws IOException, Exception {
+ if (dynamicFrame != null) {
+ return dynamicFrame.encode();
+ } else if (staticFrame != null) {
+ return staticFrame.encode();
+ }
+ return null;
+ }
+
+
+
+
+
+
+
+
+}
diff --git a/src/org/uic/barcode/dynamicFrame/Constants.java b/src/org/uic/barcode/dynamicFrame/Constants.java
new file mode 100644
index 0000000..98b62aa
--- /dev/null
+++ b/src/org/uic/barcode/dynamicFrame/Constants.java
@@ -0,0 +1,38 @@
+package org.uic.barcode.dynamicFrame;
+
+public class Constants {
+
+ /*
+ * Object Identifier for recommended signature algorithms
+ *
+ */
+ public static String KG_EC_256 = "1.2.840.10045.3.1.7";
+ public static String KG_EC = "1.2.840.10045.2.1";
+ public static String ECDSA_SHA256 = "1.2.840.10045.4.3.2";
+
+ public static String DSA_SHA1 = "1.2.840.10040.4.3";
+ public static String DSA_SHA224 = "2.16.840.1.101.3.4.3.1";
+ public static String DSA_SHA248 = "2.16.840.1.101.3.4.3.2";
+
+ public static String DATA_TYPE_FCB_VERSION_1 = "FCB1";
+ public static String DATA_TYPE_FCB_VERSION_2 = "FCB2";
+
+ public static String DYNAMIC_BARCODE_FORMAT_DEFAULT = "U1";
+
+ public static int LEVEL2_VALIDATION_OK = 0;
+ public static int LEVEL2_VALIDATION_NO_KEY = 1;
+ public static int LEVEL2_VALIDATION_NO_SIGNATURE = 2;
+ public static int LEVEL2_VALIDATION_FRAUD = 3;
+ public static int LEVEL2_VALIDATION_SIG_ALG_NOT_IMPLEMENTED = 4;
+ public static int LEVEL2_VALIDATION_KEY_ALG_NOT_IMPLEMENTED = 5;
+ public static int LEVEL2_VALIDATION_ENCODING_ERROR = 6;
+
+ public static int LEVEL1_VALIDATION_OK = 0;
+ public static int LEVEL1_VALIDATION_NO_KEY = 1;
+ public static int LEVEL1_VALIDATION_NO_SIGNATURE = 2;
+ public static int LEVEL1_VALIDATION_FRAUD = 3;
+ public static int LEVEL1_VALIDATION_SIG_ALG_NOT_IMPLEMENTED = 4;
+ public static int LEVEL1_VALIDATION_KEY_ALG_NOT_IMPLEMENTED = 5;
+ public static int LEVEL1_VALIDATION_ENCODING_ERROR = 6;
+
+}
diff --git a/src/org/uic/barcode/dynamicFrame/DataType.java b/src/org/uic/barcode/dynamicFrame/DataType.java
new file mode 100644
index 0000000..fb84db6
--- /dev/null
+++ b/src/org/uic/barcode/dynamicFrame/DataType.java
@@ -0,0 +1,95 @@
+package org.uic.barcode.dynamicFrame;
+
+import net.gcdc.asn1.datatypes.Asn1Default;
+import net.gcdc.asn1.datatypes.CharacterRestriction;
+import net.gcdc.asn1.datatypes.RestrictedString;
+import net.gcdc.asn1.datatypes.Sequence;
+import net.gcdc.asn1.datatypesimpl.OctetString;
+import net.gcdc.asn1.uper.UperEncoder;
+
+/**
+ * The Class DataType.
+ */
+@Sequence
+public class DataType {
+
+
+ /** The data format.
+ *
+ * -- FCB1 FCB version 1
+ * -- FCB2 FCB version 2
+ * -- RICS company code + ...
+ **/
+ @Asn1Default("FCB1")
+ @RestrictedString(CharacterRestriction.IA5String)
+ public String format;
+
+ /** The data. */
+ public OctetString data;
+
+ /**
+ * Gets the data format.
+ *
+ * @return the data format
+ */
+ public String getFormat() {
+ return format;
+ }
+
+ /**
+ * Sets the data format.
+ *
+ * @param dataFormat the new data format
+ */
+ public void setFormat(String format) {
+ this.format = format;
+ }
+
+ /**
+ * Gets the data.
+ *
+ * @return the data
+ */
+ public OctetString getData() {
+ return data;
+ }
+
+ /**
+ * Sets the data.
+ *
+ * @param data the new data
+ */
+ public void setData(OctetString data) {
+ this.data = data;
+ }
+
+ /**
+ * Gets the data as byte array.
+ *
+ * @return the data
+ */
+ public byte[] getByteData() {
+ return data.toByteArray();
+ }
+
+ /**
+ * Sets the data from a byte array.
+ *
+ * @param data the new data
+ */
+ public void setByteData(byte[] data) {
+ this.data = new OctetString(data);
+ }
+
+ /**
+ * Encode.
+ *
+ * Encode the header as ASN.1 PER UNALIGNED byte array
+ *
+ * @return the byte[]
+ */
+ public byte[] encode() {
+ return UperEncoder.encode(this);
+ }
+
+}
diff --git a/src/org/uic/barcode/dynamicFrame/DynamicFrame.java b/src/org/uic/barcode/dynamicFrame/DynamicFrame.java
new file mode 100644
index 0000000..445990a
--- /dev/null
+++ b/src/org/uic/barcode/dynamicFrame/DynamicFrame.java
@@ -0,0 +1,263 @@
+package org.uic.barcode.dynamicFrame;
+
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+
+import org.uic.barcode.utils.AlgorithmNameResolver;
+
+import net.gcdc.asn1.datatypes.Asn1Optional;
+import net.gcdc.asn1.datatypes.CharacterRestriction;
+import net.gcdc.asn1.datatypes.HasExtensionMarker;
+import net.gcdc.asn1.datatypes.RestrictedString;
+import net.gcdc.asn1.datatypes.Sequence;
+import net.gcdc.asn1.datatypesimpl.OctetString;
+import net.gcdc.asn1.uper.UperEncoder;
+
+
+/**
+ * The DynamicHeader for bar codes
+ *
+ * Implementation of the Draft under discussion, not final.
+ */
+@Sequence
+@HasExtensionMarker
+public class DynamicFrame extends Object{
+
+ public DynamicFrame() {}
+
+ /** The format. */
+ @RestrictedString(CharacterRestriction.IA5String)
+ public String format;
+
+ /*level 2 data*/
+ Level2DataType level2SignedData;
+
+
+ /** The signature of level 2 data*/
+ @Asn1Optional public OctetString level2Signature;
+
+ /**
+ * Gets the format.
+ *
+ * @return the format
+ */
+ public String getFormat() {
+ return format;
+ }
+
+ /**
+ * Sets the format.
+ *
+ * @param format the new format
+ */
+ public void setFormat(String format) {
+ this.format = format;
+ }
+
+ public Level2DataType getLevel2SignedData() {
+ return level2SignedData;
+ }
+
+ public void setLevel2SignedData(Level2DataType level2SignedData) {
+ this.level2SignedData = level2SignedData;
+ }
+
+ public OctetString getLevel2Signature() {
+ return level2Signature;
+ }
+
+ public void setLevel2Signature(OctetString level2Signature) {
+ this.level2Signature = level2Signature;
+ }
+
+ /**
+ * Encode.
+ *
+ * Encode the header as ASN.1 PER UNALIGNED byte array
+ *
+ * @return the byte[]
+ */
+ public byte[] encode() {
+ return UperEncoder.encode(this);
+ }
+
+ /**
+ * Decode.
+ *
+ * Decode the header from an ASN.1 PER UNALIGNED encoded byte array
+ *
+ * @param bytes the bytes
+ * @return the dynamic header
+ */
+ public static DynamicFrame decode(byte[] bytes) {
+ return UperEncoder.decode(bytes, DynamicFrame.class);
+ }
+
+ /**
+ * Verify the level 2 signature
+ *
+ * Note: an appropriate security provider (e.g. BC) must be registered before
+ *
+ */
+ public int validateLevel2() {
+
+
+ String level2KeyAlg = this.getLevel2SignedData().getLevel1Data().level2KeyAlg;
+
+ if (level2KeyAlg == null || level2KeyAlg.length() == 0) {
+ return Constants.LEVEL2_VALIDATION_NO_KEY;
+ }
+
+ if (this.level2Signature.toByteArray() == null || this.level2Signature.toByteArray().length == 0) {
+ return Constants.LEVEL2_VALIDATION_NO_SIGNATURE;
+ }
+
+
+
+ String keyAlgName = null;
+ try {
+ keyAlgName = AlgorithmNameResolver.getName(AlgorithmNameResolver.TYPE_KEY_GENERATOR_ALG, level2KeyAlg);
+ } catch (Exception e1) {
+ return Constants.LEVEL2_VALIDATION_KEY_ALG_NOT_IMPLEMENTED;
+ }
+ if (keyAlgName == null || keyAlgName.length() == 0) {
+ return Constants.LEVEL2_VALIDATION_KEY_ALG_NOT_IMPLEMENTED;
+ }
+
+ PublicKey key = null;
+ try {
+ key = KeyFactory.getInstance(keyAlgName).generatePublic(new X509EncodedKeySpec(this.getLevel2SignedData().getLevel1Data().level2publicKey.toByteArray()));
+ } catch (InvalidKeySpecException | NoSuchAlgorithmException e1) {
+ return Constants.LEVEL2_VALIDATION_KEY_ALG_NOT_IMPLEMENTED;
+ }
+
+ //find the algorithm name for the signature OID
+ String algo = null;
+ try {
+ algo = AlgorithmNameResolver.getName(AlgorithmNameResolver.TYPE_SIGNATURE_ALG,this.getLevel2SignedData().getLevel1Data().level2SigningAlg);
+ } catch (Exception e1) {
+ return Constants.LEVEL2_VALIDATION_SIG_ALG_NOT_IMPLEMENTED;
+ }
+ if (algo == null) {
+ return Constants.LEVEL2_VALIDATION_SIG_ALG_NOT_IMPLEMENTED;
+ }
+
+ Signature sig;
+ try {
+ sig = Signature.getInstance(algo);
+ } catch (NoSuchAlgorithmException e) {
+ return Constants.LEVEL2_VALIDATION_SIG_ALG_NOT_IMPLEMENTED;
+ }
+ try {
+ sig.initVerify(key);
+ } catch (InvalidKeyException e) {
+ return Constants.LEVEL2_VALIDATION_SIG_ALG_NOT_IMPLEMENTED;
+ }
+
+ try {
+ sig.update(UperEncoder.encode(level2SignedData));
+ } catch (SignatureException e) {
+ return Constants.LEVEL2_VALIDATION_SIG_ALG_NOT_IMPLEMENTED;
+ } catch (IllegalArgumentException e) {
+ return Constants.LEVEL2_VALIDATION_ENCODING_ERROR;
+ } catch (UnsupportedOperationException e) {
+ return Constants.LEVEL2_VALIDATION_ENCODING_ERROR;
+ }
+
+ byte[] signature = level2Signature.toByteArray();
+ try {
+ if (sig.verify(signature)){
+ return Constants.LEVEL2_VALIDATION_OK;
+ } else {
+ return Constants.LEVEL2_VALIDATION_FRAUD;
+ }
+ } catch (SignatureException e) {
+ return Constants.LEVEL2_VALIDATION_SIG_ALG_NOT_IMPLEMENTED;
+ }
+ }
+
+ /**
+ * Verify the level 1 signature
+ *
+ * Note: an appropriate security provider (e.g. BC) must be registered before
+ *
+ */
+ public int validateLevel1(PublicKey key) {
+
+ if (this.level2SignedData == null) {
+ return Constants.LEVEL1_VALIDATION_NO_SIGNATURE;
+ }
+
+
+ if (this.level2SignedData.level1Signature == null || this.level2SignedData.level1Signature.toByteArray().length == 0) {
+ return Constants.LEVEL1_VALIDATION_NO_SIGNATURE;
+ }
+
+ byte[] signature = this.getLevel2SignedData().level1Signature.toByteArray();
+
+ //find the algorithm name for the signature OID
+ String algo = null;
+ try {
+ algo = AlgorithmNameResolver.getSignatureAlgorithmName(getLevel2SignedData().getLevel1Data().level1SigningAlg);
+ } catch (Exception e1) {
+ return Constants.LEVEL1_VALIDATION_SIG_ALG_NOT_IMPLEMENTED;
+ }
+ if (algo == null) {
+ return Constants.LEVEL1_VALIDATION_SIG_ALG_NOT_IMPLEMENTED;
+ }
+
+ Signature sig;
+ try {
+ sig = Signature.getInstance(algo);
+ } catch (NoSuchAlgorithmException e) {
+ return Constants.LEVEL1_VALIDATION_SIG_ALG_NOT_IMPLEMENTED;
+ }
+ try {
+ sig.initVerify(key);
+ } catch (InvalidKeyException e) {
+ return Constants.LEVEL1_VALIDATION_SIG_ALG_NOT_IMPLEMENTED;
+ }
+
+ try {
+ sig.update(this.level2SignedData.level1Data.encode());
+ } catch (SignatureException e) {
+ return Constants.LEVEL1_VALIDATION_SIG_ALG_NOT_IMPLEMENTED;
+ } catch (IllegalArgumentException e) {
+ return Constants.LEVEL1_VALIDATION_ENCODING_ERROR;
+ } catch (UnsupportedOperationException e) {
+ return Constants.LEVEL1_VALIDATION_ENCODING_ERROR;
+ }
+
+
+ try {
+ if (sig.verify(signature)){
+ return Constants.LEVEL2_VALIDATION_OK;
+ } else {
+ return Constants.LEVEL2_VALIDATION_FRAUD;
+ }
+ } catch (SignatureException e) {
+ return Constants.LEVEL2_VALIDATION_SIG_ALG_NOT_IMPLEMENTED;
+ }
+ }
+
+ public void signLevel2(PrivateKey key) throws Exception {
+
+ //find the algorithm name for the signature OID
+ String algo = AlgorithmNameResolver.getSignatureAlgorithmName(this.getLevel2SignedData().getLevel1Data().level2SigningAlg);
+ Signature sig = Signature.getInstance(algo);
+ sig.initSign(key);
+ byte[] data = level2SignedData.encode();
+ sig.update(data);
+ this.level2Signature = new OctetString(sig.sign());
+
+ }
+
+
+}
diff --git a/src/org/uic/barcode/dynamicFrame/Level1DataType.java b/src/org/uic/barcode/dynamicFrame/Level1DataType.java
new file mode 100644
index 0000000..ac48ba8
--- /dev/null
+++ b/src/org/uic/barcode/dynamicFrame/Level1DataType.java
@@ -0,0 +1,218 @@
+package org.uic.barcode.dynamicFrame;
+
+import org.uic.ticket.EncodingFormatException;
+import org.uic.ticket.api.utils.UicEncoderUtils;
+
+import net.gcdc.asn1.datatypes.Asn1Optional;
+import net.gcdc.asn1.datatypes.CharacterRestriction;
+import net.gcdc.asn1.datatypes.IntRange;
+import net.gcdc.asn1.datatypes.RestrictedString;
+import net.gcdc.asn1.datatypes.Sequence;
+import net.gcdc.asn1.datatypesimpl.OctetString;
+import net.gcdc.asn1.uper.UperEncoder;
+
+/**
+ * The Class SignedDataType.
+ */
+@Sequence
+public class Level1DataType {
+
+ /**
+ * The security provider
+ * numeric codes 1 ...32000
+ *
+ * */
+ @IntRange(minValue=1,maxValue=32000)
+ @Asn1Optional public Long securityProviderNum;
+
+ /** The security provider alphanumeric codes. */
+ @RestrictedString(CharacterRestriction.IA5String)
+ @Asn1Optional public String securityProviderIA5;
+
+
+ /** The key id. */
+ @IntRange(minValue=1,maxValue=99999)
+ @Asn1Optional public Long keyId;
+
+
+ /** The data. */
+ public SequenceOfDataType data;
+
+ /**
+ * The key generator algorithms
+ * Object Identifier of the Algorithm
+ * Number notation:
+ *
+ * e.g.:
+ * -- DSA SHA224 2.16.840.1.101.3.4.3.1
+ * -- DSA SHA248 2.16.840.1.101.3.4.3.2
+ * -- ECC 256 1.2.840.10045.3.1.7
+ *
+ *
+ */
+ @RestrictedString(CharacterRestriction.ObjectIdentifier)
+ @Asn1Optional public String level1KeyAlg;
+
+
+ @RestrictedString(CharacterRestriction.ObjectIdentifier)
+ @Asn1Optional public String level2KeyAlg;
+
+ /**
+ * The signing algorithm
+ * Object Identifier of the Algorithms
+ * Number notation:
+ *
+ * e.g.:
+ * -- DSA SHA224 2.16.840.1.101.3.4.3.1
+ * -- DSA SHA248 2.16.840.1.101.3.4.3.2
+ * -- ECC 256 1.2.840.10045.3.1.7
+ *
+ *
+ */
+ @RestrictedString(CharacterRestriction.ObjectIdentifier)
+ @Asn1Optional public String level1SigningAlg;
+
+ @RestrictedString(CharacterRestriction.ObjectIdentifier)
+ @Asn1Optional public String level2SigningAlg;
+
+
+ /** The level 2 public key*/
+ @Asn1Optional public OctetString level2publicKey;
+
+
+
+ /**
+ * Gets the security provider num.
+ *
+ * @return the security provider num
+ */
+ public Long getSecurityProviderNum() {
+ return securityProviderNum;
+ }
+
+ /**
+ * Sets the security provider num.
+ *
+ * in case the security provider code is encoded in IA5 this will return null
+ *
+ * @param securityProviderNum the new security provider num
+ */
+ public void setSecurityProviderNum(Long securityProviderNum) {
+ this.securityProviderNum = securityProviderNum;
+ }
+
+ /**
+ * Gets the security provider IA5.
+ *
+ * in case the security provider code is encoded numerically this will return null
+ *
+ * @return the security provider IA5
+ */
+ public String getSecurityProviderIA5() {
+ return securityProviderIA5;
+ }
+
+ /**
+ * Sets the security provider
+ *
+ * The security provider code must use the IA5 Alphabet .
+ *
+ * @param securityProvider the new security provider
+ * @throws EncodingFormatException the encoding format exception
+ */
+ public void setSecurityProvider(String securityProvider) throws EncodingFormatException {
+ this.securityProviderNum = UicEncoderUtils.getNum(securityProvider);
+ this.securityProviderIA5 = UicEncoderUtils.getIA5NonNum(securityProvider);
+ }
+
+
+ /**
+ * Gets the security provider.
+ *
+ * @return the security provider
+ */
+ public String getSecurityProvider() {
+ return UicEncoderUtils.mapToString(this.securityProviderNum, this.securityProviderIA5);
+ }
+
+
+ /**
+ * Sets the security provider IA 5.
+ *
+ * @param securityProviderIA5 the new security provider IA 5
+ */
+ public void setSecurityProviderIA5(String securityProviderIA5) {
+ this.securityProviderIA5 = securityProviderIA5;
+ }
+
+ public Long getKeyId() {
+ return keyId;
+ }
+
+ public void setKeyId(Long keyId) {
+ this.keyId = keyId;
+ }
+
+ public SequenceOfDataType getData() {
+ return data;
+ }
+
+ public void setData(SequenceOfDataType data) {
+ this.data = data;
+ }
+
+ public String getLevel2KeyAlg() {
+ return level2KeyAlg;
+ }
+
+ public void setLevel2KeyAlg(String level2KeyAlg) {
+ this.level2KeyAlg = level2KeyAlg;
+ }
+
+ public String getLevel1SigningAlg() {
+ return level1SigningAlg;
+ }
+
+ public void setLevel1SigningAlg(String level1SigningAlg) {
+ this.level1SigningAlg = level1SigningAlg;
+ }
+
+ public String getLevel2SigningAlg() {
+ return level2SigningAlg;
+ }
+
+ public void setLevel2SigningAlg(String level2SigningAlg) {
+ this.level2SigningAlg = level2SigningAlg;
+ }
+
+ public OctetString getLevel2publicKey() {
+ return level2publicKey;
+ }
+
+ public void setLevel2publicKey(OctetString level2publicKey) {
+ this.level2publicKey = level2publicKey;
+ }
+
+
+
+ public String getLevel1KeyAlg() {
+ return level1KeyAlg;
+ }
+
+ public void setLevel1KeyAlg(String level1KeyAlg) {
+ this.level1KeyAlg = level1KeyAlg;
+ }
+
+ /**
+ * Gets the data for signature.
+ *
+ * The byte array containing the ASN.1 PER UNALIGNED encoded data of the DataBlock
+ *
+ *
+ * @return the data for signature
+ */
+ public byte[] encode() {
+ return UperEncoder.encode(this);
+
+ }
+}
diff --git a/src/org/uic/barcode/dynamicFrame/Level2DataType.java b/src/org/uic/barcode/dynamicFrame/Level2DataType.java
new file mode 100644
index 0000000..66e4225
--- /dev/null
+++ b/src/org/uic/barcode/dynamicFrame/Level2DataType.java
@@ -0,0 +1,98 @@
+package org.uic.barcode.dynamicFrame;
+
+import java.security.PrivateKey;
+import java.security.Signature;
+
+import org.uic.barcode.utils.AlgorithmNameResolver;
+
+import net.gcdc.asn1.datatypes.Asn1Optional;
+import net.gcdc.asn1.datatypes.Sequence;
+import net.gcdc.asn1.datatypesimpl.OctetString;
+import net.gcdc.asn1.uper.UperEncoder;
+
+/**
+ * The Class DataType.
+ */
+@Sequence
+public class Level2DataType {
+
+ Level1DataType level1Data;
+
+ /** The data. */
+ @Asn1Optional public OctetString level1Signature;
+
+
+ @Asn1Optional DataType level2Data;
+
+
+ public Level1DataType getLevel1Data() {
+ return level1Data;
+ }
+
+
+ public void setLevel1Data(Level1DataType level1Data) {
+ this.level1Data = level1Data;
+ }
+
+
+ public OctetString getLevel1Signature() {
+ return level1Signature;
+ }
+
+ public byte[] getLevel1SignatureBytes() {
+ return level1Signature.toByteArray();
+ }
+
+ public void setLevel1Signature(OctetString level1Signature) {
+ this.level1Signature = level1Signature;
+ }
+
+ public void setLevel1Signature(byte[] level1Signature) {
+ this.level1Signature = new OctetString(level1Signature);
+ }
+
+
+ public DataType getLevel2Data() {
+ return level2Data;
+ }
+
+
+ public void setLevel2Data(DataType level2Data) {
+ this.level2Data = level2Data;
+ }
+
+
+ /**
+ * Encode.
+ *
+ * Encode the header as ASN.1 PER UNALIGNED byte array
+ *
+ * @return the byte[]
+ */
+ public byte[] encode() {
+ return UperEncoder.encode(this);
+ }
+
+ /**
+ * Sign the contained data block.
+ *
+ * Note: an appropriate security provider (e.g. BC) must be registered before
+ *
+ * @param key the key
+ * @return
+ * @return the byte[]
+ * @throws Exception
+ */
+ public void signLevel1(PrivateKey key) throws Exception {
+ //find the algorithm name for the signature OID
+ String algo = AlgorithmNameResolver.getSignatureAlgorithmName(getLevel1Data().level1SigningAlg);
+ Signature sig = Signature.getInstance(algo);
+ sig.initSign(key);
+ byte[] data = level1Data.encode();
+ sig.update(data);
+ this.level1Signature = new OctetString(sig.sign());
+ }
+
+
+
+}
diff --git a/src/org/uic/barcode/dynamicFrame/SequenceOfDataType.java b/src/org/uic/barcode/dynamicFrame/SequenceOfDataType.java
new file mode 100644
index 0000000..c879ddd
--- /dev/null
+++ b/src/org/uic/barcode/dynamicFrame/SequenceOfDataType.java
@@ -0,0 +1,26 @@
+package org.uic.barcode.dynamicFrame;
+
+
+import java.util.Collection;
+
+import net.gcdc.asn1.datatypes.Asn1SequenceOf;
+// TODO: Auto-generated Javadoc
+
+/**
+ * The Class SequenceOfDataType.
+ */
+public class SequenceOfDataType extends Asn1SequenceOf<DataType>{
+
+ /**
+ * Instantiates a new sequence of data type.
+ */
+ public SequenceOfDataType() { super(); }
+
+ /**
+ * Instantiates a new sequence of data type.
+ *
+ * @param coll the coll
+ */
+ public SequenceOfDataType(Collection<DataType> coll) { super(coll); }
+
+}
diff --git a/src/org/uic/barcode/dynamicFrame/headerSpec.asn b/src/org/uic/barcode/dynamicFrame/headerSpec.asn
new file mode 100644
index 0000000..08d84f6
--- /dev/null
+++ b/src/org/uic/barcode/dynamicFrame/headerSpec.asn
@@ -0,0 +1,119 @@
+-- Author: ClemensGantert
+-- Created: Thu Jun 04 17:19:28 CEST 2020
+ASN-Module DEFINITIONS AUTOMATIC TAGS ::= BEGIN
+
+-- imports and exports
+-- EXPORTS ALL;
+
+
+-- ##############################################################################################
+-- #
+-- # UIC barcode header - first draft
+-- #
+-- ##############################################################################################
+
+
+-- ##############################################################################################
+-- #
+-- # Naming and encoding conventions
+-- #
+-- # Elements included as String and as Numeric values:
+-- # Some elements are included in different formats to reduce the data size.
+-- # These elements must be included only once.
+-- # These elements are named with the same name and appendix
+-- # Num (numeric values)
+-- # IA5 (String values according to ASN IA5String (7Bit))
+-- #
+-- # RICS codes must be used to encode companies (issuer, product owner, ...) where available
+-- # other codes are possible based on bilateral agreements
+-- # the format is kept more flexible to cover upcoming extensions of the RICS code by ERA
+-- #
+-- #
+-- # - A bar code which is only static (printed on a paper), and for which the security is in the system, doesn’t need any of these elements.
+-- # - A bar code which is only static, and includes its own security, needs:
+-- # level1Signature
+-- # level1KeyAlg if the associated key does not include the complete certificate in keys.xml but only the public key
+-- # (but level1SigningAlg is not necessary as it is in keys.xml)
+-- # - A dynamic bar code including static and dynamic signatures needs:
+-- # The same elements as a static bar code above,
+-- # level2SigningAlg, level2keyAlg, level2PublicKey, and level2Signature.
+-- #
+-- #########################################################################################
+
+
+-- ############################################################################################
+
+
+-- type assignments
+
+ -- #########################################################################################
+ -- the basic entry point of the data structure
+ -- ##########################################################################################
+ UicBarcodeHeader ::= SEQUENCE {
+ -- barcode format type
+ format IA5String,
+ -- "U1" = UIC ticket
+
+
+ level2SignedData Level2DataType,
+
+ -- signature is calculated on the PER unaligned encoding of level2 signature data
+ level2Signature OCTET STRING OPTIONAL
+
+
+ }
+
+ Level2DataType ::= SEQUENCE {
+
+ level1Data Level1DataType,
+
+ -- signature is calculated on the PER unaligned encoding of level1 signature data
+ level1Signature OCTET STRING OPTIONAL,
+
+ level2Data DataType OPTIONAL
+
+ }
+
+
+ Level1DataType ::= SEQUENCE {
+
+ -- provider of the level1 signature (RICS code)
+ securityProviderNum INTEGER (1..32000) OPTIONAL,
+ securityProviderIA5 IA5String OPTIONAL,
+
+ keyId INTEGER(0..99999) OPTIONAL,
+
+ dataSequence SEQUENCE OF DataType,
+
+
+ -- object identifier of the key algorithms
+ -- e.g.
+ -- ECC P-256 1.2.840.10045.3.1.7
+ level1KeyAlg OBJECT IDENTIFIER OPTIONAL,
+ level2KeyAlg OBJECT IDENTIFIER OPTIONAL,
+
+ -- object identifier of the signing algorithm
+ -- e.g.
+ -- DSA SHA224 2.16.840.1.101.3.4.3.1
+ -- DSA SHA256 2.16.840.1.101.3.4.3.2
+ -- ECDSA-256 1.2.840.10045.4.3.2
+ -- algorithm used for signing
+ level1SigningAlg OBJECT IDENTIFIER OPTIONAL,
+ level2SigningAlg OBJECT IDENTIFIER OPTIONAL,
+
+ level2PublicKey OCTET STRING OPTIONAL
+
+ }
+
+ DataType ::= SEQUENCE {
+ -- Content of data format:
+ -- FCBn (FCB1 = FCB version 1, FCB2 = FCB version 2)
+ -- FDCn dynamic content
+ -- or proprietary:
+ -- _RICS company code + addon
+ dataFormat IA5String,
+ data OCTET STRING
+ }
+
+
+END \ No newline at end of file
diff --git a/src/org/uic/barcode/dynamicFrame/package.html b/src/org/uic/barcode/dynamicFrame/package.html
new file mode 100644
index 0000000..dbe6c06
--- /dev/null
+++ b/src/org/uic/barcode/dynamicFrame/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head></head>
+<body>
+
+<h1>drafted new header for dynamic content </h1>
+<p>Provides a decoding and encoding of the header data frame. (Draft for UIC IRS 90918-9).</p>
+</body>
+</html> \ No newline at end of file
diff --git a/src/org/uic/barcode/package.html b/src/org/uic/barcode/package.html
new file mode 100644
index 0000000..4a6ee0d
--- /dev/null
+++ b/src/org/uic/barcode/package.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head></head>
+<body>
+
+ <p>Provides the decoder and encoder for a UIC barcode</p>
+
+
+ <p> Included features:<br/><br/>
+ <ul>
+ <li>dynamic bar code (DOSIPA) (version 1)</li>
+ <li>static bar code (version 1 and 2)</li>
+ <li>- FCB version 1</li>
+ <li>- FCB version 2 (dynamic bar code only)</li>
+ <li>- TLB (static bar code only)</li>
+ </ul>
+ </p>
+
+
+</body>
+</html> \ No newline at end of file
diff --git a/src/org/uic/barcode/staticFrame/DataRecord.java b/src/org/uic/barcode/staticFrame/DataRecord.java
new file mode 100644
index 0000000..27e128f
--- /dev/null
+++ b/src/org/uic/barcode/staticFrame/DataRecord.java
@@ -0,0 +1,199 @@
+package org.uic.barcode.staticFrame;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.uic.ticket.EncodingFormatException;
+
+/**
+ * The Class DataRecord implements the basic decoding and encoding
+ * of the data record structure, the split into tag, version, length and content.
+ *
+ * Implementing classes must provide decoding and encoding of the content
+ *
+ */
+public abstract class DataRecord {
+
+ /** The id tag. */
+ protected String idTag;
+
+ /** The version id. */
+ protected String versionId;
+
+ /** The content. */
+ protected byte[] content;
+
+ /**
+ * Instantiates a new data record.
+ *
+ * @param idTag the id tag
+ * @param version the version
+ */
+ public DataRecord (String idTag, String version) {
+ this.idTag = idTag;
+ this.versionId = version;
+ }
+
+ /**
+ * Instantiates a new data record.
+ *
+ * @param idTag the id tag
+ */
+ public DataRecord (String idTag) {
+ this.idTag = idTag;
+ }
+
+ /**
+ * Encode.
+ *
+ * @return the byte[]
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ public byte[] encode() throws IOException, EncodingFormatException {
+
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+
+ encodeContent();
+
+ //size of tag + version + length
+ int length = 12;
+
+ //size of data
+ length = length + content.length;
+
+ String lengthElement = String.format("%04d",length);
+
+ while (idTag.length() < 6) {
+ idTag = idTag + " ";
+ }
+
+ while (versionId.length() < 2) {
+ versionId = "0" + versionId;
+ }
+
+ outputStream.write(idTag.getBytes());
+
+ outputStream.write(versionId.getBytes());
+
+ outputStream.write(lengthElement.getBytes());
+
+ outputStream.write(content);
+
+ return outputStream.toByteArray();
+ }
+
+ /**
+ * Decode.
+ *
+ * @param byteData the byte data
+ * @return the int
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ public int decode(byte[] byteData) throws IOException, EncodingFormatException {
+
+ int offset = 0;
+ String tag = new String(Arrays.copyOfRange(byteData, offset, offset + 6));
+ this.setIdTag(tag);
+ offset = offset + 6;
+
+ String version = new String(Arrays.copyOfRange(byteData, offset, offset + 2));
+ this.setVersionId(version);
+ offset = offset + 2;
+
+ String dataSize = new String(Arrays.copyOfRange(byteData, offset, offset + 4));
+ offset = offset + 4;
+
+ int length = Integer.parseInt(dataSize) - 12;
+ this.setData(Arrays.copyOfRange(byteData, offset, offset + length));
+
+ decodeContent();
+
+ return length + 12;
+ }
+
+
+ /**
+ * Gets the id tag.
+ *
+ * @return the id tag
+ */
+ public String getIdTag() {
+ return idTag;
+ }
+
+
+ /**
+ * Sets the id tag.
+ *
+ * @param idTag the new id tag
+ */
+ public void setIdTag(String idTag) {
+ this.idTag = idTag;
+ }
+
+
+ /**
+ * Gets the version id.
+ *
+ * @return the version id
+ */
+ public String getVersionId() {
+ return versionId;
+ }
+
+
+ /**
+ * Sets the version id.
+ *
+ * @param versionId the new version id
+ */
+ public void setVersionId(String versionId) {
+ this.versionId = versionId;
+ }
+
+
+ /**
+ * Gets the data.
+ *
+ * @return the data
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ protected byte[] getData() throws IOException, EncodingFormatException {
+ return content;
+ }
+
+
+ /**
+ * Sets the data.
+ *
+ * @param data the new data
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ protected void setData(byte[] data) throws IOException, EncodingFormatException {
+ this.content = data;
+ }
+
+
+
+ /**
+ * Decode content.
+ *
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ protected abstract void decodeContent() throws IOException, EncodingFormatException;
+
+ /**
+ * Encode content.
+ *
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ protected abstract void encodeContent() throws IOException, EncodingFormatException;
+
+}
diff --git a/src/org/uic/barcode/staticFrame/GENERICDataRecord.java b/src/org/uic/barcode/staticFrame/GENERICDataRecord.java
new file mode 100644
index 0000000..2477af9
--- /dev/null
+++ b/src/org/uic/barcode/staticFrame/GENERICDataRecord.java
@@ -0,0 +1,62 @@
+package org.uic.barcode.staticFrame;
+
+import java.io.IOException;
+
+import org.uic.ticket.EncodingFormatException;
+
+
+/**
+ * The Class GENERICDataRecord implements a generic bilateral data record included in a static bar code frame.
+ */
+public class GENERICDataRecord extends DataRecord {
+
+ /**
+ * Instantiates a new GENERIC data record.
+ *
+ * @param idTag the id tag
+ */
+ public GENERICDataRecord(String idTag) {
+ super(idTag);
+ }
+
+ /**
+ * Decode content.
+ *
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ @Override
+ protected void decodeContent() throws IOException, EncodingFormatException {
+ // Do Nothing, needs to be implemented by subclasses
+ }
+
+ /**
+ * Encode content.
+ *
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ @Override
+ protected void encodeContent() throws IOException, EncodingFormatException {
+ // Do Nothing, needs to be implemented by subclasses
+ }
+
+ /**
+ * Gets the content.
+ *
+ * @return the content
+ */
+ public byte[] getContent() {
+ return content;
+ }
+
+ /**
+ * Sets the content.
+ *
+ * @param content the new content
+ */
+ public void setContent(byte[] content) {
+ this.content = content;
+ }
+
+}
diff --git a/src/org/uic/barcode/staticFrame/StaticFrame.java b/src/org/uic/barcode/staticFrame/StaticFrame.java
new file mode 100644
index 0000000..7a0f029
--- /dev/null
+++ b/src/org/uic/barcode/staticFrame/StaticFrame.java
@@ -0,0 +1,762 @@
+package org.uic.barcode.staticFrame;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.Security;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.Provider.Service;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.zip.DataFormatException;
+import java.util.zip.Deflater;
+import java.util.zip.Inflater;
+
+import org.uic.ticket.EncodingFormatException;
+
+
+/**
+ * The Class StaticHeader implements the static bar code header frame defined in UIC IRS 90918-9.
+ * It allows to decode and encode the bar code content and to add sub-records as defined in the IRS 90918-9 for:
+ * - additional header data
+ * - Ticket Layout content
+ * - Flexible content
+ * - bilateral data records
+ */
+public class StaticFrame {
+
+ /** The additional header record. */
+ private UHEADDataRecord headerRecord;
+
+ /** The bar code version. */
+ private int version;
+
+ /** The u_flex. */
+ private UFLEXDataRecord uFlex;
+
+ /** The u_tlay. */
+ private UTLAYDataRecord uTlay;
+
+ /** The security provider. */
+ private String securityProvider;
+
+ /** The signature key. */
+ private String signatureKey;
+
+ /** The signature. */
+ private byte[] signature;
+
+ /** The data records. */
+ private ArrayList<DataRecord> dataRecords = new ArrayList<DataRecord>();
+
+
+ private byte[] signedData = null;
+
+ /**
+ * Instantiates a new static header frame.
+ */
+ public StaticFrame (){ }
+
+
+
+ /**
+ * Instantiates a new static header and decodes the provided data.
+ *
+ * @param bytes the bar code data
+ * @throws EncodingFormatException the encoding format exception
+ * @throws DataFormatException the data format exception
+ * @throws IOException Signals that an I/O exception has occurred.
+ */
+ public StaticFrame (byte[] bytes) throws EncodingFormatException, DataFormatException, IOException{
+ decode(bytes);
+ }
+
+
+ /**
+ * Encode the barcode data.
+ *
+ * @param version the barcode version
+ * @return byte[] the encoded data as
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws Exception the exception
+ */
+ /*
+ * creates a UIC bar code of version 1
+ *
+ * limits:
+ * - version 1 allows for signatures up to 50 byte length
+ * - max data length 2048 Byte
+ * input:
+ * data to be included
+ * provider of the signature
+ * processing:
+ * 1. create header informations
+ * 2. compression of the data content
+ * 3. adding a signature
+ * output:
+ * raw data to be included in an aztec bar code
+ *
+ */
+ public byte[] encode() throws IOException, Exception {
+
+ if (headerRecord == null && uFlex == null && uTlay == null
+ && (dataRecords == null || dataRecords.isEmpty())) return null;
+
+ if (signedData == null) {
+ signedData = encodeData();
+ }
+
+ if (version != 1 && version != 2) {
+ throw (new Exception(String.format("UIC Barcode Version %d not supported", version)));
+ }
+
+ if (signedData.length < 1) {
+ throw new IOException("data missing!");
+ }
+ if (signedData.length > 2048) {
+ throw new IOException("too many data!"); //2048 should be enough
+ }
+
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+
+ //UIC bar code version 1
+ String header = "#UT01";
+ if (version == 2) {
+ header = "#UT02";
+ }
+ outputStream.write(header.getBytes());
+
+ outputStream.write(securityProvider.getBytes());
+
+
+ while (signatureKey.length() < 5) {
+ signatureKey = "0" + signatureKey;
+ }
+ outputStream.write(signatureKey.getBytes());
+
+ if (signature.length < 1) {
+ // signature too small for bar code version 1
+ throw new IOException("signature size too small!");
+ }
+
+ if (version == 1) {
+ if (signature.length > 50) {
+ // signature too large for bar code version 1
+ throw new IOException("signature size too large!");
+ }
+ outputStream.write(Arrays.copyOfRange(signature, 0, 50));
+ } else if (version == 2) {
+ BigInteger[] bInts = null;
+ byte zeroByte = 0;
+
+ bInts = decodeSignatureIntegerSequence(signature);
+ byte[] r = toUnsignedBytes(bInts[0]);
+
+ byte[] s = toUnsignedBytes(bInts[1]);
+
+ if (r.length > 32 || s.length > 32) {
+ throw (new EncodingFormatException(String.format("DSA signature too big")));
+ }
+ for (int i = 0; i < 32 - r.length; i++) {
+ outputStream.write(zeroByte);
+ }
+ outputStream.write(r);
+ for (int i = 0; i < 32 - s.length; i++) {
+ outputStream.write(zeroByte);
+ }
+ outputStream.write(s);
+ //outputStream.write(Arrays.copyOfRange(signature, 0, 64));
+ }
+
+ String length = String.format("%04d", signedData.length);
+ outputStream.write(length.getBytes());
+
+ outputStream.write(signedData);
+
+ outputStream.close();
+
+ return outputStream.toByteArray();
+ }
+
+
+ /**
+ * Adds a proprietary data record.
+ *
+ * @param record the record
+ */
+ public void addDataRecord(DataRecord record) {
+ dataRecords.add(record);
+ }
+
+ /**
+ * Gets the version of the header frame.
+ *
+ * @return the version
+ */
+ public int getVersion() {
+ return version;
+ }
+
+ /**
+ * Sets the version of the header frame.
+ * supported values are 1 and 2
+ *
+ * @param version the new version
+ */
+ public void setVersion(int version) {
+ this.version = version;
+ }
+
+ /**
+ * Gets the security provider.
+ *
+ * @return the security provider
+ */
+ public String getSecurityProvider() {
+ return securityProvider;
+ }
+
+ /**
+ * Sets the security provider.
+ *
+ * @param securityProvider the new security provider
+ */
+ public void setSecurityProvider(String securityProvider) {
+ this.securityProvider = securityProvider;
+ }
+
+ /**
+ * Gets the signature key identifier.
+ *
+ * @return the signature key
+ */
+ public String getSignatureKey() {
+ return signatureKey;
+ }
+
+ /**
+ * Sets the signature key identifier.
+ *
+ * @param signatureKey the new signature key
+ */
+ public void setSignatureKey(String signatureKey) {
+ this.signatureKey = signatureKey;
+ }
+
+ /**
+ * Gets the signature.
+ *
+ * @return the signature
+ */
+ public byte[] getSignature() {
+ return signature;
+ }
+
+ /**
+ * Sets the signature.
+ *
+ * @param signature the new signature
+ */
+ public void setSignature(byte[] signature) {
+ this.signature = signature;
+ }
+
+ /**
+ * Gets the additional header record.
+ *
+ * @return the header record
+ */
+ public UHEADDataRecord getHeaderRecord() {
+ return headerRecord;
+ }
+
+ /**
+ * Gets the list of bilateral data records.
+ *
+ * @return the data records
+ */
+ public ArrayList<DataRecord> getDataRecords() {
+ return dataRecords;
+ }
+
+ /**
+ * Gets the data for signing.
+ *
+ * @return the data to be signed
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ public byte[] getDataForSignature() throws IOException, EncodingFormatException {
+ // data compression
+ if (signedData != null) return signedData;
+
+ Deflater deflater = new Deflater();
+ byte[] data = encodeData();
+ deflater.setInput(data);
+ ByteArrayOutputStream compressStream = new ByteArrayOutputStream(data.length);
+ byte[] buffer = new byte[2048];
+ deflater.finish();
+ while (!deflater.finished()) {
+ int count = deflater.deflate(buffer); // returns the number of result bytes
+ compressStream.write(buffer, 0, count);
+ }
+ compressStream.close();
+
+ return compressStream.toByteArray();
+ }
+
+ /**
+ * Get the encoded data for the bar code.
+ *
+ * @return the byte[]
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ private byte[] encodeData() throws IOException, EncodingFormatException {
+
+ if (this.uFlex == null && this.uTlay == null && this.headerRecord == null &&
+ (dataRecords == null || dataRecords.isEmpty())) return null;
+
+ ByteArrayOutputStream totalStream = new ByteArrayOutputStream();
+
+ //encode header for layout
+ if (headerRecord != null) {
+ byte[] header = headerRecord.encode();
+
+ if (header != null && header.length > 0) {
+ totalStream.write(header);
+ }
+ }
+
+ //encode layout
+ if (uTlay != null) {
+ byte[] layout = uTlay.encode();
+ if (layout != null && layout.length > 0) {
+ totalStream.write(layout);
+ }
+ }
+
+ if (uFlex != null) {
+ byte[] content = uFlex.encode();
+ if (content != null && content.length > 0){
+ totalStream.write(content);
+ }
+ }
+
+ //third party content
+ for (DataRecord dataRecord : dataRecords){
+
+ byte[] content = dataRecord.encode();
+ if (content != null && content.length > 0){
+ totalStream.write(content);
+ }
+ }
+ return totalStream.toByteArray();
+ }
+
+ /**
+ * Encode signature integer sequence.
+ *
+ * Support function to format two parameters as DER encoded integer list
+ * to get a valid formated DSA signature from the signature parameter
+ *
+ * @param i1 the i 1
+ * @param i2 the i 2
+ * @return the byte[]
+ * @throws IOException Signals that an I/O exception has occurred.
+ */
+ public static byte[] encodeSignatureIntegerSequence(BigInteger i1, BigInteger i2) throws IOException {
+
+ //SEQUENCE OF --> tag 16
+ int sequenceTag = 16 + 32; // (bits 6 = 1 constructed)
+ //INTEGER --> tag 2
+ int integerTag = 2;
+
+ byte[] b1 = toUnsignedBytes(i1);
+ int lb1 = b1.length;
+ byte[] b2 = toUnsignedBytes(i2);
+ int lb2 = b2.length;
+
+ int sequenceLength = lb1 + lb2 + 4;
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ out.write((byte) sequenceTag);
+ out.write((byte) sequenceLength);
+ out.write((byte) integerTag);
+ out.write((byte) lb1);
+ out.write(b1);
+ out.write((byte) integerTag);
+ out.write((byte) lb2);
+ out.write(b2);
+
+ return out.toByteArray();
+ }
+
+ /**
+ * Decode signature integer sequence.
+ *
+ * Support function to decode a DSA signature
+ * Provides the two DSA signature parameter encoded in a DSA signature
+ *
+ * @param bytes the bytes
+ * @return the big integer[]
+ * @throws Exception the exception
+ */
+ public static BigInteger[] decodeSignatureIntegerSequence(byte[] bytes) throws Exception {
+
+ int sequenceTag = (int) bytes[0];
+
+ if (sequenceTag != 48) throw new Exception("signature is not a sequence");
+
+ int sequenceLength = (int) bytes[1];
+
+ if (sequenceLength < 6) throw new Exception("signature sequence too short");
+
+ BigInteger[] result = new BigInteger[2];
+
+ int offset = 2;
+ int i = 0;
+ while (offset < bytes.length && i < 2) {
+ int integerTag = (int) bytes[offset];
+ if (integerTag != 2) throw new Exception("signature is not an integer sequence");
+ int integerLength = (int) bytes[offset + 1];
+ byte[] value = Arrays.copyOfRange(bytes, offset + 2, offset + 2 + integerLength);
+ result[i] = new BigInteger(+1, value);
+ offset = offset + integerLength + 2;
+ i++;
+ }
+
+ return result;
+ }
+
+ /**
+ * Decode.
+ *
+ * @param inputData the input data
+ * @throws EncodingFormatException the encoding format exception
+ * @throws DataFormatException the data format exception
+ * @throws IOException Signals that an I/O exception has occurred.
+ */
+ public void decode(byte[] inputData) throws EncodingFormatException, DataFormatException, IOException {
+
+
+ int offset = 0;
+ String headerTag = new String( Arrays.copyOfRange(inputData,offset,offset + 3));
+ offset = offset + 3;
+ if (!headerTag.equals("#UT")) {
+ throw (new EncodingFormatException("not a UIC barcode"));
+ }
+
+
+ String versionValue = new String(Arrays.copyOfRange(inputData,offset,offset + 2));
+ offset = offset + 2;
+ int barcodeVersion = 0;
+ try {
+ barcodeVersion = Integer.parseInt(versionValue);
+ this.setVersion(barcodeVersion);
+ } catch (NumberFormatException e2) {
+ throw (new EncodingFormatException(String.format("UIC Barcode Version %s not supported", versionValue)));
+ }
+
+ String providerValue = new String( Arrays.copyOfRange(inputData,offset,offset + 4));
+ this.setSecurityProvider(providerValue);
+ offset = offset + 4;
+
+ String signatureKeyIdValue = new String( Arrays.copyOfRange(inputData,offset,offset + 5));
+ this.setSignatureKey(signatureKeyIdValue);
+ offset = offset + 5;
+
+ byte[] sealdata = null;
+
+ if (barcodeVersion == 1) {
+ sealdata = Arrays.copyOfRange(inputData, offset, offset + 50);
+ signature = trimDsaSignature(sealdata);
+ offset = offset + 50;
+ } else if (barcodeVersion == 2) {
+ sealdata = Arrays.copyOfRange(inputData, offset, offset + 64);
+ signature = recombineDsaSignature(sealdata);
+ offset = offset + 64;
+ } else {
+ throw (new EncodingFormatException(String.format("UIC Barcode Version %s not supported", versionValue)));
+ }
+
+
+ String lengthValue = new String( Arrays.copyOfRange(inputData,offset,offset + 4));
+ offset = offset + 4;
+
+ int dataLength = 0;
+ dataLength = Integer.parseInt(lengthValue);
+
+ signedData = Arrays.copyOfRange(inputData, offset, offset + dataLength);
+
+ ByteBuffer containedDataBuffer = ByteBuffer.allocate(dataLength);
+ containedDataBuffer.put(signedData);
+
+ byte[] inflatedDataBuffer = new byte[2000];
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ Inflater inflater = new Inflater();
+ byte[] inflaterInput = containedDataBuffer.array();
+ inflater.setInput(inflaterInput);
+ while (!inflater.finished()) {
+ int count = inflater.inflate(inflatedDataBuffer,0,2000);
+ if (inflater.needsDictionary()) {
+ break;
+ }
+ outputStream.write(inflatedDataBuffer, 0, count);
+ }
+
+ outputStream.close();
+
+ byte[] byteData = outputStream.toByteArray();
+
+ offset = 0;
+ int remainingBytes = byteData.length;
+
+ while (remainingBytes > 0) {
+
+ String tag = new String(Arrays.copyOfRange(byteData, offset, offset + 6));
+ int length = 0;
+
+ if (tag.startsWith("U_TLAY")) {
+ UTLAYDataRecord record = new UTLAYDataRecord();
+ length = record.decode(Arrays.copyOfRange(byteData, offset, byteData.length));
+ this.uTlay = record;
+ } else if (tag.startsWith("U_FLEX")) {
+ UFLEXDataRecord record = new UFLEXDataRecord();
+ length = record.decode(Arrays.copyOfRange(byteData, offset, byteData.length));
+ this.uFlex = record;
+ } else if (tag.startsWith("U_HEAD")) {
+ UHEADDataRecord record = new UHEADDataRecord();
+ length = record.decode(Arrays.copyOfRange(byteData, offset, byteData.length));
+ this.headerRecord = record;
+ } else {
+ DataRecord record = new GENERICDataRecord(tag);
+ length = record.decode(Arrays.copyOfRange(byteData, offset, byteData.length));
+ addDataRecord(record);
+ }
+ offset = offset + length;
+ remainingBytes = remainingBytes - length;
+ }
+ }
+
+
+ private byte[] recombineDsaSignature(byte[] sealdata) throws IOException {
+
+ //check whether the encoding is wrong and the sealdata contain a signature
+ //remove trailing zeroes from the signature
+ BigInteger[] bInts = null;
+ try {
+ bInts = decodeSignatureIntegerSequence(sealdata);
+ byte[] sig = encodeSignatureIntegerSequence(bInts[0],bInts[1]);
+ //decoding the entire signature was ok, so there was no split
+ return sig;
+ } catch (Exception e) {
+ //the signature is correctly implemented, continue with recombination
+ }
+
+ // split the data into two blocks
+ int length = sealdata.length / 2;
+ byte[] rBytes = Arrays.copyOfRange(sealdata, 0, length);
+ byte[] sBytes = Arrays.copyOfRange(sealdata, length, length + length);
+
+ //convert to BigInteger to get rid of leading zeroes
+ BigInteger r = new BigInteger(1,rBytes);
+ BigInteger s = new BigInteger(1,sBytes);
+
+ //encode as DSA signature structure
+ //SEQUENCE OF --> tag 16
+ int sequenceTag = 16 + 32; // (bits 6 = 1 constructed)
+ //INTEGER --> tag 2
+ int integerTag = 2;
+ byte[] b1 = toUnsignedBytes(r);
+ int lb1 = b1.length;
+ byte[] b2 = toUnsignedBytes(s);
+ int lb2 = b2.length;
+ int sequenceLength = lb1 + lb2 + 4;
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write((byte) sequenceTag);
+ out.write((byte) sequenceLength);
+ out.write((byte) integerTag);
+ out.write((byte) lb1);
+ out.write(b1);
+ out.write((byte) integerTag);
+ out.write((byte) lb2);
+ out.write(b2);
+ return out.toByteArray();
+
+
+ }
+
+ private static byte[] toUnsignedBytes(BigInteger i) {
+ byte[] b = i.abs().toByteArray();
+ //remove top sign bit
+ if (b[0] == 0) {
+ b = Arrays.copyOfRange(b, 1, b.length);
+ }
+ return b;
+ }
+
+
+ private byte[] trimDsaSignature(byte[] sealdata) throws EncodingFormatException {
+ //remove trailing zeroes from the signature
+ BigInteger[] bInts = null;
+ try {
+ bInts = decodeSignatureIntegerSequence(sealdata);
+ return encodeSignatureIntegerSequence(bInts[0],bInts[1]);
+ } catch (Exception e) {
+ throw (new EncodingFormatException(String.format("Invalid DSA signature")));
+ }
+ }
+
+
+
+ /**
+ * Verify the signature
+ *
+ * Note: an appropriate security provider (e.g. BC) must be registered before
+ *
+ * @param key the key
+ * @param algo the algorithm name
+ * @return true, if successful
+ * @throws InvalidKeyException the invalid key exception
+ * @throws NoSuchAlgorithmException the no such algorithm exception
+ * @throws SignatureException the signature exception
+ * @throws IllegalArgumentException the illegal argument exception
+ * @throws UnsupportedOperationException the unsupported operation exception
+ * @throws EncodingFormatException
+ * @throws IOException
+ */
+ public boolean ByAlgorithmName(PublicKey key, String algo) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IllegalArgumentException, UnsupportedOperationException, IOException, EncodingFormatException {
+ Signature sig = Signature.getInstance(algo);
+ sig.initVerify(key);
+ sig.update(this.getDataForSignature());
+ return sig.verify(this.getSignature());
+ }
+
+ /**
+ * Verify the signature
+ *
+ * Note: an appropriate security provider (e.g. BC) must be registered before
+ *
+ * @param key the key
+ * @param singningAlg the Object ID of the signing algorithm
+ * @return true, if successful
+ * @throws InvalidKeyException the invalid key exception
+ * @throws NoSuchAlgorithmException the no such algorithm exception
+ * @throws SignatureException the signature exception
+ * @throws IllegalArgumentException the illegal argument exception
+ * @throws UnsupportedOperationException the unsupported operatign exception
+ * @throws EncodingFormatException
+ * @throws IOException
+ */
+ public boolean verifyByAlgorithmOid(PublicKey key, String signingAlg) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IllegalArgumentException, UnsupportedOperationException, IOException, EncodingFormatException {
+ //find the algorithm name for the signature OID
+ String algo = null;
+ Provider[] provs = Security.getProviders();
+ for (Provider prov : provs) {
+ Service service = prov.getService("Signature",signingAlg);
+ if (service != null) {
+ algo = service.getAlgorithm();
+ }
+ }
+ Signature sig = Signature.getInstance(algo);
+ sig.initVerify(key);
+ sig.update(getDataForSignature());
+ return sig.verify(this.getSignature());
+ }
+
+ /**
+ * Sign the contained data block.
+ *
+ * Note: an appropriate security provider (e.g. BC) must be registered before
+ *
+ * @param key the key
+ * @param singningAlg the Object ID of the signing algorithm
+ * @return
+ * @throws NoSuchAlgorithmException the no such algorithm exception
+ * @throws InvalidKeyException the invalid key exception
+ * @throws SignatureException the signature exception
+ * @throws EncodingFormatException
+ * @throws IOException
+ */
+ public void signByAlgorithmOID(PrivateKey key,String signingAlg) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, IOException, EncodingFormatException {
+ //find the algorithm name for the signature OID
+ String algo = null;
+ Provider[] provs = Security.getProviders();
+ for (Provider prov : provs) {
+ Service service = prov.getService("Signature",signingAlg);
+ if (service != null) {
+ algo = service.getAlgorithm();
+ }
+ }
+ Signature sig = Signature.getInstance(algo);
+ sig.initSign(key);
+ signedData = getDataForSignature();
+ sig.update(signedData);
+ signature = sig.sign();
+ }
+
+ /**
+ * Sign the contained data block.
+ *
+ * Note: an appropriate security provider (e.g. BC) must be registered before
+ *
+ * @param key the key
+ * @param algo the name of the signing algorithm
+ * @return
+ * @throws NoSuchAlgorithmException the no such algorithm exception
+ * @throws InvalidKeyException the invalid key exception
+ * @throws SignatureException the signature exception
+ * @throws EncodingFormatException
+ * @throws IOException
+ */
+ public void signUsingAlgorithmName(PrivateKey key,String algo) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, IOException, EncodingFormatException {
+ Signature sig = Signature.getInstance(algo);
+ sig.initSign(key);
+ sig.update(getDataForSignature());
+ signature = sig.sign();
+ }
+
+
+
+ public UFLEXDataRecord getuFlex() {
+ return uFlex;
+ }
+
+
+
+ public UTLAYDataRecord getuTlay() {
+ return uTlay;
+ }
+
+
+
+ public void setuFlex(UFLEXDataRecord uFlex) {
+ this.uFlex = uFlex;
+ }
+
+
+
+ public void setuTlay(UTLAYDataRecord uTlay) {
+ this.uTlay = uTlay;
+ }
+
+
+
+ public void setHeaderRecord(UHEADDataRecord headerRecord) {
+ this.headerRecord = headerRecord;
+ }
+
+
+
+
+}
diff --git a/src/org/uic/barcode/staticFrame/UFLEXDataRecord.java b/src/org/uic/barcode/staticFrame/UFLEXDataRecord.java
new file mode 100644
index 0000000..9731198
--- /dev/null
+++ b/src/org/uic/barcode/staticFrame/UFLEXDataRecord.java
@@ -0,0 +1,90 @@
+package org.uic.barcode.staticFrame;
+
+import java.io.IOException;
+
+import org.uic.ticket.EncodingFormatException;
+import org.uic.ticket.UicRailTicketCoder;
+import org.uic.ticket.api.spec.IUicRailTicket;
+
+/**
+ * The Class UFLEXDataRecord implements the dara record to hold the data of an ASN.1 PER encoded UIC ticket.
+ */
+public class UFLEXDataRecord extends DataRecord {
+
+
+ /** The ticket. */
+ private IUicRailTicket ticket;
+
+ /**
+ * Instantiates a new UFLEX data record.
+ */
+ public UFLEXDataRecord() {
+ super("U_FLEX");
+ }
+
+ /**
+ * Instantiates a new UFLEX data record.
+ *
+ * @param version the version
+ */
+ public UFLEXDataRecord(String version) {
+ super("U_FLEX", version);
+ }
+
+
+ /**
+ * Gets the ticket.
+ *
+ * @return the ticket
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ public IUicRailTicket getTicket() throws IOException, EncodingFormatException {
+ if (ticket != null) {
+ return ticket;
+ }
+ return null;
+ }
+
+ /**
+ * Sets the ticket.
+ *
+ * @param ticket the new ticket
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ public void setTicket(IUicRailTicket ticket) throws IOException, EncodingFormatException {
+ this.ticket = ticket;
+ super.setData(null);
+ }
+
+
+ /**
+ * Decode content.
+ *
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ @Override
+ protected void decodeContent() throws IOException, EncodingFormatException {
+ UicRailTicketCoder coder = new UicRailTicketCoder();
+ int version = Integer.parseInt(super.getVersionId());
+ this.ticket = coder.decodeFromAsn(content,version);
+ }
+
+ /**
+ * Encode content.
+ *
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ @Override
+ protected void encodeContent() throws IOException, EncodingFormatException {
+ UicRailTicketCoder coder = new UicRailTicketCoder();
+ int version = Integer.parseInt(super.getVersionId());
+ content = coder.encode(ticket, version);
+ }
+
+
+
+}
diff --git a/src/org/uic/barcode/staticFrame/UHEADDataRecord.java b/src/org/uic/barcode/staticFrame/UHEADDataRecord.java
new file mode 100644
index 0000000..e2ec301
--- /dev/null
+++ b/src/org/uic/barcode/staticFrame/UHEADDataRecord.java
@@ -0,0 +1,269 @@
+package org.uic.barcode.staticFrame;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
+import org.uic.ticket.EncodingFormatException;
+
+/**
+ * The Class UHEADDataRecord implements the additional header record of a statis UIC bar code
+ */
+public class UHEADDataRecord extends DataRecord{
+
+
+ /** The issuing date. */
+ private Date issuingDate = null;
+
+ /** The flags. */
+ private int flags;
+
+ /** The issuer. */
+ private String issuer;
+
+ /** The identifier. */
+ private String identifier;
+
+ /** The language. */
+ private String language;
+
+ /** The additional language. */
+ private String additionalLanguage;
+
+ /**
+ * Instantiates a new UHEAD data record.
+ */
+ public UHEADDataRecord() {
+ super("U_HEAD");
+ }
+
+
+ /**
+ * Gets the issuing date.
+ *
+ * @return the issuing date
+ */
+ public Date getIssuingDate() {
+ return issuingDate;
+ }
+
+ /**
+ * Sets the issuing date.
+ *
+ * @param issuingDate the new issuing date
+ */
+ public void setIssuingDate(Date issuingDate) {
+ this.issuingDate = issuingDate;
+ }
+
+ /**
+ * Gets the flags.
+ *
+ * @return the flags
+ */
+ public int getFlags() {
+ return flags;
+ }
+
+ /**
+ * Sets the flags.
+ *
+ * @param flags the new flags
+ */
+ public void setFlags(int flags) {
+ this.flags = flags;
+ }
+
+ /**
+ * Gets the issuer.
+ *
+ * @return the issuer
+ */
+ public String getIssuer() {
+ return issuer;
+ }
+
+ /**
+ * Sets the issuer.
+ *
+ * @param issuer the new issuer
+ */
+ public void setIssuer(String issuer) {
+ this.issuer = issuer;
+ }
+
+ /**
+ * Gets the identifier.
+ *
+ * @return the identifier
+ */
+ public String getIdentifier() {
+ return identifier;
+ }
+
+ /**
+ * Sets the identifier.
+ *
+ * @param identifier the new identifier
+ */
+ public void setIdentifier(String identifier) {
+ this.identifier = identifier;
+ }
+
+ /**
+ * Gets the language.
+ *
+ * @return the language
+ */
+ public String getLanguage() {
+ return language;
+ }
+
+ /**
+ * Sets the language.
+ *
+ * @param language the new language
+ */
+ public void setLanguage(String language) {
+ this.language = language;
+ }
+
+ /**
+ * Gets the additional language.
+ *
+ * @return the additional language
+ */
+ public String getAdditionalLanguage() {
+ return additionalLanguage;
+ }
+
+ /**
+ * Sets the additional language.
+ *
+ * @param additionalLanguage the new additional language
+ */
+ public void setAdditionalLanguage(String additionalLanguage) {
+ this.additionalLanguage = additionalLanguage;
+ }
+
+
+ /**
+ * Decode content.
+ *
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ protected void decodeContent() throws IOException, EncodingFormatException{
+
+ if (content == null || content.length == 0 ) return;
+
+ issuer = decodeString(content, 0 , 4);
+
+ identifier = decodeString(content, 4 , 20);
+
+ String issuingDateString = decodeString(content, 24 , 12);
+
+ String flagsString = decodeString(content,36 , 1);
+
+ language = decodeString(content, 37 , 2);
+
+ additionalLanguage = decodeString(content,39 , 2);
+
+
+ try {
+ flags = Integer.parseInt(flagsString);
+ } catch (Exception e) {
+ flags = 9;
+ }
+
+ // date format "DDMMYYYYHHMM"
+ SimpleDateFormat formatter = new SimpleDateFormat("ddMMyyyyhhmm");
+ try {
+ issuingDate = formatter.parse(issuingDateString);
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+
+ /**
+ * Decode string.
+ *
+ * @param byteData the byte data
+ * @param offset the offset
+ * @param length the length
+ * @return the string
+ */
+ private String decodeString(byte[] byteData, int offset, int length) {
+
+ char[] chars = new char[length];
+
+ for (int i = 0; i < length && i < byteData.length;i++) {
+ byte byteValue = byteData[offset + i];
+ if (byteValue == '\n') {
+ byteValue = ' ';
+ }
+ chars[i] = (char) byteValue;
+ }
+
+ return String.copyValueOf(chars);
+ }
+
+ /**
+ * Encode content.
+ *
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ protected void encodeContent() throws IOException, EncodingFormatException {
+
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+
+ String issuerElement = String.format("%4s", this.issuer);
+
+ String idElement = String.format("%20s", this.identifier);
+
+ //DDMMYYYYHHMM"
+ Calendar now = Calendar.getInstance();
+
+ // issuing date can be in the ticket or in the header
+ if (this.issuingDate != null) {
+ now.setTime(this.issuingDate);
+ }
+
+ String timeElement = String.format("%02d%02d%04d%02d%02d",
+ now.get(Calendar.DAY_OF_MONTH),
+ now.get(Calendar.MONTH),
+ now.get(Calendar.YEAR),
+ now.get(Calendar.HOUR),
+ now.get(Calendar.MINUTE));
+
+
+ String flagsElement = String.format("%01d",this.flags);
+
+
+ String languageElement = String.format("%2s%2s" ,this.language, this.additionalLanguage);
+
+ try {
+
+ outputStream.write(issuerElement.getBytes());
+ outputStream.write(idElement.getBytes());
+ outputStream.write(timeElement.getBytes());
+
+ outputStream.write(flagsElement.getBytes());
+ outputStream.write(languageElement.getBytes());
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ super.setData(outputStream.toByteArray());
+
+
+ }
+
+}
diff --git a/src/org/uic/barcode/staticFrame/UTLAYDataRecord.java b/src/org/uic/barcode/staticFrame/UTLAYDataRecord.java
new file mode 100644
index 0000000..2e9a2dc
--- /dev/null
+++ b/src/org/uic/barcode/staticFrame/UTLAYDataRecord.java
@@ -0,0 +1,272 @@
+/*
+ *
+ */
+package org.uic.barcode.staticFrame;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+import org.uic.barcode.staticHeader.ticketLayoutBarcode.FormatType;
+import org.uic.barcode.staticHeader.ticketLayoutBarcode.LayoutElement;
+import org.uic.barcode.staticHeader.ticketLayoutBarcode.TicketLayout;
+import org.uic.ticket.EncodingFormatException;
+
+/**
+ * The Class UTLAYDataRecord implements a bar code data record containing the ticket layout.
+ */
+public class UTLAYDataRecord extends DataRecord {
+
+ /** The ticket layout. */
+ private TicketLayout layout;
+
+ /**
+ * Instantiates a new empty UTLAY data record.
+ */
+ public UTLAYDataRecord() {
+ super("U_TLAY","01");
+ }
+
+ /**
+ * Decode utf-8 string.
+ *
+ * @param byteData the byte data
+ * @param offset the offset
+ * @param length the length
+ * @return the string
+ * @throws UnsupportedEncodingException the unsupported encoding exception
+ */
+ private static String decodeUtf8String(byte[] byteData, int offset, int length) throws UnsupportedEncodingException {
+ byte[] bytes = new byte[length];
+ for (int i = 0; i < length; i++){
+ bytes[i] = byteData[i + offset];
+ }
+ return StandardCharsets.UTF_8.decode(ByteBuffer.wrap(bytes)).toString();
+ }
+
+ /**
+ * Decode string.
+ *
+ * @param byteData the byte data
+ * @param offset the offset
+ * @param length the length
+ * @return the string
+ */
+ private static String decodeString(byte[] byteData, int offset, int length) {
+ byte[] bytes = new byte[length];
+ for (int i = 0; i < length; i++){
+ bytes[i] = byteData[i + offset];
+ }
+ return StandardCharsets.ISO_8859_1.decode(ByteBuffer.wrap(bytes)).toString();
+ }
+
+ /**
+ * Encode utf-8.
+ *
+ * @param value the value
+ * @return the byte[]
+ */
+ private static byte[] encodeUtf8(String value) {
+
+ try {
+ return value.getBytes("UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException("UTF8 String encoding wrong!",e);
+ }
+ }
+
+ /**
+ * To string.
+ *
+ * @return the string
+ */
+ public String toString() {
+
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("TLB: ").append(layout.getLayoutStandard()).append('\n');
+
+ for (LayoutElement e : layout.getElements()){
+ sb.append("column: ").append(e.getColumn()).append(" - ");
+ sb.append("line: ").append(e.getLine()).append(" - ");
+ sb.append("width: ").append(e.getWidth()).append(" - ");
+ sb.append("heigth: ").append(e.getHeight()).append(" - ");
+ sb.append("text: ").append(e.getText()).append(" - ");
+ sb.append("format: ").append(e.getFormat().toString()).append('\n');
+ }
+
+ return sb.toString();
+
+ }
+
+ /**
+ * Decode content.
+ *
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ @Override
+ protected void decodeContent() throws IOException, EncodingFormatException {
+
+ layout = new TicketLayout();
+
+ if (content == null || content.length == 0 ) return;
+
+ int offset = 0;
+
+ String layoutType = decodeString(content, offset , 4);
+ layout.setLayoutStandard(layoutType);
+ offset = offset + 4;
+
+ String numberValue = decodeString(content, offset , 4);
+ offset = offset + 4;
+
+ int elements = 0;
+ try {
+ elements = Integer.parseInt(numberValue);
+ } catch(NumberFormatException e){
+ //Do Nothing
+ }
+
+ int remainingBytes = content.length - offset;
+
+ for (int i = 0; i < elements && remainingBytes > 0 ;i++){
+
+ String lineValue = decodeString(content, offset , 2);
+ offset = offset + 2;
+ int line = 0;
+ try {
+ line = Integer.parseInt(lineValue);
+ } catch(NumberFormatException e){
+ //Do Nothing
+ }
+ String columnValue = decodeString(content, offset , 2);
+ offset = offset + 2;
+ int column = 0;
+ try {
+ column = Integer.parseInt(columnValue);
+ } catch(NumberFormatException e){
+ //Do Nothing
+ }
+ String heightValue = decodeString(content, offset , 2);
+ offset = offset + 2;
+ int height = 0;
+ try {
+ height = Integer.parseInt(heightValue);
+ } catch(NumberFormatException e){
+ //Do Nothing
+ }
+ String widthValue = decodeString(content, offset , 2);
+ offset = offset + 2;
+ int width = 0;
+ try {
+ width = Integer.parseInt(widthValue);
+ } catch(NumberFormatException e){
+ //Do Nothing
+ }
+ String formatValue = decodeString(content, offset , 1);
+ offset = offset + 1;
+ int format = 0;
+ try {
+ format = Integer.parseInt(formatValue);
+ } catch(NumberFormatException e){
+ //Do Nothing
+ }
+ String lengthValue = decodeString(content, offset , 4);
+ offset = offset + 4;
+ int length = 0;
+ try {
+ length = Integer.parseInt(lengthValue);
+ } catch(NumberFormatException e){
+ //Do Nothing
+ }
+
+ String text;
+ try {
+ text = decodeUtf8String(content, offset ,length);
+ } catch (UnsupportedEncodingException e) {
+ text = "unsupported character set";
+ }
+ offset = offset + length;
+
+ LayoutElement layoutElement = new LayoutElement();
+
+ layoutElement.setColumn(column);
+ layoutElement.setLine(line);
+ layoutElement.setHeight(height);
+ layoutElement.setWidth(width);
+ layoutElement.setText(text);
+
+ layoutElement.setFormat(FormatType.values()[format]);
+
+ layout.addLayoutElement(layoutElement);
+
+ }
+
+ }
+
+ /**
+ * Encode content.
+ *
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws EncodingFormatException the encoding format exception
+ */
+ @Override
+ protected void encodeContent() throws IOException, EncodingFormatException {
+
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+
+ if (layout == null || layout.getElements() == null || layout.getElements().isEmpty()) {
+ return;
+ }
+
+ //number of text elements
+ String numberOfFields = String.format("%04d",layout.getElements().size());
+
+ outputStream.write(layout.getLayoutStandard().getBytes());
+ outputStream.write(numberOfFields.getBytes());
+
+ for (LayoutElement e : layout.getElements()){
+
+ String line = String.format("%02d",e.getLine());
+ String column = String.format("%02d",e.getColumn());
+ String heigth = String.format("%02d",e.getHeight());
+ String width = String.format("%02d",e.getWidth());
+ String format = String.format("%01d",e.getFormat().ordinal());
+ String size = String.format("%04d",encodeUtf8(e.getText()).length);
+
+ outputStream.write(line.getBytes());
+ outputStream.write(column.getBytes());
+ outputStream.write(heigth.getBytes());
+ outputStream.write(width.getBytes());
+ outputStream.write(format.getBytes());
+ outputStream.write(size.getBytes());
+ outputStream.write(encodeUtf8(e.getText()));
+
+ }
+
+ content = outputStream.toByteArray();
+ }
+
+ /**
+ * Sets the layout.
+ *
+ * @param layout the new layout
+ */
+ public void setLayout(TicketLayout layout) {
+ this.layout = layout;
+ }
+
+
+ /**
+ * Gets the layout.
+ *
+ * @return the layout
+ */
+ public TicketLayout getLayout() {
+ return layout;
+ }
+
+}
diff --git a/src/org/uic/barcode/staticFrame/package.html b/src/org/uic/barcode/staticFrame/package.html
new file mode 100644
index 0000000..b76540b
--- /dev/null
+++ b/src/org/uic/barcode/staticFrame/package.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head></head>
+<body>
+
+<h1>static bar code header frame</h1>
+
+<p>Provides an implementation of the static bar code header frame specified in UIC IRS 90918-9 including:<br/><br/>
+
+<ul>
+ <li> encoding of the data for creating a bar code</li>
+ <li> decoding of data from bar code content</li>
+ <li> support for the additional header information required with the TLB content</li>
+ <li> support for the TLB bar code content</li>
+ <li> support for the FCB content (using the UIC FCB implementation)</li>
+ <li> support for bilaterally on unilaterally defined additional content </li>
+</ul>
+</p>
+
+</body>
+</html> \ No newline at end of file
diff --git a/src/org/uic/barcode/staticHeader/ticketLayoutBarcode/FormatType.java b/src/org/uic/barcode/staticHeader/ticketLayoutBarcode/FormatType.java
new file mode 100644
index 0000000..c5cc39e
--- /dev/null
+++ b/src/org/uic/barcode/staticHeader/ticketLayoutBarcode/FormatType.java
@@ -0,0 +1,34 @@
+package org.uic.barcode.staticHeader.ticketLayoutBarcode;
+
+
+public enum FormatType {
+ NORMAL("NORMAL"),
+ BOLD("BOLD"),
+ ITALIC("ITALIC"),
+ BOLDITALIC("BOLDITALIC"),
+ SMALL("SMALL"),
+ SMALLBOLD("SMALLBOLD"),
+ SMALLITALIC("SMALLITALIC"),
+ SMALLBOLDITALIC("SMALLBOLDITALIC");
+
+
+
+ public String text;
+
+ FormatType(String text) {
+ this.text = text;
+ }
+
+ public static FormatType getFormatType(int i) {
+ try {
+ return FormatType.values()[i];
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ public String toString(){
+ return text;
+ }
+
+}
diff --git a/src/org/uic/barcode/staticHeader/ticketLayoutBarcode/LayoutElement.java b/src/org/uic/barcode/staticHeader/ticketLayoutBarcode/LayoutElement.java
new file mode 100644
index 0000000..04593f2
--- /dev/null
+++ b/src/org/uic/barcode/staticHeader/ticketLayoutBarcode/LayoutElement.java
@@ -0,0 +1,50 @@
+package org.uic.barcode.staticHeader.ticketLayoutBarcode;
+
+public class LayoutElement {
+
+ private int column;
+ private int line;
+ private int height;
+ private int width;
+ private String text;
+ private FormatType format = FormatType.NORMAL;
+
+ public int getColumn() {
+ return column;
+ }
+ public void setColumn(int column) {
+ this.column = column;
+ }
+ public int getLine() {
+ return line;
+ }
+ public void setLine(int line) {
+ this.line = line;
+ }
+ public int getHeight() {
+ return height;
+ }
+ public void setHeight(int height) {
+ this.height = height;
+ }
+ public int getWidth() {
+ return width;
+ }
+ public void setWidth(int width) {
+ this.width = width;
+ }
+ public String getText() {
+ return text;
+ }
+ public void setText(String text) {
+ this.text = text;
+ }
+ public FormatType getFormat() {
+ return format;
+ }
+ public void setFormat(FormatType format) {
+ this.format = format;
+ }
+
+
+}
diff --git a/src/org/uic/barcode/staticHeader/ticketLayoutBarcode/TicketLayout.java b/src/org/uic/barcode/staticHeader/ticketLayoutBarcode/TicketLayout.java
new file mode 100644
index 0000000..e788f9a
--- /dev/null
+++ b/src/org/uic/barcode/staticHeader/ticketLayoutBarcode/TicketLayout.java
@@ -0,0 +1,61 @@
+package org.uic.barcode.staticHeader.ticketLayoutBarcode;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TicketLayout {
+
+ private String layoutStandard = "RCT2";
+
+ /** The layout elements. */
+ private List<LayoutElement> elements = new ArrayList<LayoutElement>();
+
+
+ /**
+ * Gets the layout standard.
+ *
+ * @return the layout standard
+ */
+ public String getLayoutStandard() {
+ if (layoutStandard == null || layoutStandard.length() != 4) {
+ layoutStandard = "RCT2";
+ }
+ return layoutStandard;
+ }
+
+ /**
+ * Sets the layout standard.
+ *
+ * @param layoutStandard the new layout standard
+ */
+ public void setLayoutStandard(String layoutStandard) {
+ this.layoutStandard = layoutStandard;
+ }
+
+ /**
+ * Adds the layout element.
+ *
+ * @param element the element
+ */
+ public void addLayoutElement(LayoutElement element){
+ elements.add(element);
+ }
+
+ /**
+ * Removes the layout elements.
+ */
+ public void removeLayoutElements(){
+ elements.clear();
+ }
+
+ /**
+ * Gets the elements.
+ *
+ * @return the elements
+ */
+ public List<LayoutElement> getElements(){
+ return elements;
+ }
+
+
+}
diff --git a/src/org/uic/barcode/test/DynamicFrameDoubleSignatureTest.java b/src/org/uic/barcode/test/DynamicFrameDoubleSignatureTest.java
new file mode 100644
index 0000000..f74c40f
--- /dev/null
+++ b/src/org/uic/barcode/test/DynamicFrameDoubleSignatureTest.java
@@ -0,0 +1,212 @@
+package org.uic.barcode.test;
+
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.SignatureException;
+import java.util.Arrays;
+import java.util.zip.DataFormatException;
+
+import org.bouncycastle.jce.ECNamedCurveTable;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.jce.spec.ECParameterSpec;
+import org.junit.Before;
+import org.junit.Test;
+import org.uic.barcode.Decoder;
+import org.uic.barcode.Encoder;
+import org.uic.barcode.dynamicFrame.Constants;
+import org.uic.barcode.dynamicFrame.DataType;
+import org.uic.barcode.test.utils.Level2TestDataFactory;
+import org.uic.barcode.test.utils.SimpleUICTestTicket;
+import org.uic.barcode.utils.AlgorithmNameResolver;
+import org.uic.ticket.EncodingFormatException;
+import org.uic.ticket.api.spec.IUicRailTicket;
+
+public class DynamicFrameDoubleSignatureTest {
+
+ public String signatureAlgorithmOID = null;
+ public String elipticCurve = null;
+ public String keyPairAlgorithmOID = null;
+
+ public KeyPair keyPairLevel1 = null;
+ public KeyPair keyPairLevel2 = null;
+
+ public IUicRailTicket testFCBticket = null;
+
+
+ @Before public void initialize() {
+
+ signatureAlgorithmOID = Constants.ECDSA_SHA256;
+ keyPairAlgorithmOID = Constants.KG_EC_256;
+ elipticCurve = "secp256k1";
+
+ testFCBticket = SimpleUICTestTicket.getUicTestTicket();
+
+ Security.addProvider(new BouncyCastleProvider());
+
+ try {
+ keyPairLevel1 = generateECKeys(keyPairAlgorithmOID, elipticCurve);
+ keyPairLevel2 = generateECKeys(keyPairAlgorithmOID, elipticCurve);
+ } catch (Exception e) {
+ assert(false);
+ }
+
+ assert(keyPairLevel1 != null);
+
+ }
+
+
+ @Test public void testDynamicHeaderBarcodeEncoding() {
+
+ IUicRailTicket ticket = testFCBticket;
+
+ Encoder enc = null;
+
+ try {
+ enc = new Encoder(ticket, null, Encoder.UIC_BARCODE_TYPE_DOSIPAS, 1, 1);
+ } catch (IOException | EncodingFormatException e1) {
+ assert(false);
+ }
+
+ assert(enc != null);
+
+ try {
+ enc.setLevel1Algs(signatureAlgorithmOID, keyPairAlgorithmOID);
+ enc.setLevel2Algs(signatureAlgorithmOID, keyPairAlgorithmOID,keyPairLevel2.getPublic());
+ enc.signLevel1("1080", keyPairLevel1.getPrivate(), signatureAlgorithmOID, "1");
+ } catch (Exception e) {
+ assert(false);
+ }
+
+ assert(enc != null);
+
+ try {
+ enc.setLevel2Data(Level2TestDataFactory.getLevel2SimpleTestData());
+ enc.signLevel2(keyPairLevel2.getPrivate());
+ } catch (Exception e) {
+ assert(false);
+ }
+
+
+ byte[] encoded = null;
+ try {
+ encoded = enc.encode();
+ } catch (Exception e) {
+ assert(false);
+ }
+
+ assert(encoded != null);
+
+
+
+ }
+
+ @Test public void testDynamicHeaderBarcodeDecoding() {
+
+ IUicRailTicket ticket = testFCBticket;
+
+ Encoder enc = null;
+
+ try {
+ enc = new Encoder(ticket, null, Encoder.UIC_BARCODE_TYPE_DOSIPAS, 1, 1);
+ } catch (IOException | EncodingFormatException e1) {
+ assert(false);
+ }
+
+ assert(enc != null);
+
+ try {
+ enc.setLevel1Algs(signatureAlgorithmOID, keyPairAlgorithmOID);
+ enc.setLevel2Algs(signatureAlgorithmOID, keyPairAlgorithmOID,keyPairLevel2.getPublic());
+ enc.signLevel1("1080", keyPairLevel1.getPrivate(), signatureAlgorithmOID, "1");
+ } catch (Exception e) {
+ assert(false);
+ }
+
+ assert(enc != null);
+
+
+ DataType level2Data = Level2TestDataFactory.getLevel2SimpleTestData();
+ try {
+ enc.setLevel2Data(level2Data);
+ enc.signLevel2(keyPairLevel2.getPrivate());
+ } catch (Exception e) {
+ assert(false);
+ }
+
+
+ byte[] encoded = null;
+ try {
+ encoded = enc.encode();
+ } catch (Exception e) {
+ assert(false);
+ }
+
+ assert(encoded != null);
+
+ Decoder dec = null;
+ try {
+ dec = new Decoder(encoded);
+ } catch (IOException e) {
+ assert(false);
+ } catch (EncodingFormatException e) {
+ assert(false);
+ } catch (DataFormatException e) {
+ assert(false);
+ }
+ assert(dec != null);
+
+ int signatureCheck = 0;
+ try {
+ signatureCheck = dec.validateLevel1(keyPairLevel1.getPublic(),null);
+ } catch (InvalidKeyException | NoSuchAlgorithmException | SignatureException | IllegalArgumentException
+ | UnsupportedOperationException | IOException | EncodingFormatException e) {
+ assert(false);
+ }
+ assert(signatureCheck == Constants.LEVEL1_VALIDATION_OK);
+
+ signatureCheck = 0;
+ try {
+ signatureCheck = dec.validateLevel2();
+ } catch (IllegalArgumentException | UnsupportedOperationException e) {
+ assert(false);
+ }
+ assert(signatureCheck == Constants.LEVEL2_VALIDATION_OK);
+
+
+ DataType level2DataDec = dec.getLevel2Data();
+
+ assert(level2Data.getFormat().equals(level2DataDec.getFormat()));
+ assert(Arrays.equals(level2Data.getData().toByteArray(),level2DataDec.getData().toByteArray()));
+
+ SimpleUICTestTicket.compare(ticket, dec.getUicTicket());
+
+
+ }
+
+ public KeyPair generateECDSAKeys(String keyAlgorithmName, String paramName) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException{
+ ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec(paramName);
+ KeyPairGenerator g = KeyPairGenerator.getInstance(keyAlgorithmName, "BC");
+ g.initialize(ecSpec, new SecureRandom());
+ return g.generateKeyPair();
+ }
+
+ public KeyPair generateECKeys(String keyAlgorithmOid, String curve) throws Exception{
+
+ String keyAlgorithmName = AlgorithmNameResolver.getName(AlgorithmNameResolver.TYPE_KEY_GENERATOR_ALG, keyAlgorithmOid, "BC");
+
+ keyAlgorithmName = "ECDSA";
+ ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec(curve);
+ KeyPairGenerator g = KeyPairGenerator.getInstance(keyAlgorithmName, "BC");
+ g.initialize(ecSpec, new SecureRandom());
+ return g.generateKeyPair();
+ }
+
+
+}
diff --git a/src/org/uic/barcode/test/DynamicFrameLOwLevelTest.java b/src/org/uic/barcode/test/DynamicFrameLOwLevelTest.java
new file mode 100644
index 0000000..d3148b2
--- /dev/null
+++ b/src/org/uic/barcode/test/DynamicFrameLOwLevelTest.java
@@ -0,0 +1,116 @@
+package org.uic.barcode.test;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+import org.bouncycastle.jce.ECNamedCurveTable;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.jce.spec.ECParameterSpec;
+import org.junit.Before;
+import org.junit.Test;
+import org.uic.barcode.dynamicFrame.Constants;
+import org.uic.barcode.dynamicFrame.DynamicFrame;
+import org.uic.barcode.test.utils.SimpleDynamicFrameTestBarcode;
+
+import net.gcdc.asn1.uper.UperEncoder;
+
+public class DynamicFrameLOwLevelTest {
+
+ public String algorithmOID = Constants.ECDSA_SHA256;
+ public KeyPair keyPair = null;
+ public String publicKeyHex = "3081A7301006072A8648CE3D020106052B81040027038192000405B2797BB27F96EC3769B81E563EEB97A4CE3B5BB4EE19BC90A3F496B805644AA042736E5FA9B3A5FBEA5B01CD1D9EC13C009F9655B31FFF9AA52AC6D90B5D6220B58853A1D18BF20779BE5C52356AE70B19242065F82B76961E2A079F42CA9A41A1AB4D5518446AC3721953AE6323C60E15389498DE5F592A24DDDA45F736D93695C797C0F28A712EC25B9CD8078457";
+ public String privateKeyHex = "30820109020100301006072A8648CE3D020106052B810400270481F13081EE020101044801EF44914319A5DD528C06419D7B0FD0CDD7F62A231BEB197E45A0074C02E11FE82ABAD916BE94FD8256260AA9191F19241CFC7E372B3A4E0ADA06CCA51678C54198667DFC9B0DA8A00706052B81040027A18195038192000405B2797BB27F96EC3769B81E563EEB97A4CE3B5BB4EE19BC90A3F496B805644AA042736E5FA9B3A5FBEA5B01CD1D9EC13C009F9655B31FFF9AA52AC6D90B5D6220B58853A1D18BF20779BE5C52356AE70B19242065F82B76961E2A079F42CA9A41A1AB4D5518446AC3721953AE6323C60E15389498DE5F592A24DDDA45F736D93695C797C0F28A712EC25B9CD8078457";
+
+ public String hexReferenceContent = "400EAC986010DF80A021DE008808014374F3E7D72F2A9979F4A13A90086280B40020894DED0DC0688DEEE0AC593368D60220DCF2EED3BF903B6BCA3B937BAB801280EB44AC0505B4200000000100E6F70656E5469636B6574496E666F120220103B830B9B9B0B3B28084A0B6B9BA32B93230B680202F698040100B20004C6C8020404E9979F40201620505B402C80A0F68020AA192338F4100C08008097308194024800DA0C61105BAD7E13ADF9D5A00CBC47732865EA67E8371A5FBE38B4FABBBABD37459D12048DA6664700E787C32962A607A784FD2FC669A9A8EC9F91CD53AF2B922EFECE24FF3D68024800A1F7CF1C0625FB19CF089E74D668F5E8C15179BEF7BA79D9D169A12FA47F6340ED50BADB57CD83110060FEC08B1EF978C6DB08A172B0DE20C442D4507442623A74A624457590040";
+
+
+ @Before public void initialize() {
+
+ Security.addProvider(new BouncyCastleProvider());
+
+ try {
+ keyPair = generateECDSAKeys();
+ } catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
+ e.printStackTrace();
+ }
+ privateKeyHex = UperEncoder.hexStringFromBytes(keyPair.getPrivate().getEncoded());
+ publicKeyHex = UperEncoder.hexStringFromBytes(keyPair.getPublic().getEncoded());
+
+ assert(keyPair != null);
+
+ try {
+ PublicKey publicKey = KeyFactory.getInstance("ECDSA").generatePublic(new X509EncodedKeySpec(UperEncoder.bytesFromHexString(publicKeyHex)));
+ PrivateKey privateKey = KeyFactory.getInstance("ECDSA").generatePrivate(new PKCS8EncodedKeySpec(UperEncoder.bytesFromHexString(privateKeyHex)));
+ keyPair = new KeyPair(publicKey,privateKey);
+ } catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ }
+ assert(keyPair != null);
+
+ }
+
+
+ @Test public void testDynamicHeaderBarcodeEncoding() {
+
+ DynamicFrame barcode1 = SimpleDynamicFrameTestBarcode.getSimpleDynamicHeaderBarcode(algorithmOID, keyPair);
+
+ byte[] encoded = barcode1.encode();
+
+ //String hex = UperEncoder.hexStringFromBytes(encoded);
+
+ /*
+ * check the available implementations
+ String s = null;
+ try {
+ s = AlgorithmNameResolver.getSecurityNames();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ */
+
+
+ //note: hex is different each time due to the random seed in the signature
+ assert(encoded != null);
+
+ }
+
+ @Test public void testDynamicHeaderBarcodeDecoding() {
+
+ DynamicFrame barcode1 = SimpleDynamicFrameTestBarcode.getSimpleDynamicHeaderBarcode(algorithmOID, keyPair);
+
+ byte[] encoded = barcode1.encode();
+
+ DynamicFrame barcode = DynamicFrame.decode(encoded);
+
+ int signatureCheck = barcode.validateLevel1(keyPair.getPublic());
+
+ assert(signatureCheck == Constants.LEVEL1_VALIDATION_OK);
+
+ SimpleDynamicFrameTestBarcode.compareFrame(barcode1, barcode);
+
+
+
+
+ }
+
+ public KeyPair generateECDSAKeys() throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException{
+ ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("B-571");
+ KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", "BC");
+ g.initialize(ecSpec, new SecureRandom());
+ return g.generateKeyPair();
+ }
+
+
+
+}
diff --git a/src/org/uic/barcode/test/DynamicFrameSimpleTest.java b/src/org/uic/barcode/test/DynamicFrameSimpleTest.java
new file mode 100644
index 0000000..72b137e
--- /dev/null
+++ b/src/org/uic/barcode/test/DynamicFrameSimpleTest.java
@@ -0,0 +1,171 @@
+package org.uic.barcode.test;
+
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.SignatureException;
+import java.util.zip.DataFormatException;
+
+import org.bouncycastle.jce.ECNamedCurveTable;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.jce.spec.ECParameterSpec;
+import org.junit.Before;
+import org.junit.Test;
+import org.uic.barcode.Decoder;
+import org.uic.barcode.Encoder;
+import org.uic.barcode.dynamicFrame.Constants;
+import org.uic.barcode.test.utils.SimpleUICTestTicket;
+import org.uic.barcode.utils.AlgorithmNameResolver;
+import org.uic.ticket.EncodingFormatException;
+import org.uic.ticket.api.spec.IUicRailTicket;
+
+public class DynamicFrameSimpleTest {
+
+ public String signatureAlgorithmOID = null;
+ public String elipticCurve = null;
+ public String keyPairAlgorithmOID = null;
+
+ public KeyPair keyPair = null;
+
+ public IUicRailTicket testFCBticket = null;
+
+
+ @Before public void initialize() {
+
+ signatureAlgorithmOID = Constants.ECDSA_SHA256;
+ keyPairAlgorithmOID = Constants.KG_EC_256;
+ elipticCurve = "secp256k1";
+
+ testFCBticket = SimpleUICTestTicket.getUicTestTicket();
+
+ Security.addProvider(new BouncyCastleProvider());
+
+ try {
+ keyPair = generateECKeys(Constants.KG_EC, elipticCurve);
+ //keyPair = generateECDSAKeys("ECDSA", "B-571");
+ } catch (Exception e) {
+ assert(false);
+ }
+
+ assert(keyPair != null);
+
+ }
+
+
+ @Test public void testDynamicHeaderBarcodeEncoding() {
+
+ IUicRailTicket ticket = testFCBticket;
+
+ Encoder enc = null;
+
+ try {
+ enc = new Encoder(ticket, null, Encoder.UIC_BARCODE_TYPE_DOSIPAS, 1, 1);
+ } catch (IOException | EncodingFormatException e1) {
+ assert(false);
+ }
+
+ assert(enc != null);
+
+ try {
+ enc.signLevel1("1080", keyPair.getPrivate(), signatureAlgorithmOID, "1");
+ } catch (Exception e) {
+ assert(false);
+ }
+
+
+ byte[] encoded = null;
+ try {
+ encoded = enc.encode();
+ } catch (Exception e) {
+ assert(false);
+ }
+
+ assert(encoded != null);
+
+
+
+ }
+
+ @Test public void testDynamicHeaderBarcodeDecoding() {
+
+ IUicRailTicket ticket = testFCBticket;
+
+ Encoder enc = null;
+
+ try {
+ enc = new Encoder(ticket, null, Encoder.UIC_BARCODE_TYPE_DOSIPAS, 1, 1);
+ } catch (IOException | EncodingFormatException e1) {
+ assert(false);
+ }
+
+ assert(enc != null);
+
+ try {
+ enc.signLevel1("1080", keyPair.getPrivate(), signatureAlgorithmOID, "1");
+ } catch (Exception e) {
+ assert(false);
+ }
+
+
+ byte[] encoded = null;
+ try {
+ encoded = enc.encode();
+ } catch (Exception e) {
+ assert(false);
+ }
+
+ assert(encoded != null);
+
+ Decoder dec = null;
+ try {
+ dec = new Decoder(encoded);
+ } catch (IOException e) {
+ assert(false);
+ } catch (EncodingFormatException e) {
+ assert(false);
+ } catch (DataFormatException e) {
+ assert(false);
+ }
+ assert(dec != null);
+
+ int signatureCheck = 0;
+ try {
+ signatureCheck = dec.validateLevel1(keyPair.getPublic(),null);
+ } catch (InvalidKeyException | NoSuchAlgorithmException | SignatureException | IllegalArgumentException
+ | UnsupportedOperationException | IOException | EncodingFormatException e) {
+ assert(false);
+ }
+
+ assert(signatureCheck == Constants.LEVEL1_VALIDATION_OK);
+
+ SimpleUICTestTicket.compare(ticket, dec.getUicTicket());
+
+
+ }
+
+ public KeyPair generateECDSAKeys(String keyAlgorithmName, String paramName) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException{
+ ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec(paramName);
+ KeyPairGenerator g = KeyPairGenerator.getInstance(keyAlgorithmName, "BC");
+ g.initialize(ecSpec, new SecureRandom());
+ return g.generateKeyPair();
+ }
+
+ public KeyPair generateECKeys(String keyAlgorithmOid, String curve) throws Exception{
+
+ String keyAlgorithmName = AlgorithmNameResolver.getName(AlgorithmNameResolver.TYPE_KEY_GENERATOR_ALG, keyAlgorithmOid, "BC");
+
+ keyAlgorithmName = "ECDSA";
+ ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec(curve);
+ KeyPairGenerator g = KeyPairGenerator.getInstance(keyAlgorithmName, "BC");
+ g.initialize(ecSpec, new SecureRandom());
+ return g.generateKeyPair();
+ }
+
+
+}
diff --git a/src/org/uic/barcode/test/SignatureSplitTest.java b/src/org/uic/barcode/test/SignatureSplitTest.java
new file mode 100644
index 0000000..2e51525
--- /dev/null
+++ b/src/org/uic/barcode/test/SignatureSplitTest.java
@@ -0,0 +1,76 @@
+package org.uic.barcode.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import org.junit.Test;
+import org.uic.barcode.staticFrame.StaticFrame;
+import org.uic.barcode.test.utils.TestUtils;
+import org.uic.ticket.EncodingFormatException;
+
+public class SignatureSplitTest {
+
+
+ /*
+ *
+ *
+ * World-Schema DEFINITIONS AUTOMATIC TAGS ::=
+ BEGIN
+ Signature ::= SEQUENCE OF INTEGER
+ END
+
+
+ value Signature ::= {
+ 340282366920938463,
+ 134515671861986
+ }
+
+
+ Encoding using DER encoding rule
+ Signature SEQUENCE OF: tag = [UNIVERSAL 16] constructed; length = 18
+ INTEGER: tag = [UNIVERSAL 2] primitive; length = 8
+ 340282366920938463
+ INTEGER: tag = [UNIVERSAL 2] primitive; length = 6
+ 134515671861986
+ Encoded successfully in 20 bytes: 30120208 04B8ED02 83A6D3DF 02067A57 5ED68AE2
+
+
+ *
+ *
+ */
+
+
+ @Test public void testSplitSignature() throws IOException, EncodingFormatException{
+
+ BigInteger i1 = BigInteger.valueOf(340282366920938463L);
+ BigInteger i2 = BigInteger.valueOf(134515671861986L);
+
+ byte[] encoded = StaticFrame.encodeSignatureIntegerSequence(i1,i2);
+
+
+ String hex = TestUtils.hexStringFromBytes(encoded);
+
+ assertEquals(hex,"3012020804B8ED0283A6D3DF02067A575ED68AE2");
+
+ BigInteger[] ints = null;
+ try {
+ ints = StaticFrame.decodeSignatureIntegerSequence(encoded);
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ assert(ints != null);
+
+ assert(ints.length == 2);
+
+ assert(i1.equals(ints[0]));
+
+ assert(i2.equals(ints[1]));
+
+ }
+
+
+} \ No newline at end of file
diff --git a/src/org/uic/barcode/test/StaticFrameBarcodeTest.java b/src/org/uic/barcode/test/StaticFrameBarcodeTest.java
new file mode 100644
index 0000000..a0855de
--- /dev/null
+++ b/src/org/uic/barcode/test/StaticFrameBarcodeTest.java
@@ -0,0 +1,193 @@
+package org.uic.barcode.test;
+
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.SignatureException;
+import java.util.zip.DataFormatException;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.junit.Before;
+import org.junit.Test;
+import org.uic.barcode.Decoder;
+import org.uic.barcode.Encoder;
+import org.uic.barcode.dynamicFrame.Constants;
+import org.uic.barcode.staticHeader.ticketLayoutBarcode.TicketLayout;
+import org.uic.barcode.test.utils.SimpleTestTicketLayout;
+import org.uic.barcode.test.utils.SimpleUICTestTicket;
+import org.uic.ticket.EncodingFormatException;
+import org.uic.ticket.api.spec.IUicRailTicket;
+
+/**
+ * The Class StaticFrameBarcodeTest.
+ */
+public class StaticFrameBarcodeTest {
+
+ /** The algorithm OID. */
+ public String algorithmOID = Constants.DSA_SHA224;
+
+ public int keySize = 2048;
+
+ /** The key pair. */
+ public KeyPair keyPair = null;
+
+
+ public IUicRailTicket testFCBticket = null;
+
+ public TicketLayout testLayout = null;
+
+
+ /**
+ * Initialize.
+ *
+ * set the signature algorithm
+ * generate a key pair
+ * set the test content
+ * for ticket and layout
+ */
+ @Before public void initialize() {
+
+ algorithmOID = Constants.DSA_SHA224;
+ keySize = 2048;
+ testFCBticket = SimpleUICTestTicket.getUicTestTicket();
+ testLayout = SimpleTestTicketLayout.getSimpleTestTicketLayout();
+
+ Security.addProvider(new BouncyCastleProvider());
+
+ try {
+ keyPair = generateDSAKeys(keySize);
+ } catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
+ e.printStackTrace();
+ }
+
+ assert(keyPair != null);
+
+ }
+
+
+ /**
+ * Test dynamic header barcode encoding.
+ */
+ @Test public void testDynamicHeaderBarcodeEncoding() {
+
+ IUicRailTicket ticket = testFCBticket;
+
+ TicketLayout layout = testLayout;
+
+ Encoder enc = null;
+
+ try {
+ enc = new Encoder(ticket, layout, Encoder.UIC_BARCODE_TYPE_CLASSIC, 2, 1);
+ } catch (IOException | EncodingFormatException e1) {
+ assert(false);
+ }
+
+ enc.setStaticHeaderParams("123456789012", "de");
+
+ assert(enc != null);
+
+ try {
+ enc.signLevel1("1080", keyPair.getPrivate(), algorithmOID, "1");
+ } catch (Exception e) {
+ assert(false);
+ }
+
+ byte[] encoded = null;
+ try {
+ encoded = enc.encode();
+ } catch (Exception e) {
+ assert(false);
+ }
+
+ assert(encoded != null);
+
+ }
+
+ /**
+ * Test dynamic header barcode decoding.
+ */
+ @Test public void testDynamicHeaderBarcodeDecoding() {
+
+
+ IUicRailTicket ticket = testFCBticket;
+
+ TicketLayout layout = testLayout;
+
+
+ Encoder enc = null;
+
+ try {
+ enc = new Encoder(ticket, layout, Encoder.UIC_BARCODE_TYPE_CLASSIC, 2, 1);
+ } catch (IOException | EncodingFormatException e1) {
+ assert(false);
+ }
+
+ enc.setStaticHeaderParams("123456789012", "de");
+
+ assert(enc != null);
+
+ try {
+ enc.signLevel1("1080", keyPair.getPrivate(), algorithmOID, "1");
+ } catch (Exception e) {
+ assert(false);
+ }
+
+
+ byte[] encoded = null;
+ try {
+ encoded = enc.encode();
+ } catch (Exception e) {
+ assert(false);
+ }
+
+ assert(encoded != null);
+
+ Decoder dec = null;
+ try {
+ dec = new Decoder(encoded);
+ } catch (IOException e) {
+ assert(false);
+ } catch (EncodingFormatException e) {
+ assert(false);
+ } catch (DataFormatException e) {
+ assert(false);
+ }
+ assert(dec != null);
+
+ int signatureCheck = 0;
+ try {
+ signatureCheck = dec.validateLevel1(keyPair.getPublic(),algorithmOID);
+ } catch (InvalidKeyException | NoSuchAlgorithmException | SignatureException | IllegalArgumentException
+ | UnsupportedOperationException | IOException | EncodingFormatException e) {
+ assert(false);
+ }
+
+ assert(signatureCheck == Constants.LEVEL1_VALIDATION_OK);
+
+ SimpleUICTestTicket.compare(ticket, dec.getUicTicket());
+
+ SimpleTestTicketLayout.compare(layout, dec.getLayout());
+
+ }
+
+ /**
+ * Generate DSA keys.
+ *
+ * @return the key pair
+ * @throws NoSuchAlgorithmException the no such algorithm exception
+ * @throws NoSuchProviderException the no such provider exception
+ * @throws InvalidAlgorithmParameterException the invalid algorithm parameter exception
+ */
+ public KeyPair generateDSAKeys(int keySize) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException{
+ KeyPairGenerator g = KeyPairGenerator.getInstance("DSA", "BC");
+ g.initialize(keySize, new SecureRandom());
+ return g.generateKeyPair();
+ }
+
+}
diff --git a/src/org/uic/barcode/test/TicketLayoutTest.java b/src/org/uic/barcode/test/TicketLayoutTest.java
new file mode 100644
index 0000000..0210896
--- /dev/null
+++ b/src/org/uic/barcode/test/TicketLayoutTest.java
@@ -0,0 +1,45 @@
+package org.uic.barcode.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+
+import org.junit.Test;
+import org.uic.barcode.staticFrame.UTLAYDataRecord;
+import org.uic.barcode.staticHeader.ticketLayoutBarcode.TicketLayout;
+import org.uic.barcode.test.utils.SimpleTestTicketLayout;
+import org.uic.barcode.test.utils.TestUtils;
+import org.uic.ticket.EncodingFormatException;
+
+
+public class TicketLayoutTest {
+
+
+ @Test public void testTicketLayout() throws IOException, EncodingFormatException{
+
+ UTLAYDataRecord tl1 = new UTLAYDataRecord();
+
+ TicketLayout layout = SimpleTestTicketLayout.getSimpleTestTicketLayout();
+ tl1.setLayout(layout);
+
+ byte[] encoded = null;
+ try {
+ encoded = tl1.encode();
+ } catch (IOException e) {
+ throw (e);
+ }
+
+ String hex = TestUtils.hexStringFromBytes(encoded);
+
+ assertEquals(hex,"555F544C41593031303034305243543230303031303130313031323030303030374DC3BC6C6C6572");
+
+ UTLAYDataRecord tl2 = new UTLAYDataRecord();
+
+ tl2.decode(tl1.encode());
+
+
+ assertEquals(tl1.toString(),tl2.toString());
+
+ }
+
+}
diff --git a/src/org/uic/barcode/test/utils/Level2TestDataFactory.java b/src/org/uic/barcode/test/utils/Level2TestDataFactory.java
new file mode 100644
index 0000000..a361561
--- /dev/null
+++ b/src/org/uic/barcode/test/utils/Level2TestDataFactory.java
@@ -0,0 +1,20 @@
+package org.uic.barcode.test.utils;
+
+import org.uic.barcode.dynamicFrame.DataType;
+
+import net.gcdc.asn1.datatypesimpl.OctetString;
+
+public class Level2TestDataFactory {
+
+ public static DataType getLevel2SimpleTestData() {
+
+ String testContent = "2020.10.01T12:12.20";
+
+ DataType level2Data = new DataType();
+ level2Data.setFormat("TEST");
+ level2Data.setData(new OctetString(testContent.getBytes()));
+
+ return level2Data;
+ }
+
+}
diff --git a/src/org/uic/barcode/test/utils/SimpleDynamicFrameTestBarcode.java b/src/org/uic/barcode/test/utils/SimpleDynamicFrameTestBarcode.java
new file mode 100644
index 0000000..1782ca6
--- /dev/null
+++ b/src/org/uic/barcode/test/utils/SimpleDynamicFrameTestBarcode.java
@@ -0,0 +1,80 @@
+package org.uic.barcode.test.utils;
+
+import java.security.KeyPair;
+
+import org.uic.barcode.dynamicFrame.Constants;
+import org.uic.barcode.dynamicFrame.DataType;
+import org.uic.barcode.dynamicFrame.DynamicFrame;
+import org.uic.barcode.dynamicFrame.Level1DataType;
+import org.uic.barcode.dynamicFrame.Level2DataType;
+import org.uic.barcode.dynamicFrame.SequenceOfDataType;
+import org.uic.ticket.EncodingFormatException;
+import org.uic.ticket.api.asn.omv1.UicRailTicketData;
+import org.uic.ticket.api.test.SimpleUicTestTicket;
+
+
+public class SimpleDynamicFrameTestBarcode {
+
+ public static DynamicFrame getSimpleDynamicHeaderBarcode(String algorithm, KeyPair keyPair) {
+
+
+
+ DynamicFrame barcode = null;
+
+
+
+ try {
+ barcode = new DynamicFrame();
+ barcode.setFormat(Constants.DYNAMIC_BARCODE_FORMAT_DEFAULT);
+ Level2DataType level2Data = new Level2DataType();
+ barcode.setLevel2SignedData(level2Data);
+
+ Level1DataType level1Data = new Level1DataType();
+ level2Data.setLevel1Data(level1Data);
+
+ level1Data.setSecurityProvider("1080");
+ level1Data.setKeyId(1L);
+
+ level1Data.setLevel1SigningAlg(Constants.ECDSA_SHA256);
+
+ DataType data = new DataType();
+ UicRailTicketData ticket = SimpleUicTestTicket.getUicTestTicket();
+ byte[] ticketData = ticket.encode();
+ data.setByteData(ticketData);
+ data.setFormat(Constants.DATA_TYPE_FCB_VERSION_1);
+ SequenceOfDataType dataSequence = new SequenceOfDataType();
+ level1Data.setData(dataSequence);
+ level1Data.getData().add(data);
+
+ try {
+ level2Data.signLevel1(keyPair.getPrivate());
+ } catch (Exception e) {
+ assert(false);
+ }
+
+
+ } catch (EncodingFormatException e) {
+ e.printStackTrace();
+ return null;
+ }
+
+ return barcode;
+ }
+
+
+ public static void compareFrame(DynamicFrame frame1, DynamicFrame frame2) {
+
+ assert(frame1.getLevel2SignedData().getLevel1Data().getKeyId() == frame2.getLevel2SignedData().getLevel1Data().getKeyId());
+
+ assert(frame1.getLevel2SignedData().getLevel1Data().getLevel1SigningAlg().equals(frame2.getLevel2SignedData().getLevel1Data().level1SigningAlg));
+
+ assert(frame1.getLevel2SignedData().getLevel1Data().getSecurityProvider().equals(frame2.getLevel2SignedData().getLevel1Data().getSecurityProvider()));
+
+ DataType data1 = frame1.getLevel2SignedData().getLevel1Data().getData().get(0);
+ DataType data2 = frame2.getLevel2SignedData().getLevel1Data().getData().get(0);
+
+ assert(data1.getFormat().equals(data2.getFormat()));
+
+ }
+
+}
diff --git a/src/org/uic/barcode/test/utils/SimpleTestTicketLayout.java b/src/org/uic/barcode/test/utils/SimpleTestTicketLayout.java
new file mode 100644
index 0000000..24cbf50
--- /dev/null
+++ b/src/org/uic/barcode/test/utils/SimpleTestTicketLayout.java
@@ -0,0 +1,33 @@
+package org.uic.barcode.test.utils;
+
+import org.uic.barcode.staticHeader.ticketLayoutBarcode.LayoutElement;
+import org.uic.barcode.staticHeader.ticketLayoutBarcode.TicketLayout;
+
+public class SimpleTestTicketLayout {
+
+
+ public static TicketLayout getSimpleTestTicketLayout() {
+
+ TicketLayout layout = new TicketLayout();
+
+ layout.setLayoutStandard("RCT2");
+
+ LayoutElement element = new LayoutElement();
+ element.setColumn(1);
+ element.setLine(1);
+ element.setHeight(1);
+ element.setWidth(20);
+ element.setText("Müller");
+ layout.addLayoutElement(element);
+
+ return layout;
+
+ }
+
+ public static void compare(TicketLayout layout1, TicketLayout layout2) {
+
+ assert(layout1.getLayoutStandard().equals(layout2.getLayoutStandard()));
+
+ }
+
+}
diff --git a/src/org/uic/barcode/test/utils/SimpleUICTestTicket.java b/src/org/uic/barcode/test/utils/SimpleUICTestTicket.java
new file mode 100644
index 0000000..3f38515
--- /dev/null
+++ b/src/org/uic/barcode/test/utils/SimpleUICTestTicket.java
@@ -0,0 +1,254 @@
+package org.uic.barcode.test.utils;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.uic.ticket.api.impl.SimpleCardReference;
+import org.uic.ticket.api.impl.SimpleControlDetail;
+import org.uic.ticket.api.impl.SimpleCustomerStatusDescription;
+import org.uic.ticket.api.impl.SimpleExtension;
+import org.uic.ticket.api.impl.SimpleIssuingDetail;
+import org.uic.ticket.api.impl.SimpleOpenTicket;
+import org.uic.ticket.api.impl.SimpleStationPassage;
+import org.uic.ticket.api.impl.SimpleTicketLink;
+import org.uic.ticket.api.impl.SimpleToken;
+import org.uic.ticket.api.impl.SimpleTraveler;
+import org.uic.ticket.api.impl.SimpleTravelerDetail;
+import org.uic.ticket.api.impl.SimpleUicRailTicket;
+import org.uic.ticket.api.spec.ICardReference;
+import org.uic.ticket.api.spec.IControlDetail;
+import org.uic.ticket.api.spec.ICustomerStatusDescription;
+import org.uic.ticket.api.spec.IExtension;
+import org.uic.ticket.api.spec.IIssuingDetail;
+import org.uic.ticket.api.spec.ILinkMode;
+import org.uic.ticket.api.spec.IOpenTicket;
+import org.uic.ticket.api.spec.IStationPassage;
+import org.uic.ticket.api.spec.ITicketLink;
+import org.uic.ticket.api.spec.IToken;
+import org.uic.ticket.api.spec.ITraveler;
+import org.uic.ticket.api.spec.IUicRailTicket;
+
+public class SimpleUICTestTicket {
+
+
+ public static IUicRailTicket getUicTestTicket() {
+ IUicRailTicket ticket = new SimpleUicRailTicket();
+ populateTicket(ticket);
+ return ticket;
+ }
+
+
+ private static void populateTicket(IUicRailTicket ticket) {
+
+ ticket.setControlDetails(new SimpleControlDetail());
+ populate(ticket.getControlDetails());
+
+
+ ticket.setIssuerDetails(new SimpleIssuingDetail());
+ populateIssuingData(ticket.getIssuerDetails());
+
+ SimpleTravelerDetail td = new SimpleTravelerDetail();
+ populateTravelerData(td);
+ ticket.setTravelerDetails(td);
+
+
+ //OpenTicket
+ IOpenTicket do1 = new SimpleOpenTicket();
+ populate(do1);
+ ticket.addOpenTicket(do1);
+
+ //StationPassage
+ IStationPassage do2 = new SimpleStationPassage();
+ populateStationPassage(do2);
+ ticket.addStationPassage(do2);
+
+ //token
+ IToken to = new SimpleToken();
+ to.setTokenProvider("VDV");
+ byte[] ba = { (byte) 0x82, (byte) 0xDA };
+ to.setToken(ba);
+
+
+ ticket.addExtension(populateExtension());
+
+ }
+
+ /*
+ ticket stationPassage : {
+ productName "passage"
+ ,station {8312345}
+ ,stationNameUTF8 { "Amsterdam" }
+ ,validFromDay 0
+ ,validUntilDay 4
+ }
+ */
+ private static void populateStationPassage(IStationPassage sp) {
+ sp.setProductName("passage");
+
+ try {
+ Date date = new SimpleDateFormat("dd/MM/yyyy").parse("01/01/2018");
+ sp.setValidFrom(date);
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ try {
+ Date date = new SimpleDateFormat("dd/MM/yyyy").parse("04/01/2018");
+ sp.setValidUntil(date);
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+
+ sp.addStation("8312345");
+ sp.addStationName("Amsterdam");
+
+ }
+
+ /*
+ {
+ token {tokenProviderIA5 "VDV", token '82DA'H }
+ ,ticket openTicket : {
+ returnIncluded FALSE
+ infoText "openTicketInfo"
+ }
+ }
+ */
+
+ private static void populate(IOpenTicket otd) {
+ otd.setInfoText("openTicketInfo");
+ otd.setReturnIncluded(false);
+ }
+
+
+ /*
+ ,travelerDetail{
+ traveler {
+ {
+ firstName "John"
+ ,secondName "Dow"
+ ,idCard "12345"
+ ,ticketHolder TRUE
+ ,status {{customerStatusDescr "senior" }}
+ }
+ }
+ ,groupName "myGroup"
+ }
+ */
+
+ private static void populateTravelerData(SimpleTravelerDetail td) {
+ td.setGroupName("myGroup");
+ ITraveler tr = new SimpleTraveler();
+ tr.setIdCard("12345");
+ tr.setFirstName("John");
+ tr.setSecondName("Dow");
+ tr.setTicketHolder(true);
+ ICustomerStatusDescription cst = new SimpleCustomerStatusDescription();
+ tr.addStatusDescription(cst);
+ cst.setDescription("senior");
+ td.addTraveler(tr);
+ }
+
+ /*
+ ,issuingDetail {
+ issuerNum 1080
+ issuingYear 2018
+ issuingDay 1
+ specimen TRUE,
+ securePaperTicket FALSE,
+ activated TRUE,
+ issuerPNR "issuerTestPNR",
+ issuedOnLine 12
+ }
+ */
+ private static void populateIssuingData(IIssuingDetail iIssuingDetail) {
+
+ try {
+ Date date = new SimpleDateFormat("dd/MM/yyyy").parse("01/01/2018");
+ iIssuingDetail.setIssuer("1080");
+ iIssuingDetail.setIssuingDate(date);
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+
+
+ iIssuingDetail.setIssuerPNR("issuerTestPNR");
+ iIssuingDetail.setSpecimen(true);
+ iIssuingDetail.setSecurePaperTicket(false);
+ iIssuingDetail.setActivated(true);
+ iIssuingDetail.setIssuedOnLine(12);
+ }
+
+ /*
+ ,extension {
+ { extensionId "1", extensionData '82DA'H }
+ ,{ extensionId "2", extensionData '83DA'H }
+ }
+ */
+ private static IExtension populateExtension() {
+ IExtension ed1 = new SimpleExtension();
+ ed1.setId("1");
+ byte[] ba1 = { (byte) 0x82, (byte) 0xDA };
+ ed1.setBinarydata(ba1);
+ return ed1;
+ }
+
+ /*
+ ,controlDetail {
+ identificationByCardReference {
+ { trailingCardIdNum 100 }
+ }
+ ,identificationByIdCard FALSE
+ ,identificationByPassportId FALSE
+ ,passportValidationRequired FALSE
+ ,onlineValidationRequired FALSE
+ ,ageCheckRequired FALSE
+ ,reductionCardCheckRequired FALSE
+ ,infoText "cd"
+ ,includedTickets {
+ { productOwnerIA5 "test" }
+ }
+ }
+ */
+ private static void populate(IControlDetail iControlDetail) {
+ iControlDetail.setInfoText("cd");
+ iControlDetail.setAgeCheckRequired(false);
+ iControlDetail.setIdentificationByIdCard(false);
+ iControlDetail.setIdentificationByPassportId(false);
+ iControlDetail.setOnlineValidationRequired(false);
+ iControlDetail.setPassportValidationRequired(false);
+ iControlDetail.setReductionCardCheckRequired(false);
+ iControlDetail.getIdentificationByCardReference().add(populateCardRefrence());
+ iControlDetail.addLinkedTicket(populateLinkedTicket());
+ }
+
+
+ /*
+ *
+ */
+ private static ITicketLink populateLinkedTicket() {
+ ITicketLink it = new SimpleTicketLink();
+ it.setProductOwner("test");
+ it.setLinkMode(ILinkMode.issuedTogether);
+ return it;
+ }
+
+ /*
+ {
+ trailingCardIdNum 100
+ }
+ */
+ private static ICardReference populateCardRefrence() {
+ ICardReference cr = new SimpleCardReference();
+ cr.setTrailingCardId("100");
+ return cr;
+ }
+
+
+ public static void compare(IUicRailTicket ticket1, IUicRailTicket ticket2) {
+
+ assert(ticket1.getIssuerDetails().getIssuer().equals(ticket2.getIssuerDetails().getIssuer()));
+
+ }
+
+
+}
diff --git a/src/org/uic/barcode/test/utils/TestUtils.java b/src/org/uic/barcode/test/utils/TestUtils.java
new file mode 100644
index 0000000..7311ae4
--- /dev/null
+++ b/src/org/uic/barcode/test/utils/TestUtils.java
@@ -0,0 +1,38 @@
+package org.uic.barcode.test.utils;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public class TestUtils {
+
+ final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
+
+ public static String hexStringFromBytes(byte[] bytes) {
+ char[] hexChars = new char[bytes.length * 2];
+ for (int j = 0; j < bytes.length; j++) {
+ int v = bytes[j] & 0xFF;
+ hexChars[j * 2] = hexArray[v >>> 4];
+ hexChars[j * 2 + 1] = hexArray[v & 0x0F];
+ }
+ return new String(hexChars);
+ }
+
+
+ public static Date parseDate (String source){
+
+ SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
+
+ try {
+ return formatter.parse(source);
+ } catch (ParseException e) {
+ try {
+ return formatter.parse("2001-01-01");
+ } catch (ParseException e1) {
+ return null;
+ }
+ }
+
+ }
+
+}
diff --git a/src/org/uic/barcode/utils/AlgorithmNameResolver.java b/src/org/uic/barcode/utils/AlgorithmNameResolver.java
new file mode 100644
index 0000000..300bf7d
--- /dev/null
+++ b/src/org/uic/barcode/utils/AlgorithmNameResolver.java
@@ -0,0 +1,173 @@
+package org.uic.barcode.utils;
+
+import java.security.Provider;
+import java.security.Provider.Service;
+import java.security.Security;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class AlgorithmNameResolver {
+
+
+ public static final String TYPE_KEY_GENERATOR_ALG = "KeyPairGenerator";
+ public static final String TYPE_SIGNATURE_ALG = "Signature";
+
+
+ private static final Pattern KEY_TYPE_PATTERN = Pattern.compile("^(\\w+)[.].*$");
+ private static final Pattern KEY_ALIAS_TYPE_PATTERN = Pattern.compile("^Alg[.]Alias[.](\\w+).*$");
+ private static final Pattern KEY_OID_PATTERN = Pattern.compile(".*?(\\d+(?:[.]\\d+){3,})$");
+
+ public static String getSignatureAlgorithmName (String oid) throws Exception {
+
+ Provider[] provs = Security.getProviders();
+ for (Provider prov : provs) {
+ Service service = prov.getService(AlgorithmNameResolver.TYPE_SIGNATURE_ALG,oid);
+ if (service != null) {
+ return service.getAlgorithm();
+ }
+ }
+ return null;
+
+ }
+
+
+ public static String getName(String type, String oid) throws Exception {
+
+ Provider[] provs = Security.getProviders();
+
+ for (Provider prov : provs) {
+
+ SortedSet<String> typeAndOID = getTypeAndOIDStrings(prov);
+
+ for (String entry : typeAndOID) {
+ String[] typeAndOIDArray = entry.split("-");
+ String ptype = typeAndOIDArray[0];
+ if (ptype.equalsIgnoreCase(type)) {
+ String poid = typeAndOIDArray[1];
+ Service pservice = prov.getService(ptype, poid);
+ String palgo = pservice.getAlgorithm();
+
+ if (ptype.equalsIgnoreCase(type) && poid.equals(oid)) {
+ return palgo;
+ }
+ }
+ }
+ }
+
+
+ if (oid.startsWith("1.2.840.10045")) {
+ return "ECDSA";
+ } else if (oid.startsWith("1.2.840.10040")) {
+ return "DSA";
+ }
+
+ return null;
+
+ }
+
+ public static String getName(String type, String oid, String providerName) throws Exception {
+
+ Provider[] provs = Security.getProviders();
+
+ for (Provider prov : provs) {
+
+ if (providerName == null || prov.getName().equals(providerName)) {
+
+ SortedSet<String> typeAndOID = getTypeAndOIDStrings(prov);
+
+ for (String entry : typeAndOID) {
+ String[] typeAndOIDArray = entry.split("-");
+ String ptype = typeAndOIDArray[0];
+ if (ptype.equalsIgnoreCase(type)) {
+ String poid = typeAndOIDArray[1];
+ Service pservice = prov.getService(ptype, poid);
+ String palgo = pservice.getAlgorithm();
+
+ if (ptype.equalsIgnoreCase(type) && poid.equals(oid)) {
+ return palgo;
+ }
+ }
+ }
+ }
+ }
+
+
+ if (oid.startsWith("1.2.840.10045")) {
+ return "ECDSA";
+ } else if (oid.startsWith("1.2.840.10040")) {
+ return "DSA";
+ }
+
+ return null;
+
+ }
+
+ private static SortedSet<String> getTypeAndOIDStrings(Provider prov) {
+
+ SortedSet<String> typeAndOID = new TreeSet<>();
+
+ Set<Object> keys = prov.keySet();
+ for (Object key : keys) {
+ String keyString = key.toString();
+ Matcher oidMatcher = KEY_OID_PATTERN.matcher(keyString);
+ if (oidMatcher.matches()) {
+ // get OID from matched keyString
+ String oid = oidMatcher.group(1);
+
+ // determine type
+ String type;
+ Matcher aliasTypeMatcher = KEY_ALIAS_TYPE_PATTERN.matcher(keyString);
+ if (aliasTypeMatcher.matches()) {
+ type = aliasTypeMatcher.group(1);
+ } else {
+ Matcher typeMatcher = KEY_TYPE_PATTERN.matcher(keyString);
+ typeMatcher.matches();
+ type = typeMatcher.group(1);
+ }
+ // algorithm parameters are not algorithms, so skip them
+ if (type.equals("AlgorithmParameters")) {
+ continue;
+ }
+
+ // auto-removes dupes
+ typeAndOID.add(type + "-" + oid);
+ }
+ }
+ return typeAndOID;
+ }
+
+
+ public static String getSecurityNames() throws Exception {
+
+ Provider[] provs = Security.getProviders();
+
+ StringBuilder sb = new StringBuilder();
+
+ for (Provider prov : provs) {
+
+ SortedSet<String> typeAndOID = getTypeAndOIDStrings(prov);
+
+ for (String entry : typeAndOID) {
+ String[] typeAndOIDArray = entry.split("-");
+ String ptype = typeAndOIDArray[0];
+ String poid = typeAndOIDArray[1];
+ Service pservice = prov.getService(ptype, poid);
+ String palgo = "";
+ if ( pservice != null) {
+ palgo = pservice.getAlgorithm();
+ }
+ sb.append(prov).append(";");
+ sb.append(ptype).append(";");
+ sb.append(poid).append(";");
+ sb.append(palgo).append(";");
+ sb.append("\r\n");
+
+ }
+ }
+ return sb.toString();
+
+ }
+}
diff --git a/src/org/uic/barcode/utils/package.html b/src/org/uic/barcode/utils/package.html
new file mode 100644
index 0000000..45a73a9
--- /dev/null
+++ b/src/org/uic/barcode/utils/package.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head></head>
+<body>
+
+<h1>Implementation of the header frames to bundle all data for a bar code.</h1>
+
+<p>
+
+ Provides a decoding and encoding of the header data frames for:<br/><br/>
+
+ <ul>
+ <li> static header as defined in UIC IRS 90918-9</li>
+ <li> dynamic header as drafted for the UIC IRS 90918-9</li>
+
+ </ul>
+</p>
+
+</body>
+</html> \ No newline at end of file