summaryrefslogtreecommitdiffstats
path: root/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/main/java/org/uic/barcode/Decoder.java158
-rw-r--r--src/main/java/org/uic/barcode/Encoder.java26
-rw-r--r--src/main/java/org/uic/barcode/asn1/uper/ByteBitBuffer.java1
-rw-r--r--src/main/java/org/uic/barcode/package.html30
-rw-r--r--src/main/java/org/uic/barcode/ssbFrame/SsbClass.java8
-rw-r--r--src/main/java/org/uic/barcode/ssbFrame/SsbCommonTicketPart.java151
-rw-r--r--src/main/java/org/uic/barcode/ssbFrame/SsbFrame.java371
-rw-r--r--src/main/java/org/uic/barcode/ssbFrame/SsbGroup.java168
-rw-r--r--src/main/java/org/uic/barcode/ssbFrame/SsbHeader.java126
-rw-r--r--src/main/java/org/uic/barcode/ssbFrame/SsbNonReservation.java131
-rw-r--r--src/main/java/org/uic/barcode/ssbFrame/SsbNonUic.java69
-rw-r--r--src/main/java/org/uic/barcode/ssbFrame/SsbPass.java262
-rw-r--r--src/main/java/org/uic/barcode/ssbFrame/SsbReservation.java237
-rw-r--r--src/main/java/org/uic/barcode/ssbFrame/SsbStationCodeTable.java9
-rw-r--r--src/main/java/org/uic/barcode/ssbFrame/SsbStations.java132
-rw-r--r--src/main/java/org/uic/barcode/ssbFrame/SsbTicketPart.java31
-rw-r--r--src/main/java/org/uic/barcode/ssbFrame/SsbTicketType.java39
-rw-r--r--src/main/java/org/uic/barcode/staticFrame/package.html29
-rw-r--r--src/main/java/org/uic/barcode/ticket/api/asn/package.html8
-rw-r--r--src/main/java/org/uic/barcode/ticket/api/impl/package.html7
-rw-r--r--src/main/java/org/uic/barcode/ticket/api/spec/package.html10
-rw-r--r--src/main/java/org/uic/barcode/ticket/api/utils/package.html7
-rw-r--r--src/main/java/org/uic/barcode/ticket/package.html12
-rw-r--r--src/main/java/org/uic/barcode/utils/SecurityUtils.java129
-rw-r--r--src/main/java/org/uic/barcode/utils/package.html21
25 files changed, 2083 insertions, 89 deletions
diff --git a/src/main/java/org/uic/barcode/Decoder.java b/src/main/java/org/uic/barcode/Decoder.java
index 9f5ea82..85faa4a 100644
--- a/src/main/java/org/uic/barcode/Decoder.java
+++ b/src/main/java/org/uic/barcode/Decoder.java
@@ -15,6 +15,7 @@ import org.uic.barcode.dynamicFrame.api.IData;
import org.uic.barcode.dynamicFrame.api.IDynamicFrame;
import org.uic.barcode.dynamicFrame.api.ILevel1Data;
import org.uic.barcode.dynamicFrame.api.ILevel2Data;
+import org.uic.barcode.ssbFrame.SsbFrame;
import org.uic.barcode.staticFrame.StaticFrame;
import org.uic.barcode.staticFrame.UFLEXDataRecord;
import org.uic.barcode.staticFrame.UTLAYDataRecord;
@@ -39,6 +40,9 @@ public class Decoder {
/** The static frame. */
private StaticFrame staticFrame = null;
+ /** The ssb frame. */
+ private SsbFrame ssbFrame = null;
+
/** The uic ticket coder. */
private UicRailTicketCoder uicTicketCoder = null;
@@ -80,7 +84,7 @@ public class Decoder {
* @throws EncodingFormatException the encoding format exception
*/
public int validateLevel1(PublicKey key) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IllegalArgumentException, UnsupportedOperationException, IOException, EncodingFormatException {
- if (dynamicFrame != null) {
+ if (dynamicFrame != null && dynamicFrame != null) {
return dynamicFrame.validateLevel1(key) ;
} else {
if (staticFrame != null) {
@@ -106,15 +110,22 @@ public class Decoder {
* @throws EncodingFormatException the encoding format exception
*/
public int validateLevel1(PublicKey key, String signingAlg) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IllegalArgumentException, UnsupportedOperationException, IOException, EncodingFormatException {
- if (dynamicFrame != null) {
+ if (dynamicFrame != null ) {
return dynamicFrame.validateLevel1(key, signingAlg) ;
- } else {
+ } else if (staticFrame != null) {
if (staticFrame.verifyByAlgorithmOid(key,signingAlg)) {
return Constants.LEVEL1_VALIDATION_OK;
} else {
return Constants.LEVEL1_VALIDATION_FRAUD;
}
+ } else if (ssbFrame!= null) {
+ if (ssbFrame.verifyByAlgorithmOid(key,signingAlg, null)) {
+ return Constants.LEVEL1_VALIDATION_OK;
+ } else {
+ return Constants.LEVEL1_VALIDATION_FRAUD;
+ }
}
+ return Constants.LEVEL1_VALIDATION_NO_SIGNATURE;
}
/**
@@ -133,15 +144,25 @@ public class Decoder {
* @throws EncodingFormatException the encoding format exception
*/
public int validateLevel1(PublicKey key, String signingAlg, Provider provider) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IllegalArgumentException, UnsupportedOperationException, IOException, EncodingFormatException {
- if (!isStaticHeader(data)) {
+ if (!isStaticHeader(data) && dynamicFrame != null) {
return dynamicFrame.validateLevel1(key, provider) ;
- } else {
+ } else if (isSsbFrame(data) && ssbFrame != null) {
+
+ if (ssbFrame.verifyByAlgorithmOid(key,signingAlg, provider)) {
+ return Constants.LEVEL1_VALIDATION_OK;
+ } else {
+ return Constants.LEVEL1_VALIDATION_FRAUD;
+ }
+
+ } else if (staticFrame != null) {
+
if (staticFrame.verifyByAlgorithmOid(key,signingAlg, provider)) {
return Constants.LEVEL1_VALIDATION_OK;
} else {
return Constants.LEVEL1_VALIDATION_FRAUD;
}
}
+ return Constants.LEVEL1_VALIDATION_NO_SIGNATURE;
}
/**
@@ -151,7 +172,7 @@ public class Decoder {
* @throws EncodingFormatException
*/
public int validateLevel2() throws EncodingFormatException {
- if (!isStaticHeader(data)) {
+ if (!isStaticHeader(data) && dynamicFrame != null) {
return dynamicFrame.validateLevel2() ;
} else {
return Constants.LEVEL2_VALIDATION_NO_SIGNATURE;
@@ -164,7 +185,7 @@ public class Decoder {
* @return the return code indicating errors
*/
public int validateLevel2(Provider prov) throws EncodingFormatException {
- if (!isStaticHeader(data)) {
+ if (!isStaticHeader(data) && dynamicFrame != null) {
return dynamicFrame.validateLevel2(prov) ;
} else {
return Constants.LEVEL2_VALIDATION_NO_SIGNATURE;
@@ -183,45 +204,81 @@ public class Decoder {
public void decode(byte[] data) throws IOException, EncodingFormatException, DataFormatException {
if (!isStaticHeader(data)) {
-
- dynamicFrame = DynamicFrameCoder.decode(data);
-
- ILevel2Data level2 = dynamicFrame.getLevel2Data();
-
- ILevel1Data level1 = level2.getLevel1Data();
-
- for (IData level1Content : level1.getData()) {
-
- uicTicketCoder = new UicRailTicketCoder();
- if (level1Content.getFormat().equals("FCB1")) {
- uicTicket = uicTicketCoder.decodeFromAsn(level1Content.getData(), 1);
- } else if (level1Content.getFormat().equals("FCB2")) {
- uicTicket = uicTicketCoder.decodeFromAsn(level1Content.getData(), 2);
- } else if (level1Content.getFormat().equals("FCB3")) {
- uicTicket = uicTicketCoder.decodeFromAsn(level1Content.getData(), 3);
+ try {
+ decodeDynamicFrame(data);
+ } catch (Exception e) {
+ dynamicFrame = null;
+ if (isSsbFrame(data)) {
+ decodeSsbFrame(data);
+ } else {
+ throw e;
+ }
+ } catch (AssertionError e) {
+ dynamicFrame = null;
+ if (isSsbFrame(data)) {
+ decodeSsbFrame(data);
+ } else {
+ throw new EncodingFormatException(e.getMessage());
}
}
-
+
} else if (isStaticHeader(data)){
-
- staticFrame = new StaticFrame();
-
- staticFrame.decode(data);
-
- UFLEXDataRecord flex = staticFrame.getuFlex();
-
- if (flex != null) {
- uicTicket = flex.getTicket();
+ try {
+ decodeStaticFrame(data);
+ } catch (Exception e) {
+ staticFrame = null;
+ throw e;
}
-
- UTLAYDataRecord tlay = staticFrame.getuTlay();
+ }
+ }
+
+ private void decodeDynamicFrame(byte[] data) throws EncodingFormatException, IOException {
+
+ dynamicFrame = DynamicFrameCoder.decode(data);
+
+ ILevel2Data level2 = dynamicFrame.getLevel2Data();
+
+ ILevel1Data level1 = level2.getLevel1Data();
+
+ for (IData level1Content : level1.getData()) {
- if (tlay != null) {
- layout = tlay.getLayout();
+ uicTicketCoder = new UicRailTicketCoder();
+ if (level1Content.getFormat().equals("FCB1")) {
+ uicTicket = uicTicketCoder.decodeFromAsn(level1Content.getData(), 1);
+ } else if (level1Content.getFormat().equals("FCB2")) {
+ uicTicket = uicTicketCoder.decodeFromAsn(level1Content.getData(), 2);
+ } else if (level1Content.getFormat().equals("FCB3")) {
+ uicTicket = uicTicketCoder.decodeFromAsn(level1Content.getData(), 3);
}
}
}
+
+ private void decodeStaticFrame(byte[] data) throws EncodingFormatException, DataFormatException, IOException {
+
+ 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();
+ }
+ }
+ private void decodeSsbFrame(byte[] data) throws EncodingFormatException, DataFormatException, IOException {
+
+ ssbFrame = new SsbFrame();
+
+ ssbFrame.decode(data);
+
+ }
/**
* Checks if is static header.
@@ -236,6 +293,19 @@ public class Decoder {
}
/**
+ * Checks if is ssb frame.
+ *
+ * @param data the data
+ * @return true, if is static header
+ */
+ private boolean isSsbFrame(byte[] data) {
+ if (data.length == 114) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Gets the uic ticket.
*
* @return the uic ticket
@@ -301,14 +371,14 @@ public class Decoder {
}
public IData getLevel2Data() {
- if (!isStaticHeader(data) && dynamicFrame.getLevel2Data() != null) {
+ if (!isStaticHeader(data) && dynamicFrame != null && dynamicFrame.getLevel2Data() != null) {
return dynamicFrame.getLevel2Data().getLevel2Data();
}
return null;
}
public byte[] getEncodedLevel1Data() throws IOException, EncodingFormatException {
- if (!isStaticHeader(data)) {
+ if (!isStaticHeader(data) && dynamicFrame != null) {
return dynamicFrame.getLevel1DataBin();
} else if (staticFrame != null) {
return staticFrame.getDataForSignature();
@@ -341,5 +411,15 @@ public class Decoder {
}
}
+
+ public SsbFrame getSsbFrame() {
+ return ssbFrame;
+ }
+
+ public void setSsbFrame(SsbFrame ssbFrame) {
+ this.ssbFrame = ssbFrame;
+ }
+
+
}
diff --git a/src/main/java/org/uic/barcode/Encoder.java b/src/main/java/org/uic/barcode/Encoder.java
index b01ca14..f2b9b0c 100644
--- a/src/main/java/org/uic/barcode/Encoder.java
+++ b/src/main/java/org/uic/barcode/Encoder.java
@@ -19,6 +19,7 @@ import org.uic.barcode.dynamicFrame.api.SimpleLevel1Data;
import org.uic.barcode.dynamicFrame.api.SimpleLevel2Data;
import org.uic.barcode.dynamicFrame.v1.DynamicFrameCoderV1;
import org.uic.barcode.dynamicFrame.v2.DynamicFrameCoderV2;
+import org.uic.barcode.ssbFrame.SsbFrame;
import org.uic.barcode.staticFrame.StaticFrame;
import org.uic.barcode.staticFrame.UFLEXDataRecord;
import org.uic.barcode.staticFrame.UHEADDataRecord;
@@ -45,6 +46,9 @@ public class Encoder {
/** The static frame. */
private StaticFrame staticFrame = null;
+ /** The ssb frame. */
+ private SsbFrame ssbFrame = null;
+
/** The UIC bar code type classic. */
public static String UIC_BARCODE_TYPE_CLASSIC = "UIC_CLASSIC";
@@ -52,6 +56,9 @@ public class Encoder {
/** The UIC bar code type DOSIPAS. */
public static String UIC_BARCODE_TYPE_DOSIPAS = "UIC_DOSIPAS";
+ /** The UIC bar code type SSB. */
+ public static String UIC_BARCODE_TYPE_SSB = "UIC_SSB";
+
/**
* Instantiates a new encoder.
*
@@ -119,6 +126,11 @@ public class Encoder {
dynamicFrame.getLevel2Data().getLevel1Data().addData(ticketData);
}
+
+ } else if (barcodeType == UIC_BARCODE_TYPE_SSB) {
+
+ ssbFrame = new SsbFrame();
+
}
}
@@ -369,6 +381,8 @@ public class Encoder {
staticFrame.getHeaderRecord().setIssuer(securityProvider);
}
staticFrame.signByAlgorithmOID(key,signingAlg);
+ } else if (ssbFrame != null) {
+ ssbFrame.signLevel1(key, null, keyId, signingAlg);
}
}
@@ -395,6 +409,8 @@ public class Encoder {
staticFrame.getHeaderRecord().setIssuer(securityProvider);
}
staticFrame.signByAlgorithmOID(key,signingAlg,prov);
+ } else if (ssbFrame != null) {
+ ssbFrame.signLevel1(key, prov, keyId, signingAlg);
}
}
@@ -446,6 +462,8 @@ public class Encoder {
return DynamicFrameCoder.encode(dynamicFrame);
} else if (staticFrame != null) {
return staticFrame.encode();
+ } else if (ssbFrame != null) {
+ return ssbFrame.encode();
}
return null;
}
@@ -461,6 +479,14 @@ public class Encoder {
}
}
+ public SsbFrame getSsbFrame() {
+ return ssbFrame;
+ }
+
+ public void setSsbFrame(SsbFrame ssbFrame) {
+ this.ssbFrame = ssbFrame;
+ }
+
diff --git a/src/main/java/org/uic/barcode/asn1/uper/ByteBitBuffer.java b/src/main/java/org/uic/barcode/asn1/uper/ByteBitBuffer.java
index e409005..ce5ca60 100644
--- a/src/main/java/org/uic/barcode/asn1/uper/ByteBitBuffer.java
+++ b/src/main/java/org/uic/barcode/asn1/uper/ByteBitBuffer.java
@@ -125,6 +125,7 @@ public class ByteBitBuffer implements BitBuffer {
public ByteBitBuffer(byte[] backingArray) {
this.bytes = backingArray;
this.isFinite = true;
+ this.limit = backingArray.length * 8;
}
private ByteBitBuffer(int initialCapacity) {
diff --git a/src/main/java/org/uic/barcode/package.html b/src/main/java/org/uic/barcode/package.html
index 4a6ee0d..075af29 100644
--- a/src/main/java/org/uic/barcode/package.html
+++ b/src/main/java/org/uic/barcode/package.html
@@ -3,19 +3,21 @@
<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>
-
-
+ <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/main/java/org/uic/barcode/ssbFrame/SsbClass.java b/src/main/java/org/uic/barcode/ssbFrame/SsbClass.java
new file mode 100644
index 0000000..cf8d9aa
--- /dev/null
+++ b/src/main/java/org/uic/barcode/ssbFrame/SsbClass.java
@@ -0,0 +1,8 @@
+package org.uic.barcode.ssbFrame;
+
+public enum SsbClass {
+
+ FIRST,
+ Second;
+
+}
diff --git a/src/main/java/org/uic/barcode/ssbFrame/SsbCommonTicketPart.java b/src/main/java/org/uic/barcode/ssbFrame/SsbCommonTicketPart.java
new file mode 100644
index 0000000..8eef552
--- /dev/null
+++ b/src/main/java/org/uic/barcode/ssbFrame/SsbCommonTicketPart.java
@@ -0,0 +1,151 @@
+package org.uic.barcode.ssbFrame;
+
+import org.uic.barcode.asn1.uper.BitBuffer;
+import org.uic.barcode.asn1.uper.ByteBitBuffer;
+import org.uic.barcode.ticket.EncodingFormatException;
+
+public abstract class SsbCommonTicketPart extends SsbTicketPart {
+
+ /*
+ Number of adult passengers Num (<100) 7,000
+ Number of child passengers Num (<100) 7,000
+ "specimen" code Bit Flag 1,000
+ Class of travel Lookup of 64 options 6,000
+ TCN: Issuing unique Ticket number 14 AlphaNum 84,000
+ Year of issue Num (0..9) 4,000
+ Issuing day, from first of January Num (<512) 9,000
+ */
+
+ protected int numberOfAdults = 0;
+ protected int numberOfChildren = 0;
+ protected boolean specimen = true;
+ protected SsbClass classCode = null;
+ protected String ticketNumber = null;
+ protected int year = 0;
+ protected int day = 0;
+
+ protected int decodeCommonPart(byte[] bytes) {
+
+ BitBuffer bits = new ByteBitBuffer(bytes);
+
+ int offset = 27; // header offset
+ numberOfAdults = bits.getInteger(offset, 7);
+ offset = offset + 7;
+ numberOfChildren = bits.getInteger(offset, 7);
+ offset = offset + 7;
+ specimen = bits.get(offset);
+ offset++;
+ int classIndex = bits.getInteger(offset, 6);
+ classCode = SsbClass.values()[classIndex];
+ offset = offset + 6;
+ ticketNumber = bits.getChar6String(offset, 84);
+ offset = offset + 84;
+ year = bits.getInteger(offset, 4);
+ offset = offset + 4;
+ day = bits.getInteger(offset, 9);
+ offset = offset + 9;
+ return offset;
+ }
+
+ protected int encodeCommonPart(byte[] bytes, int offset) throws EncodingFormatException {
+
+ BitBuffer bits = new ByteBitBuffer(bytes);
+
+ if (numberOfAdults < 0 || numberOfAdults > 99) {
+ throw new EncodingFormatException("SSB number of adults too big");
+ }
+ bits.putInteger(offset,7, numberOfAdults);
+ offset = offset + 7;
+
+ if (numberOfChildren < 0 || numberOfChildren > 99) {
+ throw new EncodingFormatException("SSB number of children too big");
+ }
+ bits.putInteger(offset, 7, numberOfChildren);
+ offset = offset + 7;
+
+ bits.put(offset,specimen);
+ offset++;
+
+ bits.putInteger(offset, 6,classCode.ordinal());
+ offset = offset + 6;
+
+ if (ticketNumber.length() > 14) {
+ throw new EncodingFormatException("SSB Ticket Number too long");
+ }
+ bits.putChar6String(offset, 84, ticketNumber);
+ offset = offset + 84;
+
+
+ bits.putInteger(offset, 4, (year % 10));
+ offset = offset + 4;
+
+ if (day > 512) {
+ throw new EncodingFormatException("SSB day too long");
+ }
+ bits.putInteger(offset, 9, day);
+ offset = offset + 9;
+
+ return offset;
+
+
+ }
+
+ public int getNumberOfAdults() {
+ return numberOfAdults;
+ }
+
+ public void setNumberOfAdults(int numberOfAdults) {
+ this.numberOfAdults = numberOfAdults;
+ }
+
+ public int getNumberOfChildren() {
+ return numberOfChildren;
+ }
+
+ public void setNumberOfChildren(int numberOfChildren) {
+ this.numberOfChildren = numberOfChildren;
+ }
+
+ public boolean isSpecimen() {
+ return specimen;
+ }
+
+ public void setSpecimen(boolean specimen) {
+ this.specimen = specimen;
+ }
+
+ public SsbClass getClassCode() {
+ return classCode;
+ }
+
+ public void setClassCode(SsbClass classCode) {
+ this.classCode = classCode;
+ }
+
+ public String getTicketNumber() {
+ return ticketNumber;
+ }
+
+ public void setTicketNumber(String ticketNumber) {
+ this.ticketNumber = ticketNumber;
+ }
+
+ public int getYear() {
+ return year;
+ }
+
+ public void setYear(int year) {
+ this.year = year;
+ }
+
+ public int getDay() {
+ return day;
+ }
+
+ public void setDay(int day) {
+ this.day = day;
+ }
+
+
+
+}
diff --git a/src/main/java/org/uic/barcode/ssbFrame/SsbFrame.java b/src/main/java/org/uic/barcode/ssbFrame/SsbFrame.java
new file mode 100644
index 0000000..b473c1e
--- /dev/null
+++ b/src/main/java/org/uic/barcode/ssbFrame/SsbFrame.java
@@ -0,0 +1,371 @@
+package org.uic.barcode.ssbFrame;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+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.Arrays;
+
+
+import org.uic.barcode.ticket.EncodingFormatException;
+import org.uic.barcode.utils.AlgorithmNameResolver;
+import org.uic.barcode.utils.SecurityUtils;
+
+public class SsbFrame {
+
+ private SsbHeader header = new SsbHeader();
+
+ private byte[] signaturePart1 = null;
+
+ private byte[] signaturePart2 = null;
+
+ private SsbNonUic nonUicData = null;
+
+ private SsbNonReservation nonReservationData = null;
+
+ private SsbReservation reservationData = null;
+
+ private SsbGroup groupData = null;
+
+ private SsbPass passData = null;
+
+ public void decode(byte[] bytes) throws EncodingFormatException {
+
+ if (bytes.length != 114) {
+ throw new EncodingFormatException("Data size does not fit to SSB");
+ }
+
+ if (header == null) {
+ header = new SsbHeader();
+ }
+
+ int offset = 0;
+
+ offset = offset + header.decodeContent(bytes,0);
+
+ if (header.getTicketType().equals(SsbTicketType.UIC_1_IRT_RES_BOA)) {
+
+ reservationData = new SsbReservation();
+ offset = offset + reservationData.decodeContent(bytes,offset);
+
+ } else if (header.getTicketType().equals(SsbTicketType.UIC_2_NRT)) {
+
+ nonReservationData = new SsbNonReservation();
+ offset = offset + nonReservationData.decodeContent(bytes,offset);
+
+ } else if (header.getTicketType().equals(SsbTicketType.UIC_3_GRP)) {
+
+ groupData = new SsbGroup();
+ offset = offset + groupData.decodeContent(bytes, offset);
+
+ } else if (header.getTicketType().equals(SsbTicketType.UIC_4_RPT)) {
+
+ passData = new SsbPass();
+ offset = offset + passData.decodeContent(bytes,offset);
+
+ } else {
+
+ nonUicData = new SsbNonUic();
+ offset = offset + nonUicData.decodeContent(bytes,offset);
+
+ }
+
+ byte[] signatureBytes = new byte[56];
+
+ try {
+ //check for non-standard signature encoding
+ BigInteger[] bInts = SecurityUtils.decodeSignatureIntegerSequence(signatureBytes);
+ byte[] sig = SecurityUtils.encodeSignatureIntegerSequence(bInts[0],bInts[1]);
+ signaturePart1 = bInts[0].toByteArray();
+ signaturePart2 = bInts[1].toByteArray();
+ //decoding the entire signature was ok, so there was no split
+ } catch (Exception e) {
+ //the signature is correctly implemented, continue with recombination
+ signaturePart1 = new byte[28];
+ signaturePart2 = new byte[28];
+
+ for (int i = 0 ; i < 28;i++) {
+ signaturePart1[i] = bytes[58 + i];
+ signaturePart2[i] = bytes[58 + 28 + i];
+ }
+ }
+
+
+ }
+
+
+
+
+ public byte[] encode() throws EncodingFormatException {
+
+ byte[] bytes = new byte[114];
+
+ int offset = header.encodeContent(bytes,0);
+
+
+
+ if (nonUicData != null) {
+ offset = nonUicData.encodeContent(bytes, offset);
+ } else if (nonReservationData != null) {
+ offset = nonReservationData.encodeContent(bytes, offset);
+ } else if (reservationData != null) {
+ offset = reservationData.encodeContent(bytes, offset);
+ } else if (groupData != null) {
+ offset = groupData.encodeContent(bytes, offset);
+ } else if (passData != null) {
+ offset = passData.encodeContent(bytes, offset);
+ } else {
+ throw new EncodingFormatException("Data Content for SSB missing");
+ };
+
+
+ if (signaturePart1.length > 28) {
+ throw new EncodingFormatException("Signature too large");
+ }
+ if (signaturePart2.length > 28) {
+ throw new EncodingFormatException("Signature too large");
+ }
+
+ for (int i = 1 ; i < 29; i++) {
+ int sigInd = signaturePart1.length - i;
+ if (sigInd < signaturePart1.length && sigInd >= 0) {
+ bytes[58 + 28 - i] = signaturePart1[sigInd];
+ } else {
+ bytes[58 + 28 - i] = '\0';
+ }
+ sigInd = signaturePart2.length - i;
+ if (sigInd < signaturePart2.length && sigInd >= 0) {
+ bytes[58 + 28 + 28 - i] = signaturePart2[sigInd];
+ } else {
+ bytes[58 + 28 + 28 - i] = '\0';
+ }
+ }
+
+
+ return bytes;
+
+ }
+
+ public byte[] getDataForSignature() throws EncodingFormatException {
+
+ byte[] bytes = new byte[58];
+
+ int offset = header.encodeContent(bytes,0);
+
+
+ if (nonUicData != null) {
+ offset = nonUicData.encodeContent(bytes, offset);
+ } else if (nonReservationData != null) {
+ offset = nonReservationData.encodeContent(bytes, offset);
+ } else if (reservationData != null) {
+ offset = reservationData.encodeContent(bytes, offset);
+ } else if (groupData != null) {
+ offset = groupData.encodeContent(bytes, offset);
+ } else if (passData != null) {
+ offset = passData.encodeContent(bytes, offset);
+ } else {
+ throw new EncodingFormatException("Data Content for SSB missing");
+ };
+
+ return bytes;
+
+ }
+
+ public SsbHeader getHeader() {
+ return header;
+ }
+
+ public void setHeader(SsbHeader header) {
+ this.header = header;
+ }
+
+ public byte[] getSignaturePart1() {
+ return signaturePart1;
+ }
+
+ public void setSignaturePart1(byte[] signaturePart1) {
+ this.signaturePart1 = signaturePart1;
+ }
+
+ public byte[] getSignaturePart2() {
+ return signaturePart2;
+ }
+
+ public void setSignaturePart2(byte[] signaturePart2) {
+ this.signaturePart2 = signaturePart2;
+ }
+
+ public SsbNonUic getNonUicData() {
+ return nonUicData;
+ }
+
+ public void setNonUicData(SsbNonUic nonUicData) {
+ this.nonUicData = nonUicData;
+ this.nonReservationData = null;
+ this.reservationData = null;
+ this.groupData = null;
+ this.passData = null;
+ }
+
+ public SsbNonReservation getNonReservationData() {
+ return nonReservationData;
+ }
+
+ public void setNonReservationData(SsbNonReservation nonReservationData) {
+ this.nonReservationData = nonReservationData;
+ header.setTicketType(SsbTicketType.UIC_2_NRT);
+ this.reservationData = null;
+ this.nonUicData = null;
+ this.groupData = null;
+ this.passData = null;
+ }
+
+ public SsbReservation getReservationData() {
+ return reservationData;
+ }
+
+ public void setReservationData(SsbReservation reservationData) {
+ header.setTicketType(SsbTicketType.UIC_1_IRT_RES_BOA);
+ this.nonReservationData = null;
+ this.nonUicData = null;
+ this.groupData = null;
+ this.passData = null;
+ this.reservationData = reservationData;
+ }
+
+ public SsbGroup getGroupData() {
+ return groupData;
+ }
+
+ public void setGroupData(SsbGroup groupData) {
+ this.groupData = groupData;
+ header.setTicketType(SsbTicketType.UIC_3_GRP);
+ this.nonReservationData = null;
+ this.nonUicData = null;
+ this.reservationData = null;
+ this.passData = null;
+
+ }
+
+ public SsbPass getPassData() {
+ return passData;
+ }
+
+ public void setPassData(SsbPass passData) {
+ this.passData = passData;
+ header.setTicketType(SsbTicketType.UIC_4_RPT);
+ this.nonReservationData = null;
+ this.nonUicData = null;
+ this.groupData = null;
+ this.reservationData = null;
+ }
+
+ public void signLevel1(PrivateKey key, Provider prov, String keyId, String algorithmOid) throws Exception {
+
+
+ this.header.setKeyId(Integer.parseInt(keyId));
+
+ byte[] data = getDataForSignature();
+
+ if (prov == null) {
+ //check for a provider supporting the key
+ prov = SecurityUtils.findPrivateKeyProvider(key);
+ }
+
+ //find the algorithm name for the signature OID
+ String algo = AlgorithmNameResolver.getSignatureAlgorithmName(algorithmOid, prov);
+ Signature sig = null;
+
+ if (prov != null) {
+ sig = Signature.getInstance(algo, prov);
+ } else {
+ sig = Signature.getInstance(algo);
+ }
+ sig.initSign(key);
+
+ sig.update(data);
+
+ byte[] signature = sig.sign();
+
+ BigInteger[] bInts = SecurityUtils.decodeSignatureIntegerSequence(signature);
+
+ signaturePart1 = toUnsignedBytes(bInts[0]);
+
+ signaturePart2 = toUnsignedBytes(bInts[1]);
+
+
+ }
+
+ 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;
+ }
+
+ /**
+ * 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
+ * @param a dedicated security provider to validate the signature
+ * @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 operating exception
+ * @throws EncodingFormatException
+ * @throws IOException
+ */
+ public boolean verifyByAlgorithmOid(PublicKey key, String signingAlg, Provider prov) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IllegalArgumentException, UnsupportedOperationException, IOException, EncodingFormatException {
+ //find the algorithm name for the signature OID
+ String algo = null;
+
+ if (prov != null) {
+ Service service = prov.getService("Signature",signingAlg);
+ if (service != null) {
+ algo = service.getAlgorithm();
+ }
+ } else {
+ Provider[] provs = Security.getProviders();
+ for (Provider p : provs) {
+ if (algo == null) {
+ Service service = p.getService("Signature",signingAlg);
+ if (service != null) {
+ algo = service.getAlgorithm();
+ }
+ }
+ }
+
+ }
+
+ if (algo == null) {
+ throw new NoSuchAlgorithmException("No service for algorithm found: " + signingAlg);
+ }
+ Signature sig = Signature.getInstance(algo);
+ sig.initVerify(key);
+ sig.update(getDataForSignature());
+
+ BigInteger r = new BigInteger(1,signaturePart1);
+ BigInteger s = new BigInteger(1,signaturePart2);
+
+ byte[] signature = SecurityUtils.encodeSignatureIntegerSequence(r,s);
+
+ return sig.verify(signature);
+ }
+
+
+}
diff --git a/src/main/java/org/uic/barcode/ssbFrame/SsbGroup.java b/src/main/java/org/uic/barcode/ssbFrame/SsbGroup.java
new file mode 100644
index 0000000..7751ef6
--- /dev/null
+++ b/src/main/java/org/uic/barcode/ssbFrame/SsbGroup.java
@@ -0,0 +1,168 @@
+package org.uic.barcode.ssbFrame;
+
+import org.uic.barcode.asn1.uper.BitBuffer;
+import org.uic.barcode.asn1.uper.ByteBitBuffer;
+import org.uic.barcode.ticket.EncodingFormatException;
+
+public class SsbGroup extends SsbCommonTicketPart {
+
+ protected int firstDayOfValidity = 0;
+ protected int lastDayOfValidity = 0;
+ protected boolean isReturnJourney = false;
+ private int infoCode = 0;
+ private String text = null;
+ private SsbStations stations = new SsbStations();
+
+ private String groupName = null;
+
+ private int counterMarkNumber = 0;
+
+
+ @Override
+ protected int decodeContent(byte[] bytes, int offset) {
+
+ offset = offset + decodeCommonPart(bytes);
+
+ BitBuffer bits = new ByteBitBuffer(bytes);
+
+ isReturnJourney = bits.get(offset);
+ offset = offset++;
+
+ firstDayOfValidity = bits.getInteger(offset, 9);
+ offset = offset + 9;
+
+ lastDayOfValidity = bits.getInteger(offset, 9);
+ offset = offset + 9;
+
+ offset = stations.decode(offset, bytes);
+
+ groupName = bits.getChar6String(offset, 72);
+ offset = offset + 72;
+
+ counterMarkNumber = bits.getInteger(offset, 9);
+ offset = offset + 9;
+
+ infoCode = bits.getInteger(offset, 14);
+ offset = offset + 14;
+
+ text = bits.getChar6String(offset, 144);
+ offset = offset + 144;
+
+ return offset;
+
+ }
+
+ @Override
+ protected int encodeContent(byte[] bytes, int offset) throws EncodingFormatException {
+
+ offset = offset + encodeCommonPart(bytes, offset);
+
+ BitBuffer bits = new ByteBitBuffer(bytes);
+
+ bits.put(offset, isReturnJourney);
+ offset = offset++;
+
+ if (firstDayOfValidity < 0 || firstDayOfValidity > 511) {
+ throw new EncodingFormatException("SSB first day of validity too big");
+ }
+ bits.putInteger(offset, 9, firstDayOfValidity);
+ offset = offset + 9;
+
+ if (lastDayOfValidity < 0 || lastDayOfValidity > 511) {
+ throw new EncodingFormatException("SSB last day of validity too big");
+ }
+ bits.putInteger(offset, 9, lastDayOfValidity);
+ offset = offset + 9;
+
+ offset = stations.encode(offset, bytes);
+
+ if (groupName.length() > 12) {
+ throw new EncodingFormatException("SSB group name too big");
+ }
+ bits.putChar6String(offset, 72,groupName);
+ offset = offset + 72;
+
+ if (counterMarkNumber < 0 || counterMarkNumber > 246) {
+ throw new EncodingFormatException("SSB number of countermark too big");
+ }
+ bits.putInteger(offset, 9,counterMarkNumber);
+ offset = offset + 9;
+
+ if (infoCode < 0 || infoCode > 9999) {
+ throw new EncodingFormatException("SSB info code too big");
+ }
+ bits.putInteger(offset, 14, infoCode);
+ offset = offset + 14;
+
+ if (text.length() > 24) {
+ throw new EncodingFormatException("SSB text too big");
+ }
+ bits.putChar6String(offset, 144, text);
+ offset = offset + 144;
+
+ return offset;
+ }
+
+ public int getFirstDayOfValidity() {
+ return firstDayOfValidity;
+ }
+
+ public void setFirstDayOfValidity(int firstDayOfValidity) {
+ this.firstDayOfValidity = firstDayOfValidity;
+ }
+
+ public int getLastDayOfValidity() {
+ return lastDayOfValidity;
+ }
+
+ public void setLastDayOfValidity(int lastDayOfValidity) {
+ this.lastDayOfValidity = lastDayOfValidity;
+ }
+
+ public boolean isReturnJourney() {
+ return isReturnJourney;
+ }
+
+ public void setReturnJourney(boolean isReturnJourney) {
+ this.isReturnJourney = isReturnJourney;
+ }
+
+ public int getInfoCode() {
+ return infoCode;
+ }
+
+ public void setInfoCode(int infoCode) {
+ this.infoCode = infoCode;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ public SsbStations getStations() {
+ return stations;
+ }
+
+ public String getGroupName() {
+ return groupName;
+ }
+
+ public void setGroupName(String groupName) {
+ this.groupName = groupName;
+ }
+
+ public int getCounterMarkNumber() {
+ return counterMarkNumber;
+ }
+
+ public void setCounterMarkNumber(int counterMarkNumber) {
+ this.counterMarkNumber = counterMarkNumber;
+ }
+
+
+
+}
diff --git a/src/main/java/org/uic/barcode/ssbFrame/SsbHeader.java b/src/main/java/org/uic/barcode/ssbFrame/SsbHeader.java
new file mode 100644
index 0000000..48c8eaf
--- /dev/null
+++ b/src/main/java/org/uic/barcode/ssbFrame/SsbHeader.java
@@ -0,0 +1,126 @@
+package org.uic.barcode.ssbFrame;
+
+import org.uic.barcode.asn1.uper.BitBuffer;
+import org.uic.barcode.asn1.uper.ByteBitBuffer;
+import org.uic.barcode.ticket.EncodingFormatException;
+
+public class SsbHeader extends SsbTicketPart {
+
+ private int version = 3;
+ private SsbTicketType ticketType = null;
+ private int keyId = 0;
+ private int issuer = 0;
+
+ /*
+ Version Num 0-4 Bits
+ Issuer code Num 14 Bits
+ ID Num 4 Bits
+ Ticket type code Num 5 Bits
+ */
+
+ public SsbHeader(int version, SsbTicketType type, int keyId, int issuer) {
+ this.issuer = issuer;
+ this.keyId = keyId;
+ this.ticketType = type;
+ this.version = version;
+ }
+
+ public SsbHeader() {
+ }
+
+ public int decodeContent(byte[] headerData, int offset) {
+
+ BitBuffer bits = new ByteBitBuffer(headerData);
+
+ version = bits.getInteger(0, 4);
+ issuer = bits.getInteger(4, 14);
+ keyId = bits.getInteger(18, 4);
+ ticketType = SsbTicketType.values()[bits.getInteger(22, 5)];
+
+ return 4 + 14 + 4 + 5;
+
+ }
+
+ public int encodeContent(byte[] bytes, int offset) throws EncodingFormatException {
+
+ BitBuffer bits = new ByteBitBuffer(bytes);
+
+ if (version < 0 || version > 15) {
+ throw new EncodingFormatException("SSB Version too big");
+ }
+
+ bits.putInteger(0, 4, version);
+
+ if (issuer < 0 || issuer > 9999) {
+ throw new EncodingFormatException("SSB Issuer code too big");
+ }
+
+ bits.putInteger(4, 14, issuer);
+
+ if (keyId < 0 || keyId > 15) {
+ throw new EncodingFormatException("SSB Key Id too big");
+ }
+
+ bits.putInteger(18, 4, keyId);
+
+ bits.putInteger(22, 5, ticketType.ordinal());
+
+ return 4 + 14 + 4 + 5;
+
+ }
+
+
+
+ public int getVersion() {
+ return version;
+ }
+
+
+
+ public void setVersion(int version) {
+ this.version = version;
+ }
+
+
+
+ public SsbTicketType getTicketType() {
+ return ticketType;
+ }
+
+
+
+ public void setTicketType(SsbTicketType ticketType) {
+ this.ticketType = ticketType;
+ }
+
+
+
+ public int getKeyId() {
+ return keyId;
+ }
+
+
+
+ public void setKeyId(int keyId) {
+ this.keyId = keyId;
+ }
+
+
+
+ public int getIssuer() {
+ return issuer;
+ }
+
+
+
+ public void setIssuer(int issuer) {
+ this.issuer = issuer;
+ }
+
+
+
+
+
+
+
+}
diff --git a/src/main/java/org/uic/barcode/ssbFrame/SsbNonReservation.java b/src/main/java/org/uic/barcode/ssbFrame/SsbNonReservation.java
new file mode 100644
index 0000000..80fc2bc
--- /dev/null
+++ b/src/main/java/org/uic/barcode/ssbFrame/SsbNonReservation.java
@@ -0,0 +1,131 @@
+package org.uic.barcode.ssbFrame;
+
+import org.uic.barcode.asn1.uper.BitBuffer;
+import org.uic.barcode.asn1.uper.ByteBitBuffer;
+import org.uic.barcode.ticket.EncodingFormatException;
+
+public class SsbNonReservation extends SsbCommonTicketPart {
+
+ protected int firstDayOfValidity = 0;
+ protected int lastDayOfValidity = 0;
+ protected boolean isReturnJourney = false;
+ private int infoCode = 0;
+ private String text = null;
+ private SsbStations stations = new SsbStations();
+
+
+ @Override
+ protected int decodeContent(byte[] bytes, int offset) {
+
+ offset = offset + decodeCommonPart(bytes);
+
+ BitBuffer bits = new ByteBitBuffer(bytes);
+
+ isReturnJourney = bits.get(offset);
+ offset = offset++;
+
+ firstDayOfValidity = bits.getInteger(offset, 9);
+ offset = offset + 9;
+
+ lastDayOfValidity = bits.getInteger(offset, 9);
+ offset = offset + 9;
+
+ offset = stations.decode(offset, bytes);
+
+ infoCode = bits.getInteger(offset, 14);
+ offset = offset + 14;
+
+ text = bits.getChar6String(offset, 222);
+ offset = offset + 222;
+
+ return offset;
+
+ }
+
+ @Override
+ protected int encodeContent(byte[] bytes, int offset) throws EncodingFormatException {
+
+ offset = offset + encodeCommonPart(bytes, offset);
+
+ BitBuffer bits = new ByteBitBuffer(bytes);
+
+ bits.put(offset, isReturnJourney);
+ offset = offset++;
+
+ if (firstDayOfValidity < 0 || firstDayOfValidity > 511) {
+ throw new EncodingFormatException("SSB first day of validity too big");
+ }
+ bits.putInteger(offset, 9, firstDayOfValidity);
+ offset = offset + 9;
+
+ if (lastDayOfValidity < 0 || lastDayOfValidity > 511) {
+ throw new EncodingFormatException("SSB last day of validity too big");
+ }
+ bits.putInteger(offset, 9, lastDayOfValidity);
+ offset = offset + 9;
+
+ offset = stations.encode(offset, bytes);
+
+ if (infoCode < 0 || infoCode > 9999) {
+ throw new EncodingFormatException("SSB info code too big");
+ }
+ bits.putInteger(offset, 14, infoCode);
+ offset = offset + 14;
+
+ if (text.length() > 37) {
+ throw new EncodingFormatException("SSB text too big");
+ }
+ bits.putChar6String(offset, 222, text);
+ offset = offset + 222;
+
+ return offset;
+
+ }
+
+ public int getFirstDayOfValidity() {
+ return firstDayOfValidity;
+ }
+
+ public void setFirstDayOfValidity(int firstDayOfValidity) {
+ this.firstDayOfValidity = firstDayOfValidity;
+ }
+
+ public int getLastDayOfValidity() {
+ return lastDayOfValidity;
+ }
+
+ public void setLastDayOfValidity(int lastDayOfValidity) {
+ this.lastDayOfValidity = lastDayOfValidity;
+ }
+
+ public boolean isReturnJourney() {
+ return isReturnJourney;
+ }
+
+ public void setReturnJourney(boolean isReturnJourney) {
+ this.isReturnJourney = isReturnJourney;
+ }
+
+ public int getInfoCode() {
+ return infoCode;
+ }
+
+ public void setInfoCode(int infoCode) {
+ this.infoCode = infoCode;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ public SsbStations getStations() {
+ return stations;
+ }
+
+
+
+}
diff --git a/src/main/java/org/uic/barcode/ssbFrame/SsbNonUic.java b/src/main/java/org/uic/barcode/ssbFrame/SsbNonUic.java
new file mode 100644
index 0000000..1f0049e
--- /dev/null
+++ b/src/main/java/org/uic/barcode/ssbFrame/SsbNonUic.java
@@ -0,0 +1,69 @@
+package org.uic.barcode.ssbFrame;
+
+import org.uic.barcode.asn1.uper.AsnUtils;
+import org.uic.barcode.asn1.uper.BitBuffer;
+import org.uic.barcode.asn1.uper.ByteBitBuffer;
+
+public class SsbNonUic extends SsbTicketPart {
+
+
+
+
+ byte[] openData = null;
+
+ @Override
+ protected int decodeContent(byte[] bytes, int offset) {
+
+ BitBuffer bits = new ByteBitBuffer(bytes);
+
+ StringBuffer sb = new StringBuffer();
+
+
+ for (int i = offset; i < openDataLength; i++) {
+ if (bits.get(i) == false) {
+ sb.append("1");
+ } else {
+ sb.append("0");
+ }
+ }
+
+ for (int i = openDataLength; i < 440; i++) {
+ sb.append("0");
+ }
+
+ openData = AsnUtils.fromBooleanString(sb.toString());
+
+ return offset + openDataLength ;
+
+ }
+
+ @Override
+ protected int encodeContent(byte[] bytes, int offset) {
+
+ BitBuffer bits = new ByteBitBuffer(bytes);
+
+ String bitString = AsnUtils.toBooleanString(openData);
+
+
+ for (int i = 0; i< openDataLength ; i++) {
+ if (i < bitString.length() && bitString.charAt(i) == '0') {
+ bits.put(offset + i, true);
+ } else {
+ bits.put(offset + i, false);
+ }
+ }
+
+ return offset + openDataLength;
+ }
+
+ public byte[] getOpenData() {
+ return openData;
+ }
+
+ public void setOpenData(byte[] openData) {
+ this.openData = openData;
+ }
+
+
+
+}
diff --git a/src/main/java/org/uic/barcode/ssbFrame/SsbPass.java b/src/main/java/org/uic/barcode/ssbFrame/SsbPass.java
new file mode 100644
index 0000000..a26fb61
--- /dev/null
+++ b/src/main/java/org/uic/barcode/ssbFrame/SsbPass.java
@@ -0,0 +1,262 @@
+package org.uic.barcode.ssbFrame;
+
+import org.uic.barcode.asn1.uper.BitBuffer;
+import org.uic.barcode.asn1.uper.ByteBitBuffer;
+import org.uic.barcode.ticket.EncodingFormatException;
+
+public class SsbPass extends SsbCommonTicketPart {
+
+ /*
+ * RPT sub ticket type 3 values 2 2 bit 1 = INTERRAIL, 2 = EURAIL EUROPE, 3 = EURAIL OVERSEAS
+ First day of validity from the issuing date Num (<367) 9 bit 000 = open date for regular Eurail pass to be activated
+ Maximum duration from the issuing date for OVERSEAS; otherwise, last day of validity Num (<278) 9 bit 9 months max. validity
+ Number of days of travel allowed Num (<93) 7 bit
+ Country code 1 Num (<100) 7 0.875 100 = all countries
+ Country code 2 Num (<99) 7 0.875 If country code 1 is 100, then 00
+ Country code 3 Num (<99) 7 0.875 If country code 1 is 100, then 00
+ Country code 4 Num (<99) 7 0.875 If country code 1 is 100, then 00
+ Country code 5 Num (<99) 7 0.875 If country code 1 is 100, then 00
+ Second page Bit flag 1 0.125 For a two-page pass
+ Information messages Num (<9999) 14 1.75
+ Open text 6-bit ASCII (40 Char) 240 30
+ */
+
+
+ private int passSubType = 0;
+ private int firstDayOfValidity = 0;
+ private int maximumValidityDuration = 0;
+ private int numberOfTravels = 0;
+ private int country_1 = 0;
+ private int country_2 = 0;
+ private int country_3 = 0;
+ private int country_4 = 0;
+ private int country_5 = 0;
+ private boolean hasSecondPage = false;
+ private int infoCode = 0;
+ private String text = null;
+
+ @Override
+ protected int decodeContent(byte[] bytes, int offset) {
+
+ offset = offset + decodeCommonPart(bytes);
+
+ BitBuffer bits = new ByteBitBuffer(bytes);
+
+ passSubType = bits.getInteger(offset, 2);
+ offset = offset + 2;
+
+ firstDayOfValidity = bits.getInteger(offset, 9);
+ offset = offset + 9;
+
+ maximumValidityDuration = bits.getInteger(offset, 9);
+ offset = offset + 9;
+
+ numberOfTravels = bits.getInteger(offset, 7);
+ offset = offset + 7;
+
+ country_1 = bits.getInteger(offset, 7);
+ offset = offset + 7;
+
+ country_2 = bits.getInteger(offset, 7);
+ offset = offset + 7;
+
+ country_3 = bits.getInteger(offset, 7);
+ offset = offset + 7;
+
+ country_4 = bits.getInteger(offset, 7);
+ offset = offset + 7;
+
+ country_5 = bits.getInteger(offset, 7);
+ offset = offset + 7;
+
+ hasSecondPage = bits.get(offset);
+ offset++;
+
+ infoCode = bits.getInteger(offset, 14);
+ offset = offset + 14;
+
+ text = bits.getChar6String(offset, 240);
+ offset = offset + 240;
+
+ return offset;
+ }
+
+ @Override
+ protected int encodeContent(byte[] bytes, int offset) throws EncodingFormatException {
+
+ offset = offset + encodeCommonPart(bytes, offset);
+
+ BitBuffer bits = new ByteBitBuffer(bytes);
+
+ if (passSubType < 0 || passSubType > 3) {
+ throw new EncodingFormatException("SSB pass type too big");
+ }
+ bits.putInteger(offset, 2,passSubType);
+ offset = offset + 2;
+
+ if (firstDayOfValidity < 0 || firstDayOfValidity > 511) {
+ throw new EncodingFormatException("SSB first day of validity too big");
+ }
+ bits.putInteger(offset, 9,firstDayOfValidity);
+ offset = offset + 9;
+
+ if (maximumValidityDuration < 0 || maximumValidityDuration > 511) {
+ throw new EncodingFormatException("SSB validity duration too big");
+ }
+ bits.putInteger(offset, 9,maximumValidityDuration);
+ offset = offset + 9;
+
+ if (numberOfTravels < 0 || numberOfTravels > 94) {
+ throw new EncodingFormatException("SSB number of travels too big");
+ }
+ bits.putInteger(offset, 7, numberOfTravels);
+ offset = offset + 7;
+
+ if (country_1 < 0 || country_1 > 100) {
+ throw new EncodingFormatException("SSB country 1 too big");
+ }
+ bits.putInteger(offset, 7,country_1);
+ offset = offset + 7;
+
+ if (country_2 < 0 || country_2 > 99) {
+ throw new EncodingFormatException("SSB country 2 too big");
+ }
+ bits.putInteger(offset, 7,country_2);
+ offset = offset + 7;
+
+ if (country_3 < 0 || country_3 > 99) {
+ throw new EncodingFormatException("SSB country 3 too big");
+ }
+ bits.putInteger(offset, 7,country_3);
+ offset = offset + 7;
+
+ if (country_4 < 0 || country_4 > 99) {
+ throw new EncodingFormatException("SSB country 4 too big");
+ }
+ bits.putInteger(offset, 7,country_4);
+ offset = offset + 7;
+
+ if (country_5 < 0 || country_5 > 99) {
+ throw new EncodingFormatException("SSB country 5 too big");
+ }
+ bits.putInteger(offset, 7,country_5);
+ offset = offset + 7;
+
+ bits.put(offset, hasSecondPage);
+ offset++;
+
+ if (infoCode < 0 || infoCode > 9999) {
+ throw new EncodingFormatException("SSB info code too big");
+ }
+ bits.putInteger(offset, 14, infoCode);
+ offset = offset + 14;
+
+ if (text.length() > 40) {
+ throw new EncodingFormatException("SSB text too big");
+ }
+ bits.putChar6String(offset, 240,text);
+ offset = offset + 240;
+
+ return offset;
+ }
+
+ public int getPassSubType() {
+ return passSubType;
+ }
+
+ public void setPassSubType(int passSubType) {
+ this.passSubType = passSubType;
+ }
+
+ public int getFirstDayOfValidity() {
+ return firstDayOfValidity;
+ }
+
+ public void setFirstDayOfValidity(int firstDayOfValidity) {
+ this.firstDayOfValidity = firstDayOfValidity;
+ }
+
+ public int getMaximumValidityDuration() {
+ return maximumValidityDuration;
+ }
+
+ public void setMaximumValidityDuration(int maximumValidityDuration) {
+ this.maximumValidityDuration = maximumValidityDuration;
+ }
+
+ public int getNumberOfTravels() {
+ return numberOfTravels;
+ }
+
+ public void setNumberOfTravels(int numberOfTravels) {
+ this.numberOfTravels = numberOfTravels;
+ }
+
+ public int getCountry_1() {
+ return country_1;
+ }
+
+ public void setCountry_1(int country_1) {
+ this.country_1 = country_1;
+ }
+
+ public int getCountry_2() {
+ return country_2;
+ }
+
+ public void setCountry_2(int country_2) {
+ this.country_2 = country_2;
+ }
+
+ public int getCountry_3() {
+ return country_3;
+ }
+
+ public void setCountry_3(int country_3) {
+ this.country_3 = country_3;
+ }
+
+ public int getCountry_4() {
+ return country_4;
+ }
+
+ public void setCountry_4(int country_4) {
+ this.country_4 = country_4;
+ }
+
+ public int getCountry_5() {
+ return country_5;
+ }
+
+ public void setCountry_5(int country_5) {
+ this.country_5 = country_5;
+ }
+
+ public boolean isHasSecondPage() {
+ return hasSecondPage;
+ }
+
+ public void setHasSecondPage(boolean hasSecondPage) {
+ this.hasSecondPage = hasSecondPage;
+ }
+
+ public int getInfoCode() {
+ return infoCode;
+ }
+
+ public void setInfoCode(int infoCode) {
+ this.infoCode = infoCode;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+
+
+
+}
diff --git a/src/main/java/org/uic/barcode/ssbFrame/SsbReservation.java b/src/main/java/org/uic/barcode/ssbFrame/SsbReservation.java
new file mode 100644
index 0000000..c70c2d1
--- /dev/null
+++ b/src/main/java/org/uic/barcode/ssbFrame/SsbReservation.java
@@ -0,0 +1,237 @@
+package org.uic.barcode.ssbFrame;
+
+import org.uic.barcode.asn1.uper.BitBuffer;
+import org.uic.barcode.asn1.uper.ByteBitBuffer;
+import org.uic.barcode.ticket.EncodingFormatException;
+
+public class SsbReservation extends SsbCommonTicketPart {
+
+ private SsbStations stations = new SsbStations();
+
+ private int ticketSubType = 0;
+
+ private int departureDate = 0;
+
+ private int departureTime = 0;
+
+ private String train = null;
+
+ private int coach = 0;
+
+ private String place = null;
+
+ private boolean overbooking = false;
+
+ private int infoCode = 0;
+
+ private String text = null;
+
+
+
+
+
+ @Override
+ protected int decodeContent(byte[] bytes, int offset) {
+
+ offset = offset + decodeCommonPart(bytes);
+
+ BitBuffer bits = new ByteBitBuffer(bytes);
+
+ ticketSubType = bits.getInteger(offset, 2);
+ offset = offset + 2;
+
+ stations = new SsbStations();
+ offset = stations.decode(offset, bytes);
+
+ /*
+ * Departure date : First day of validity from the issuing date Num (<367) 9,000
+ Departure Time Num (<1440) 11,000
+ Train number AlphaNum + 5 Car 30,000
+ Coach number Num (< 999) 10,000
+ Seat/berth number 3 AlphaNum 18,000
+ Overbooking indicator Bit Flag 1,000
+ Information Messages Num (< 9999) 14,000
+ Open Tekst 6 Bit ASCII (27 Car) 162,000
+ */
+
+ departureDate = bits.getInteger(offset, 9);
+ offset = offset + 9;
+
+ departureTime = bits.getInteger(offset, 11);
+ offset = offset + 11;
+
+ train = bits.getChar6String(offset, 30);
+ offset = offset + 30;
+
+ coach = bits.getInteger(offset, 10);
+ offset = offset + 10;
+
+ place = bits.getChar6String(offset, 18);
+ offset = offset + 18;
+
+ overbooking = bits.get(offset);
+ offset++;
+
+ infoCode = bits.getInteger(offset, 14);
+ offset = offset + 14;
+
+ text = bits.getChar6String(offset, 162);
+ offset = offset + 162;
+
+ return offset;
+ }
+
+ @Override
+ protected int encodeContent(byte[] bytes, int offset) throws EncodingFormatException {
+
+ offset = offset + encodeCommonPart(bytes, offset);
+
+ BitBuffer bits = new ByteBitBuffer(bytes);
+
+ if (ticketSubType < 0 || ticketSubType > 3) {
+ throw new EncodingFormatException("SSB pass type too big");
+ }
+ bits.putInteger(offset, 2,ticketSubType);
+ offset = offset + 2;
+
+ offset = stations.encode(offset, bytes);
+
+ /*
+ * Departure date : First day of validity from the issuing date Num (<367) 9,000
+ Departure Time Num (<1440) 11,000
+ Train number AlphaNum + 5 Car 30,000
+ Coach number Num (< 999) 10,000
+ Seat/berth number 3 AlphaNum 18,000
+ Overbooking indicator Bit Flag 1,000
+ Information Messages Num (< 9999) 14,000
+ Open Tekst 6 Bit ASCII (27 Car) 162,000
+ */
+
+ if (departureDate < 0 || departureDate > 512) {
+ throw new EncodingFormatException("SSB departure date too big");
+ }
+ bits.putInteger(offset, 9, departureDate);
+ offset = offset + 9;
+
+ if (departureTime < 0 || departureTime > 1440) {
+ throw new EncodingFormatException("SSB departure time too big");
+ }
+ bits.putInteger(offset, 11,departureTime);
+ offset = offset + 11;
+
+ if (train.length() > 5) {
+ throw new EncodingFormatException("SSB train too big");
+ }
+ bits.putChar6String(offset, 30,train);
+ offset = offset + 30;
+
+ if (coach < 0 || coach > 999) {
+ throw new EncodingFormatException("SSB coach too big");
+ }
+ bits.putInteger(offset, 10,coach);
+ offset = offset + 10;
+
+ if (place.length() > 3) {
+ throw new EncodingFormatException("SSB coach too big");
+ }
+ bits.putChar6String(offset, 18,place);
+ offset = offset + 18;
+
+ bits.put(offset, overbooking);
+ offset++;
+
+ if (infoCode < 0 || infoCode > 9999) {
+ throw new EncodingFormatException("SSB info code too big");
+ }
+ bits.putInteger(offset, 14, infoCode);
+ offset = offset + 14;
+
+ if (text.length() > 27) {
+ throw new EncodingFormatException("SSB text too big");
+ }
+ bits.putChar6String(offset, 162, text);
+ offset = offset + 162;
+
+ return offset;
+
+ }
+
+ public SsbStations getStations() {
+ return stations;
+ }
+
+ public int getTicketSubType() {
+ return ticketSubType;
+ }
+
+ public void setTicketSubType(int ticketSubType) {
+ this.ticketSubType = ticketSubType;
+ }
+
+ public int getDepartureDate() {
+ return departureDate;
+ }
+
+ public void setDepartureDate(int departureDate) {
+ this.departureDate = departureDate;
+ }
+
+ public int getDepartureTime() {
+ return departureTime;
+ }
+
+ public void setDepartureTime(int departureTime) {
+ this.departureTime = departureTime;
+ }
+
+ public String getTrain() {
+ return train;
+ }
+
+ public void setTrain(String train) {
+ this.train = train;
+ }
+
+ public int getCoach() {
+ return coach;
+ }
+
+ public void setCoach(int coach) {
+ this.coach = coach;
+ }
+
+ public String getPlace() {
+ return place;
+ }
+
+ public void setPlace(String place) {
+ this.place = place;
+ }
+
+ public boolean isOverbooking() {
+ return overbooking;
+ }
+
+ public void setOverbooking(boolean overbooking) {
+ this.overbooking = overbooking;
+ }
+
+ public int getInfoCode() {
+ return infoCode;
+ }
+
+ public void setInfoCode(int infoCode) {
+ this.infoCode = infoCode;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+
+
+}
diff --git a/src/main/java/org/uic/barcode/ssbFrame/SsbStationCodeTable.java b/src/main/java/org/uic/barcode/ssbFrame/SsbStationCodeTable.java
new file mode 100644
index 0000000..8aeaf22
--- /dev/null
+++ b/src/main/java/org/uic/barcode/ssbFrame/SsbStationCodeTable.java
@@ -0,0 +1,9 @@
+package org.uic.barcode.ssbFrame;
+
+public enum SsbStationCodeTable {
+
+ UNKNOWN_0,
+ NRT,
+ RESERVATION,
+ UNKNOWN_3;
+}
diff --git a/src/main/java/org/uic/barcode/ssbFrame/SsbStations.java b/src/main/java/org/uic/barcode/ssbFrame/SsbStations.java
new file mode 100644
index 0000000..e3b7654
--- /dev/null
+++ b/src/main/java/org/uic/barcode/ssbFrame/SsbStations.java
@@ -0,0 +1,132 @@
+package org.uic.barcode.ssbFrame;
+
+import org.uic.barcode.asn1.uper.BitBuffer;
+import org.uic.barcode.asn1.uper.ByteBitBuffer;
+import org.uic.barcode.ticket.EncodingFormatException;
+
+public class SsbStations {
+
+ /*
+ * Station code 1 bit 0 = Num; or 1=Bilateral AlphaNum 6Char
+
+ Numeric:
+ Station code List 4 bit 1= NRT; 2=Reservation
+ Departure station Location 28 bit
+ Arrival Station 28 bit
+
+ AlphaNum:
+ Departure: 30 bit
+ Arrival = 30 bit
+
+ */
+
+ protected String arrivalStationCode = " ";
+ protected String departureStationCode = " ";
+ protected SsbStationCodeTable codeTable = SsbStationCodeTable.NRT;
+
+ public int encode(int offset, byte[] bytes) throws EncodingFormatException {
+
+ boolean isAlphaNumeric = false;
+
+ BitBuffer bits = new ByteBitBuffer(bytes);
+
+ try {
+ Integer.parseInt(arrivalStationCode);
+ Integer.parseInt(departureStationCode);
+ isAlphaNumeric = false;
+ } catch(NumberFormatException e) {
+ isAlphaNumeric = true;
+ }
+ bits.put(offset, isAlphaNumeric);
+ offset++;
+
+ if (isAlphaNumeric) {
+ if (departureStationCode.length() > 6) {
+ throw new EncodingFormatException("SSB departure station too long");
+ }
+ bits.putChar6String(offset,30, departureStationCode);
+ offset = offset + 30;
+
+ if (arrivalStationCode.length() > 6) {
+ throw new EncodingFormatException("SSB arrival station too long");
+ }
+ bits.putChar6String(offset,30, arrivalStationCode);
+ offset = offset + 30;
+ } else {
+ bits.putInteger(offset, 4, codeTable.ordinal());
+ offset = offset + 4;
+
+ int stationCode = Integer.parseInt(departureStationCode);
+ if (stationCode < 0 || stationCode > 9999999) {
+ throw new EncodingFormatException("SSB departure station code too long");
+ }
+ bits.putInteger(offset, 28, stationCode);
+ offset = offset + 28;
+
+ stationCode = Integer.parseInt(arrivalStationCode);
+ if (stationCode < 0 || stationCode > 9999999) {
+ throw new EncodingFormatException("SSB arrival station code too long");
+ }
+ bits.putInteger(offset, 28, stationCode);
+ offset = offset + 28;
+ }
+
+ return offset;
+
+ }
+
+ public int decode(int offset, byte[] bytes) {
+
+ BitBuffer bits = new ByteBitBuffer(bytes);
+
+ boolean isAlphaNumeric = bits.get(offset);
+ offset++;
+
+ if (isAlphaNumeric) {
+ departureStationCode = bits.getChar6String(offset,30);
+ offset = offset + 30;
+ arrivalStationCode = bits.getChar6String(offset,30);
+ offset = offset + 30;
+ } else {
+ codeTable = SsbStationCodeTable.values()[bits.getInteger(offset, 4)];
+ offset = offset + 4;
+ departureStationCode = Integer.toString(bits.getInteger(offset, 28));
+ offset = offset + 28;
+ arrivalStationCode = Integer.toString(bits.getInteger(offset, 28));
+ offset = offset + 28;
+ }
+
+
+
+ return offset;
+
+ }
+
+ public String getArrivalStationCode() {
+ return arrivalStationCode;
+ }
+
+ public void setArrivalStationCode(String arrivalStationCode) {
+ this.arrivalStationCode = arrivalStationCode;
+ }
+
+ public String getDepartureStationCode() {
+ return departureStationCode;
+ }
+
+ public void setDepartureStationCode(String departureStationCode) {
+ this.departureStationCode = departureStationCode;
+ }
+
+ public SsbStationCodeTable getCodeTable() {
+ return codeTable;
+ }
+
+ public void setCodeTable(SsbStationCodeTable codeTable) {
+ this.codeTable = codeTable;
+ }
+
+
+
+
+}
diff --git a/src/main/java/org/uic/barcode/ssbFrame/SsbTicketPart.java b/src/main/java/org/uic/barcode/ssbFrame/SsbTicketPart.java
new file mode 100644
index 0000000..717608a
--- /dev/null
+++ b/src/main/java/org/uic/barcode/ssbFrame/SsbTicketPart.java
@@ -0,0 +1,31 @@
+package org.uic.barcode.ssbFrame;
+
+import org.uic.barcode.ticket.EncodingFormatException;
+
+public abstract class SsbTicketPart {
+
+ public static int openDataLength = 437;
+
+ public void decode(byte[] bytes) throws EncodingFormatException {
+ if (bytes.length != 114) {
+ throw new EncodingFormatException("Data size does not fit to SSB");
+ }
+ decodeContent(bytes, 0);
+ };
+
+ protected abstract int decodeContent(byte[] bytes , int offset);
+
+ public void encode(byte[] bytes) throws EncodingFormatException {
+ if (bytes.length != 114) {
+ throw new EncodingFormatException("Data size does not fit to SSB");
+ }
+ encodeContent(bytes, 0);
+ }
+
+ protected abstract int encodeContent(byte[] bytes, int offset) throws EncodingFormatException;
+
+
+
+
+
+}
diff --git a/src/main/java/org/uic/barcode/ssbFrame/SsbTicketType.java b/src/main/java/org/uic/barcode/ssbFrame/SsbTicketType.java
new file mode 100644
index 0000000..cb60a26
--- /dev/null
+++ b/src/main/java/org/uic/barcode/ssbFrame/SsbTicketType.java
@@ -0,0 +1,39 @@
+package org.uic.barcode.ssbFrame;
+
+public enum SsbTicketType {
+
+ UIC_1_IRT_RES_BOA,
+ UIC_2_NRT,
+ UIC_3_GRP,
+ UIC_4_RPT,
+ UIC_5_UNDEFINED,
+ UIC_6_UNDEFINED,
+ UIC_7_UNDEFINED,
+ UIC_8_UNDEFINED,
+ UIC_9_UNDEFINED,
+ UIC_10_UNDEFINED,
+ UIC_11_UNDEFINED,
+ UIC_12_UNDEFINED,
+ UIC_13_UNDEFINED,
+ UIC_14_UNDEFINED,
+ UIC_15_UNDEFINED,
+ UIC_16_UNDEFINED,
+ UIC_17_UNDEFINED,
+ UIC_18_UNDEFINED,
+ UIC_19_UNDEFINED,
+ UIC_20_UNDEFINED,
+ NONUIC_21_BILATERAL,
+ NONUIC_22_BILATERAL,
+ NONUIC_23_BILATERAL,
+ NONUIC_24_BILATERAL,
+ NONUIC_25_BILATERAL,
+ NONUIC_26_BILATERAL,
+ NONUIC_27_BILATERAL,
+ NONUIC_28_BILATERAL,
+ NONUIC_29_BILATERAL,
+ NONUIC_30_BILATERAL,
+ NONUIC_31_BILATERAL,
+ NONUIC_32_BILATERAL;
+
+
+}
diff --git a/src/main/java/org/uic/barcode/staticFrame/package.html b/src/main/java/org/uic/barcode/staticFrame/package.html
index b76540b..5ad7515 100644
--- a/src/main/java/org/uic/barcode/staticFrame/package.html
+++ b/src/main/java/org/uic/barcode/staticFrame/package.html
@@ -3,19 +3,24 @@
<head></head>
<body>
-<h1>static bar code header frame</h1>
+ <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>
+ <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/main/java/org/uic/barcode/ticket/api/asn/package.html b/src/main/java/org/uic/barcode/ticket/api/asn/package.html
index 307075e..214572b 100644
--- a/src/main/java/org/uic/barcode/ticket/api/asn/package.html
+++ b/src/main/java/org/uic/barcode/ticket/api/asn/package.html
@@ -1,7 +1,9 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
-<head>asn</head>
-<body>
- Provides code generated from the asn.1 specification using the openAsn compiler to implement the asn.1 encoduing and decoding using unaligned PER encoding.
+<head>asn
+</head>
+<body>Provides code generated from the asn.1 specification using
+ the openAsn compiler to implement the asn.1 encoduing and decoding
+ using unaligned PER encoding.
</body>
</html> \ No newline at end of file
diff --git a/src/main/java/org/uic/barcode/ticket/api/impl/package.html b/src/main/java/org/uic/barcode/ticket/api/impl/package.html
index 587b741..f2ef54e 100644
--- a/src/main/java/org/uic/barcode/ticket/api/impl/package.html
+++ b/src/main/java/org/uic/barcode/ticket/api/impl/package.html
@@ -1,7 +1,10 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
-<head>Ticket Data Implementation</head>
+<head>Ticket Data Implementation
+</head>
<body>
- Provides a simple implementation of the ticket data interface specified in package <b>spec</b>.
+ Provides a simple implementation of the ticket data interface specified
+ in package
+ <b>spec</b>.
</body>
</html> \ No newline at end of file
diff --git a/src/main/java/org/uic/barcode/ticket/api/spec/package.html b/src/main/java/org/uic/barcode/ticket/api/spec/package.html
index 18d8b53..f3961b8 100644
--- a/src/main/java/org/uic/barcode/ticket/api/spec/package.html
+++ b/src/main/java/org/uic/barcode/ticket/api/spec/package.html
@@ -1,7 +1,13 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
-<head>UIC ticket interface</head>
+<head>UIC ticket interface
+</head>
<body>
- Provides the interface specification of the ticket data. Any ticket data implementation which wants to use the provided encoder / decoder function must implement this interface. A simple implementation is provided in package <b>impl</p>.
+ Provides the interface specification of the ticket data. Any ticket
+ data implementation which wants to use the provided encoder / decoder
+ function must implement this interface. A simple implementation is
+ provided in package
+ <b>impl
+ </p>.
</body>
</html> \ No newline at end of file
diff --git a/src/main/java/org/uic/barcode/ticket/api/utils/package.html b/src/main/java/org/uic/barcode/ticket/api/utils/package.html
index a926c2d..baf156e 100644
--- a/src/main/java/org/uic/barcode/ticket/api/utils/package.html
+++ b/src/main/java/org/uic/barcode/ticket/api/utils/package.html
@@ -1,7 +1,8 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
-<head>Utilities</head>
-<body>
- Provides utilities for the asn.1 encoding and decoding of ticket data.
+<head>Utilities
+</head>
+<body>Provides utilities for the asn.1 encoding and decoding of
+ ticket data.
</body>
</html> \ No newline at end of file
diff --git a/src/main/java/org/uic/barcode/ticket/package.html b/src/main/java/org/uic/barcode/ticket/package.html
index 993e0c4..6f10316 100644
--- a/src/main/java/org/uic/barcode/ticket/package.html
+++ b/src/main/java/org/uic/barcode/ticket/package.html
@@ -1,9 +1,13 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
-<head>UIC ticket data API</head>
+<head>UIC ticket data API
+</head>
<body>
- This API provides a specification of ticket data as an interface and an implementation of an encoder/decoder to encode and decode ticket data to an asn.1 PER encoded byte stream according to the UIC specification.
- <br/>
- Any ticket data implementing the interface defined in package <b>spec</b> can be encoded/decoded. The package <b>impl</b> provides a simple implementation of the ticket.
+ This API provides a specification of ticket data as an interface and an
+ implementation of an encoder/decoder to encode and decode ticket data
+ to an asn.1 PER encoded byte stream according to the UIC specification.
+ <br /> Any ticket data implementing the interface defined in package
+ <b>spec</b> can be encoded/decoded. The package
+ <b>impl</b> provides a simple implementation of the ticket.
</body>
</html> \ No newline at end of file
diff --git a/src/main/java/org/uic/barcode/utils/SecurityUtils.java b/src/main/java/org/uic/barcode/utils/SecurityUtils.java
index 5fdbda7..8c981af 100644
--- a/src/main/java/org/uic/barcode/utils/SecurityUtils.java
+++ b/src/main/java/org/uic/barcode/utils/SecurityUtils.java
@@ -1,5 +1,8 @@
package org.uic.barcode.utils;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
@@ -10,6 +13,7 @@ import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
+import java.util.Arrays;
/**
* The Class SecurityUtils.
@@ -182,4 +186,129 @@ public class SecurityUtils {
return null;
}
+
+ /**
+ * 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;
+ }
+
+ /**
+ * 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 = i1.toByteArray();
+ int lb1 = b1.length;
+ byte[] b2 = i2.toByteArray();
+ 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();
+ }
+
+ public static 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 = r.toByteArray();
+ int lb1 = b1.length;
+
+ byte[] b2 = s.toByteArray();
+ 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();
+
+ }
}
diff --git a/src/main/java/org/uic/barcode/utils/package.html b/src/main/java/org/uic/barcode/utils/package.html
index 45a73a9..73917d7 100644
--- a/src/main/java/org/uic/barcode/utils/package.html
+++ b/src/main/java/org/uic/barcode/utils/package.html
@@ -3,18 +3,19 @@
<head></head>
<body>
-<h1>Implementation of the header frames to bundle all data for a bar code.</h1>
+ <h1>Implementation of the header frames to bundle all data for a
+ bar code.</h1>
-<p>
+ <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>
+ 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>
- </ul>
-</p>
-
</body>
</html> \ No newline at end of file