From e023e674020f1a435f7b8c8b9276704f576ea6e5 Mon Sep 17 00:00:00 2001 From: CGantert345 <57003061+CGantert345@users.noreply.github.com> Date: Mon, 29 Mar 2021 14:08:45 +0200 Subject: structure change 1 --- .../org/uic/barcode/staticFrame/DataRecord.java | 199 ++++++ .../uic/barcode/staticFrame/GENERICDataRecord.java | 62 ++ .../org/uic/barcode/staticFrame/StaticFrame.java | 764 +++++++++++++++++++++ .../uic/barcode/staticFrame/UFLEXDataRecord.java | 90 +++ .../uic/barcode/staticFrame/UHEADDataRecord.java | 269 ++++++++ .../uic/barcode/staticFrame/UTLAYDataRecord.java | 272 ++++++++ .../java/org/uic/barcode/staticFrame/package.html | 21 + .../ticketLayoutBarcode/FormatType.java | 34 + .../ticketLayoutBarcode/LayoutElement.java | 50 ++ .../ticketLayoutBarcode/TicketLayout.java | 61 ++ 10 files changed, 1822 insertions(+) create mode 100644 src/main/java/org/uic/barcode/staticFrame/DataRecord.java create mode 100644 src/main/java/org/uic/barcode/staticFrame/GENERICDataRecord.java create mode 100644 src/main/java/org/uic/barcode/staticFrame/StaticFrame.java create mode 100644 src/main/java/org/uic/barcode/staticFrame/UFLEXDataRecord.java create mode 100644 src/main/java/org/uic/barcode/staticFrame/UHEADDataRecord.java create mode 100644 src/main/java/org/uic/barcode/staticFrame/UTLAYDataRecord.java create mode 100644 src/main/java/org/uic/barcode/staticFrame/package.html create mode 100644 src/main/java/org/uic/barcode/staticFrame/ticketLayoutBarcode/FormatType.java create mode 100644 src/main/java/org/uic/barcode/staticFrame/ticketLayoutBarcode/LayoutElement.java create mode 100644 src/main/java/org/uic/barcode/staticFrame/ticketLayoutBarcode/TicketLayout.java (limited to 'src/main/java/org/uic/barcode/staticFrame') diff --git a/src/main/java/org/uic/barcode/staticFrame/DataRecord.java b/src/main/java/org/uic/barcode/staticFrame/DataRecord.java new file mode 100644 index 0000000..16d3811 --- /dev/null +++ b/src/main/java/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.barcode.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/main/java/org/uic/barcode/staticFrame/GENERICDataRecord.java b/src/main/java/org/uic/barcode/staticFrame/GENERICDataRecord.java new file mode 100644 index 0000000..4769670 --- /dev/null +++ b/src/main/java/org/uic/barcode/staticFrame/GENERICDataRecord.java @@ -0,0 +1,62 @@ +package org.uic.barcode.staticFrame; + +import java.io.IOException; + +import org.uic.barcode.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/main/java/org/uic/barcode/staticFrame/StaticFrame.java b/src/main/java/org/uic/barcode/staticFrame/StaticFrame.java new file mode 100644 index 0000000..5246ac7 --- /dev/null +++ b/src/main/java/org/uic/barcode/staticFrame/StaticFrame.java @@ -0,0 +1,764 @@ +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.barcode.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 dataRecords = new ArrayList(); + + + 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 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 = 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(); + } + + /** + * 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 = 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(); + + + } + + 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/main/java/org/uic/barcode/staticFrame/UFLEXDataRecord.java b/src/main/java/org/uic/barcode/staticFrame/UFLEXDataRecord.java new file mode 100644 index 0000000..fad2be8 --- /dev/null +++ b/src/main/java/org/uic/barcode/staticFrame/UFLEXDataRecord.java @@ -0,0 +1,90 @@ +package org.uic.barcode.staticFrame; + +import java.io.IOException; + +import org.uic.barcode.ticket.EncodingFormatException; +import org.uic.barcode.ticket.UicRailTicketCoder; +import org.uic.barcode.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/main/java/org/uic/barcode/staticFrame/UHEADDataRecord.java b/src/main/java/org/uic/barcode/staticFrame/UHEADDataRecord.java new file mode 100644 index 0000000..855c9d5 --- /dev/null +++ b/src/main/java/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.barcode.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/main/java/org/uic/barcode/staticFrame/UTLAYDataRecord.java b/src/main/java/org/uic/barcode/staticFrame/UTLAYDataRecord.java new file mode 100644 index 0000000..549a2e7 --- /dev/null +++ b/src/main/java/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.staticFrame.ticketLayoutBarcode.FormatType; +import org.uic.barcode.staticFrame.ticketLayoutBarcode.LayoutElement; +import org.uic.barcode.staticFrame.ticketLayoutBarcode.TicketLayout; +import org.uic.barcode.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/main/java/org/uic/barcode/staticFrame/package.html b/src/main/java/org/uic/barcode/staticFrame/package.html new file mode 100644 index 0000000..b76540b --- /dev/null +++ b/src/main/java/org/uic/barcode/staticFrame/package.html @@ -0,0 +1,21 @@ + + + + + +

static bar code header frame

+ +

Provides an implementation of the static bar code header frame specified in UIC IRS 90918-9 including:

+ +

    +
  • encoding of the data for creating a bar code
  • +
  • decoding of data from bar code content
  • +
  • support for the additional header information required with the TLB content
  • +
  • support for the TLB bar code content
  • +
  • support for the FCB content (using the UIC FCB implementation)
  • +
  • support for bilaterally on unilaterally defined additional content
  • +
+

+ + + \ No newline at end of file diff --git a/src/main/java/org/uic/barcode/staticFrame/ticketLayoutBarcode/FormatType.java b/src/main/java/org/uic/barcode/staticFrame/ticketLayoutBarcode/FormatType.java new file mode 100644 index 0000000..e6b9414 --- /dev/null +++ b/src/main/java/org/uic/barcode/staticFrame/ticketLayoutBarcode/FormatType.java @@ -0,0 +1,34 @@ +package org.uic.barcode.staticFrame.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/main/java/org/uic/barcode/staticFrame/ticketLayoutBarcode/LayoutElement.java b/src/main/java/org/uic/barcode/staticFrame/ticketLayoutBarcode/LayoutElement.java new file mode 100644 index 0000000..861629d --- /dev/null +++ b/src/main/java/org/uic/barcode/staticFrame/ticketLayoutBarcode/LayoutElement.java @@ -0,0 +1,50 @@ +package org.uic.barcode.staticFrame.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/main/java/org/uic/barcode/staticFrame/ticketLayoutBarcode/TicketLayout.java b/src/main/java/org/uic/barcode/staticFrame/ticketLayoutBarcode/TicketLayout.java new file mode 100644 index 0000000..8ca8083 --- /dev/null +++ b/src/main/java/org/uic/barcode/staticFrame/ticketLayoutBarcode/TicketLayout.java @@ -0,0 +1,61 @@ +package org.uic.barcode.staticFrame.ticketLayoutBarcode; + +import java.util.ArrayList; +import java.util.List; + +public class TicketLayout { + + private String layoutStandard = "RCT2"; + + /** The layout elements. */ + private List elements = new ArrayList(); + + + /** + * 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 getElements(){ + return elements; + } + + +} -- cgit v1.2.3