From b17ce13cc31abd088088bf8ab26cc924a6a36585 Mon Sep 17 00:00:00 2001 From: CGantert345 <57003061+CGantert345@users.noreply.github.com> Date: Tue, 28 Jul 2020 17:57:25 +0200 Subject: Draft of the new DOSIPAS included --- src/org/uic/barcode/Decoder.java | 232 +++++++ src/org/uic/barcode/Encoder.java | 259 +++++++ src/org/uic/barcode/dynamicFrame/Constants.java | 38 + src/org/uic/barcode/dynamicFrame/DataType.java | 95 +++ src/org/uic/barcode/dynamicFrame/DynamicFrame.java | 263 +++++++ .../uic/barcode/dynamicFrame/Level1DataType.java | 218 ++++++ .../uic/barcode/dynamicFrame/Level2DataType.java | 98 +++ .../barcode/dynamicFrame/SequenceOfDataType.java | 26 + src/org/uic/barcode/dynamicFrame/headerSpec.asn | 119 ++++ src/org/uic/barcode/dynamicFrame/package.html | 9 + src/org/uic/barcode/package.html | 21 + src/org/uic/barcode/staticFrame/DataRecord.java | 199 ++++++ .../uic/barcode/staticFrame/GENERICDataRecord.java | 62 ++ src/org/uic/barcode/staticFrame/StaticFrame.java | 762 +++++++++++++++++++++ .../uic/barcode/staticFrame/UFLEXDataRecord.java | 90 +++ .../uic/barcode/staticFrame/UHEADDataRecord.java | 269 ++++++++ .../uic/barcode/staticFrame/UTLAYDataRecord.java | 272 ++++++++ src/org/uic/barcode/staticFrame/package.html | 21 + .../ticketLayoutBarcode/FormatType.java | 34 + .../ticketLayoutBarcode/LayoutElement.java | 50 ++ .../ticketLayoutBarcode/TicketLayout.java | 61 ++ .../test/DynamicFrameDoubleSignatureTest.java | 212 ++++++ .../uic/barcode/test/DynamicFrameLOwLevelTest.java | 116 ++++ .../uic/barcode/test/DynamicFrameSimpleTest.java | 171 +++++ src/org/uic/barcode/test/SignatureSplitTest.java | 76 ++ .../uic/barcode/test/StaticFrameBarcodeTest.java | 193 ++++++ src/org/uic/barcode/test/TicketLayoutTest.java | 45 ++ .../barcode/test/utils/Level2TestDataFactory.java | 20 + .../test/utils/SimpleDynamicFrameTestBarcode.java | 80 +++ .../barcode/test/utils/SimpleTestTicketLayout.java | 33 + .../barcode/test/utils/SimpleUICTestTicket.java | 254 +++++++ src/org/uic/barcode/test/utils/TestUtils.java | 38 + .../uic/barcode/utils/AlgorithmNameResolver.java | 173 +++++ src/org/uic/barcode/utils/package.html | 20 + 34 files changed, 4629 insertions(+) create mode 100644 src/org/uic/barcode/Decoder.java create mode 100644 src/org/uic/barcode/Encoder.java create mode 100644 src/org/uic/barcode/dynamicFrame/Constants.java create mode 100644 src/org/uic/barcode/dynamicFrame/DataType.java create mode 100644 src/org/uic/barcode/dynamicFrame/DynamicFrame.java create mode 100644 src/org/uic/barcode/dynamicFrame/Level1DataType.java create mode 100644 src/org/uic/barcode/dynamicFrame/Level2DataType.java create mode 100644 src/org/uic/barcode/dynamicFrame/SequenceOfDataType.java create mode 100644 src/org/uic/barcode/dynamicFrame/headerSpec.asn create mode 100644 src/org/uic/barcode/dynamicFrame/package.html create mode 100644 src/org/uic/barcode/package.html create mode 100644 src/org/uic/barcode/staticFrame/DataRecord.java create mode 100644 src/org/uic/barcode/staticFrame/GENERICDataRecord.java create mode 100644 src/org/uic/barcode/staticFrame/StaticFrame.java create mode 100644 src/org/uic/barcode/staticFrame/UFLEXDataRecord.java create mode 100644 src/org/uic/barcode/staticFrame/UHEADDataRecord.java create mode 100644 src/org/uic/barcode/staticFrame/UTLAYDataRecord.java create mode 100644 src/org/uic/barcode/staticFrame/package.html create mode 100644 src/org/uic/barcode/staticHeader/ticketLayoutBarcode/FormatType.java create mode 100644 src/org/uic/barcode/staticHeader/ticketLayoutBarcode/LayoutElement.java create mode 100644 src/org/uic/barcode/staticHeader/ticketLayoutBarcode/TicketLayout.java create mode 100644 src/org/uic/barcode/test/DynamicFrameDoubleSignatureTest.java create mode 100644 src/org/uic/barcode/test/DynamicFrameLOwLevelTest.java create mode 100644 src/org/uic/barcode/test/DynamicFrameSimpleTest.java create mode 100644 src/org/uic/barcode/test/SignatureSplitTest.java create mode 100644 src/org/uic/barcode/test/StaticFrameBarcodeTest.java create mode 100644 src/org/uic/barcode/test/TicketLayoutTest.java create mode 100644 src/org/uic/barcode/test/utils/Level2TestDataFactory.java create mode 100644 src/org/uic/barcode/test/utils/SimpleDynamicFrameTestBarcode.java create mode 100644 src/org/uic/barcode/test/utils/SimpleTestTicketLayout.java create mode 100644 src/org/uic/barcode/test/utils/SimpleUICTestTicket.java create mode 100644 src/org/uic/barcode/test/utils/TestUtils.java create mode 100644 src/org/uic/barcode/utils/AlgorithmNameResolver.java create mode 100644 src/org/uic/barcode/utils/package.html (limited to 'src/org/uic/barcode') diff --git a/src/org/uic/barcode/Decoder.java b/src/org/uic/barcode/Decoder.java new file mode 100644 index 0000000..2ce417c --- /dev/null +++ b/src/org/uic/barcode/Decoder.java @@ -0,0 +1,232 @@ +package org.uic.barcode; + +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.SignatureException; +import java.util.zip.DataFormatException; + +import org.uic.barcode.dynamicFrame.Constants; +import org.uic.barcode.dynamicFrame.DataType; +import org.uic.barcode.dynamicFrame.DynamicFrame; +import org.uic.barcode.dynamicFrame.Level1DataType; +import org.uic.barcode.dynamicFrame.Level2DataType; +import org.uic.barcode.dynamicFrame.SequenceOfDataType; +import org.uic.barcode.staticFrame.StaticFrame; +import org.uic.barcode.staticFrame.UFLEXDataRecord; +import org.uic.barcode.staticFrame.UTLAYDataRecord; +import org.uic.barcode.staticHeader.ticketLayoutBarcode.TicketLayout; +import org.uic.ticket.EncodingFormatException; +import org.uic.ticket.UicRailTicketCoder; +import org.uic.ticket.api.spec.IUicRailTicket; + + +/** + * The Class Decoder. + * + * signature validation and decoding of UIC bar codes + * + */ +public class Decoder { + + + /** The dynamic frame. */ + private DynamicFrame dynamicFrame = null; + + /** The static frame. */ + private StaticFrame staticFrame = null; + + /** The uic ticket coder. */ + private UicRailTicketCoder uicTicketCoder = null; + + /** The uic ticket. */ + private IUicRailTicket uicTicket = null; + + /** The layout. */ + private TicketLayout layout = null; + + /** The data. */ + byte[] data = null; + + + /** + * Instantiates a new decoder. + * + * @param data the data + * @throws IOException Signals that an I/O exception has occurred. + * @throws EncodingFormatException the encoding format exception + * @throws DataFormatException the data format exception + */ + public Decoder (byte[] data) throws IOException, EncodingFormatException, DataFormatException { + this.data = data; + decode(data); + } + + /** + * Validate level 1. + * + * @param key the public key + * @param signingAlg the signing algorithm OID + * @return the return code indicating errors + * @throws InvalidKeyException the invalid key exception + * @throws NoSuchAlgorithmException the no such algorithm exception + * @throws SignatureException the signature exception + * @throws IllegalArgumentException the illegal argument exception + * @throws UnsupportedOperationException the unsupported operation exception + * @throws IOException Signals that an I/O exception has occurred. + * @throws EncodingFormatException the encoding format exception + */ + public int validateLevel1(PublicKey key, String signingAlg) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IllegalArgumentException, UnsupportedOperationException, IOException, EncodingFormatException { + if (!isStaticHeader(data)) { + return dynamicFrame.validateLevel1(key) ; + } else { + if (staticFrame.verifyByAlgorithmOid(key,signingAlg)) { + return Constants.LEVEL1_VALIDATION_OK; + } else { + return Constants.LEVEL1_VALIDATION_FRAUD; + } + } + } + + /** + * Validate level 2. + * + * @return the return code indicating errors + */ + public int validateLevel2() { + if (!isStaticHeader(data)) { + return dynamicFrame.validateLevel2() ; + } else { + return Constants.LEVEL2_VALIDATION_NO_SIGNATURE; + } + } + + /** + * Decode. + * + * @param data the byte array raw data + * @throws IOException Signals that an I/O exception has occurred. + * @throws EncodingFormatException the encoding format exception + * @throws DataFormatException the data format exception + */ + public void decode(byte[] data) throws IOException, EncodingFormatException, DataFormatException { + + if (!isStaticHeader(data)) { + + dynamicFrame = DynamicFrame.decode(data); + + Level2DataType level2 = dynamicFrame.getLevel2SignedData(); + + Level1DataType level1 = level2.getLevel1Data(); + + SequenceOfDataType dataList = level1.getData(); + + for (DataType level1Content : dataList) { + + uicTicketCoder = new UicRailTicketCoder(); + if (level1Content.getFormat().equals("FCB1")) { + uicTicket = uicTicketCoder.decodeFromAsn(level1Content.getByteData(), 1); + } else if (level1Content.getFormat().equals("FCB2")) { + uicTicket = uicTicketCoder.decodeFromAsn(level1Content.getByteData(), 2); + } + } + + } else if (isStaticHeader(data)){ + + staticFrame = new StaticFrame(); + + staticFrame.decode(data); + + UFLEXDataRecord flex = staticFrame.getuFlex(); + + if (flex != null) { + uicTicket = flex.getTicket(); + } + + UTLAYDataRecord tlay = staticFrame.getuTlay(); + + if (tlay != null) { + layout = tlay.getLayout(); + } + } + } + + + /** + * Checks if is static header. + * + * @param data the data + * @return true, if is static header + */ + private boolean isStaticHeader(byte[] data) { + byte[] start = "#UT".getBytes(); + if (start[0] != data[0] || start[1]!= start[1] || start[2] != data[2]) return false; + return true; + } + + /** + * Gets the uic ticket. + * + * @return the uic ticket + */ + public IUicRailTicket getUicTicket() { + return uicTicket; + } + + /** + * Gets the layout. + * + * @return the layout + */ + public TicketLayout getLayout() { + return layout; + } + + /** + * Gets the dynamic header. + * + * @return the dynamic header + */ + public DynamicFrame getDynamicHeader() { + return dynamicFrame; + } + + /** + * Sets the dynamic header. + * + * @param dynamicHeader the new dynamic header + */ + public void setDynamicHeader(DynamicFrame dynamicHeader) { + this.dynamicFrame = dynamicHeader; + } + + /** + * Gets the static frame. + * + * @return the static frame + */ + public StaticFrame getStaticFrame() { + return staticFrame; + } + + /** + * Sets the static frame. + * + * @param staticFrame the new static frame + */ + public void setStaticFrame(StaticFrame staticFrame) { + this.staticFrame = staticFrame; + } + + public DataType getLevel2Data() { + if (!isStaticHeader(data) && dynamicFrame.getLevel2SignedData() != null) { + return dynamicFrame.getLevel2SignedData().getLevel2Data(); + } + return null; + } + + + + +} diff --git a/src/org/uic/barcode/Encoder.java b/src/org/uic/barcode/Encoder.java new file mode 100644 index 0000000..301b7c5 --- /dev/null +++ b/src/org/uic/barcode/Encoder.java @@ -0,0 +1,259 @@ +package org.uic.barcode; + +import java.io.IOException; +import java.security.PrivateKey; +import java.security.PublicKey; + +import org.uic.barcode.dynamicFrame.Constants; +import org.uic.barcode.dynamicFrame.DataType; +import org.uic.barcode.dynamicFrame.DynamicFrame; +import org.uic.barcode.dynamicFrame.Level1DataType; +import org.uic.barcode.dynamicFrame.Level2DataType; +import org.uic.barcode.dynamicFrame.SequenceOfDataType; +import org.uic.barcode.staticFrame.StaticFrame; +import org.uic.barcode.staticFrame.UFLEXDataRecord; +import org.uic.barcode.staticFrame.UHEADDataRecord; +import org.uic.barcode.staticFrame.UTLAYDataRecord; +import org.uic.barcode.staticHeader.ticketLayoutBarcode.TicketLayout; +import org.uic.ticket.EncodingFormatException; +import org.uic.ticket.UicRailTicketCoder; +import org.uic.ticket.api.spec.IUicRailTicket; + +import net.gcdc.asn1.datatypesimpl.OctetString; + + +/** + * The Class Encoder. + * + * signing and encoding of UIC bar codes + * + * + */ +public class Encoder { + + /** The dynamic frame. */ + private DynamicFrame dynamicFrame = null; + + /** The static frame. */ + private StaticFrame staticFrame = null; + + + /** The UIC bar code type classic. */ + public static String UIC_BARCODE_TYPE_CLASSIC = "UIC_CLASSIC"; + + /** The UIC bar code type DOSIPAS. */ + public static String UIC_BARCODE_TYPE_DOSIPAS = "UIC_DOSIPAS"; + + /** + * Instantiates a new encoder. + * + * @param ticket the ticket + * @param layout the layout + * @param barcodeType the bar code type + * @param version the version of the bar code + * @param fcbVersion the fcb version + * @throws IOException Signals that an I/O exception has occurred. + * @throws EncodingFormatException the encoding format exception + */ + public Encoder(IUicRailTicket ticket, TicketLayout layout, String barcodeType, int version, int fcbVersion) throws IOException, EncodingFormatException { + + if (barcodeType == UIC_BARCODE_TYPE_CLASSIC) { + + staticFrame = new StaticFrame(); + staticFrame.setVersion(version); + + if (layout != null) { + + UHEADDataRecord head = new UHEADDataRecord(); + head.setVersionId("01"); + staticFrame.setHeaderRecord(head); + + UTLAYDataRecord tlay = new UTLAYDataRecord(); + tlay.setLayout(layout); + tlay.setVersionId("01"); + staticFrame.setuTlay(tlay); + } + + if (ticket != null) { + + UFLEXDataRecord flex = new UFLEXDataRecord(); + flex.setTicket(ticket); + flex.setVersionId(String.format("%02d",fcbVersion)); + staticFrame.setuFlex(flex); + } + + + } else if (barcodeType == UIC_BARCODE_TYPE_DOSIPAS) { + + dynamicFrame = new DynamicFrame(); + dynamicFrame.setLevel2SignedData(new Level2DataType()); + dynamicFrame.getLevel2SignedData().setLevel1Data(new Level1DataType()); + dynamicFrame.getLevel2SignedData().getLevel1Data().setData(new SequenceOfDataType()); + + if (ticket != null) { + + if (version == 1) { + dynamicFrame.setFormat("U1"); + } + + DataType ticketData = new DataType(); + + UicRailTicketCoder uicTicketCoder = new UicRailTicketCoder(); + ticketData.setFormat(Constants.DATA_TYPE_FCB_VERSION_1); + ticketData.setData(new OctetString(uicTicketCoder.encode(ticket, fcbVersion))); + dynamicFrame.getLevel2SignedData().getLevel1Data().getData().add(ticketData); + + } + } + } + + + + /** + * Signing level 2 of a dynamic bar code + * + * @param key the key + * @throws Exception the exception + */ + public void signLevel2(PrivateKey key) throws Exception { + if (dynamicFrame != null) { + dynamicFrame.signLevel2(key); + } + } + + /** + * Sets the level 1 algorithm Is. + * + * @param level1SigningAlg the level 1 signing algorithm (OID) + * @param level1KeyAlg the level 1 key algorithm (OID) + */ + public void setLevel1Algs(String level1SigningAlg, String level1KeyAlg) { + if (dynamicFrame != null) { + dynamicFrame.getLevel2SignedData().getLevel1Data().setLevel1SigningAlg(level1SigningAlg); + dynamicFrame.getLevel2SignedData().getLevel1Data().setLevel1KeyAlg(level1KeyAlg); + } + } + + /** + * Sets the level 2 algorithm Is. + * + * @param level2SigningAlg the level 2 signing algorithm (OID) + * @param level2KeyAlg the level 2 key algorithm (OID) + * @param publicKey the public key of the level 2 signature + */ + public void setLevel2Algs(String level2SigningAlg, String level2KeyAlg, PublicKey publicKey) { + if (dynamicFrame != null) { + if (dynamicFrame.getLevel2SignedData() == null) { + dynamicFrame.setLevel2SignedData(new Level2DataType()); + } + if (dynamicFrame.getLevel2SignedData().getLevel1Data() == null) { + dynamicFrame.getLevel2SignedData().setLevel1Data(new Level1DataType()); + } + dynamicFrame.getLevel2SignedData().getLevel1Data().setLevel2SigningAlg(level2SigningAlg); + dynamicFrame.getLevel2SignedData().getLevel1Data().setLevel2KeyAlg(level2KeyAlg); + if (publicKey != null) { + dynamicFrame.getLevel2SignedData().getLevel1Data().setLevel2publicKey(new OctetString(publicKey.getEncoded())); + } + } + } + + public void setLevel2Data(DataType level2data) { + if (dynamicFrame != null) { + if (dynamicFrame.getLevel2SignedData() == null) { + dynamicFrame.setLevel2SignedData(new Level2DataType()); + } + dynamicFrame.getLevel2SignedData().setLevel2Data(level2data); + } + } + + public DataType getLevel2Data() { + if (dynamicFrame != null && dynamicFrame.getLevel2SignedData() != null) { + return dynamicFrame.getLevel2SignedData().getLevel2Data(); + } + return null; + } + + /** + * Sign level 1 of a dynamic bar code or a static bar code. + * + * @param securityProvider the security provider + * @param key the key + * @param signingAlg the signing algorithm (OID) + * @param keyId the key id + * @throws Exception the exception + */ + public void signLevel1(String securityProvider,PrivateKey key,String signingAlg, String keyId) throws Exception { + if (dynamicFrame != null) { + dynamicFrame.getLevel2SignedData().getLevel1Data().setSecurityProvider(securityProvider); + dynamicFrame.getLevel2SignedData().getLevel1Data().setLevel1SigningAlg(signingAlg); + dynamicFrame.getLevel2SignedData().getLevel1Data().setKeyId(Long.parseLong(keyId)); + dynamicFrame.getLevel2SignedData().signLevel1(key); + } else if (staticFrame != null) { + staticFrame.setSignatureKey(keyId); + staticFrame.setSecurityProvider(securityProvider); + if (staticFrame.getHeaderRecord()!= null && staticFrame.getHeaderRecord().getIssuer() == null) { + staticFrame.getHeaderRecord().setIssuer(securityProvider); + } + staticFrame.signByAlgorithmOID(key,signingAlg); + } + } + + /** + * Sets the static header parameter. + * + * @param ticketId the ticket id + * @param language the language + */ + public void setStaticHeaderParams(String ticketId,String language) { + if (staticFrame != null && staticFrame.getHeaderRecord()!= null) { + staticFrame.getHeaderRecord().setIdentifier(ticketId); + staticFrame.getHeaderRecord().setLanguage(language); + } + } + + /** + * Gets the dynamic frame. + * + * @return the dynamic frame + */ + public DynamicFrame getDynamicFrame() { + return dynamicFrame; + } + + + + /** + * Gets the static frame. + * + * @return the static frame + */ + public StaticFrame getStaticFrame() { + return staticFrame; + } + + + + /** + * Encodes the signed bar code data + * + * @return the byte[] + * @throws IOException Signals that an I/O exception has occurred. + * @throws Exception the exception + */ + public byte[] encode() throws IOException, Exception { + if (dynamicFrame != null) { + return dynamicFrame.encode(); + } else if (staticFrame != null) { + return staticFrame.encode(); + } + return null; + } + + + + + + + + +} diff --git a/src/org/uic/barcode/dynamicFrame/Constants.java b/src/org/uic/barcode/dynamicFrame/Constants.java new file mode 100644 index 0000000..98b62aa --- /dev/null +++ b/src/org/uic/barcode/dynamicFrame/Constants.java @@ -0,0 +1,38 @@ +package org.uic.barcode.dynamicFrame; + +public class Constants { + + /* + * Object Identifier for recommended signature algorithms + * + */ + public static String KG_EC_256 = "1.2.840.10045.3.1.7"; + public static String KG_EC = "1.2.840.10045.2.1"; + public static String ECDSA_SHA256 = "1.2.840.10045.4.3.2"; + + public static String DSA_SHA1 = "1.2.840.10040.4.3"; + public static String DSA_SHA224 = "2.16.840.1.101.3.4.3.1"; + public static String DSA_SHA248 = "2.16.840.1.101.3.4.3.2"; + + public static String DATA_TYPE_FCB_VERSION_1 = "FCB1"; + public static String DATA_TYPE_FCB_VERSION_2 = "FCB2"; + + public static String DYNAMIC_BARCODE_FORMAT_DEFAULT = "U1"; + + public static int LEVEL2_VALIDATION_OK = 0; + public static int LEVEL2_VALIDATION_NO_KEY = 1; + public static int LEVEL2_VALIDATION_NO_SIGNATURE = 2; + public static int LEVEL2_VALIDATION_FRAUD = 3; + public static int LEVEL2_VALIDATION_SIG_ALG_NOT_IMPLEMENTED = 4; + public static int LEVEL2_VALIDATION_KEY_ALG_NOT_IMPLEMENTED = 5; + public static int LEVEL2_VALIDATION_ENCODING_ERROR = 6; + + public static int LEVEL1_VALIDATION_OK = 0; + public static int LEVEL1_VALIDATION_NO_KEY = 1; + public static int LEVEL1_VALIDATION_NO_SIGNATURE = 2; + public static int LEVEL1_VALIDATION_FRAUD = 3; + public static int LEVEL1_VALIDATION_SIG_ALG_NOT_IMPLEMENTED = 4; + public static int LEVEL1_VALIDATION_KEY_ALG_NOT_IMPLEMENTED = 5; + public static int LEVEL1_VALIDATION_ENCODING_ERROR = 6; + +} diff --git a/src/org/uic/barcode/dynamicFrame/DataType.java b/src/org/uic/barcode/dynamicFrame/DataType.java new file mode 100644 index 0000000..fb84db6 --- /dev/null +++ b/src/org/uic/barcode/dynamicFrame/DataType.java @@ -0,0 +1,95 @@ +package org.uic.barcode.dynamicFrame; + +import net.gcdc.asn1.datatypes.Asn1Default; +import net.gcdc.asn1.datatypes.CharacterRestriction; +import net.gcdc.asn1.datatypes.RestrictedString; +import net.gcdc.asn1.datatypes.Sequence; +import net.gcdc.asn1.datatypesimpl.OctetString; +import net.gcdc.asn1.uper.UperEncoder; + +/** + * The Class DataType. + */ +@Sequence +public class DataType { + + + /** The data format. + * + * -- FCB1 FCB version 1 + * -- FCB2 FCB version 2 + * -- RICS company code + ... + **/ + @Asn1Default("FCB1") + @RestrictedString(CharacterRestriction.IA5String) + public String format; + + /** The data. */ + public OctetString data; + + /** + * Gets the data format. + * + * @return the data format + */ + public String getFormat() { + return format; + } + + /** + * Sets the data format. + * + * @param dataFormat the new data format + */ + public void setFormat(String format) { + this.format = format; + } + + /** + * Gets the data. + * + * @return the data + */ + public OctetString getData() { + return data; + } + + /** + * Sets the data. + * + * @param data the new data + */ + public void setData(OctetString data) { + this.data = data; + } + + /** + * Gets the data as byte array. + * + * @return the data + */ + public byte[] getByteData() { + return data.toByteArray(); + } + + /** + * Sets the data from a byte array. + * + * @param data the new data + */ + public void setByteData(byte[] data) { + this.data = new OctetString(data); + } + + /** + * Encode. + * + * Encode the header as ASN.1 PER UNALIGNED byte array + * + * @return the byte[] + */ + public byte[] encode() { + return UperEncoder.encode(this); + } + +} diff --git a/src/org/uic/barcode/dynamicFrame/DynamicFrame.java b/src/org/uic/barcode/dynamicFrame/DynamicFrame.java new file mode 100644 index 0000000..445990a --- /dev/null +++ b/src/org/uic/barcode/dynamicFrame/DynamicFrame.java @@ -0,0 +1,263 @@ +package org.uic.barcode.dynamicFrame; + +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +import java.security.SignatureException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; + +import org.uic.barcode.utils.AlgorithmNameResolver; + +import net.gcdc.asn1.datatypes.Asn1Optional; +import net.gcdc.asn1.datatypes.CharacterRestriction; +import net.gcdc.asn1.datatypes.HasExtensionMarker; +import net.gcdc.asn1.datatypes.RestrictedString; +import net.gcdc.asn1.datatypes.Sequence; +import net.gcdc.asn1.datatypesimpl.OctetString; +import net.gcdc.asn1.uper.UperEncoder; + + +/** + * The DynamicHeader for bar codes + * + * Implementation of the Draft under discussion, not final. + */ +@Sequence +@HasExtensionMarker +public class DynamicFrame extends Object{ + + public DynamicFrame() {} + + /** The format. */ + @RestrictedString(CharacterRestriction.IA5String) + public String format; + + /*level 2 data*/ + Level2DataType level2SignedData; + + + /** The signature of level 2 data*/ + @Asn1Optional public OctetString level2Signature; + + /** + * Gets the format. + * + * @return the format + */ + public String getFormat() { + return format; + } + + /** + * Sets the format. + * + * @param format the new format + */ + public void setFormat(String format) { + this.format = format; + } + + public Level2DataType getLevel2SignedData() { + return level2SignedData; + } + + public void setLevel2SignedData(Level2DataType level2SignedData) { + this.level2SignedData = level2SignedData; + } + + public OctetString getLevel2Signature() { + return level2Signature; + } + + public void setLevel2Signature(OctetString level2Signature) { + this.level2Signature = level2Signature; + } + + /** + * Encode. + * + * Encode the header as ASN.1 PER UNALIGNED byte array + * + * @return the byte[] + */ + public byte[] encode() { + return UperEncoder.encode(this); + } + + /** + * Decode. + * + * Decode the header from an ASN.1 PER UNALIGNED encoded byte array + * + * @param bytes the bytes + * @return the dynamic header + */ + public static DynamicFrame decode(byte[] bytes) { + return UperEncoder.decode(bytes, DynamicFrame.class); + } + + /** + * Verify the level 2 signature + * + * Note: an appropriate security provider (e.g. BC) must be registered before + * + */ + public int validateLevel2() { + + + String level2KeyAlg = this.getLevel2SignedData().getLevel1Data().level2KeyAlg; + + if (level2KeyAlg == null || level2KeyAlg.length() == 0) { + return Constants.LEVEL2_VALIDATION_NO_KEY; + } + + if (this.level2Signature.toByteArray() == null || this.level2Signature.toByteArray().length == 0) { + return Constants.LEVEL2_VALIDATION_NO_SIGNATURE; + } + + + + String keyAlgName = null; + try { + keyAlgName = AlgorithmNameResolver.getName(AlgorithmNameResolver.TYPE_KEY_GENERATOR_ALG, level2KeyAlg); + } catch (Exception e1) { + return Constants.LEVEL2_VALIDATION_KEY_ALG_NOT_IMPLEMENTED; + } + if (keyAlgName == null || keyAlgName.length() == 0) { + return Constants.LEVEL2_VALIDATION_KEY_ALG_NOT_IMPLEMENTED; + } + + PublicKey key = null; + try { + key = KeyFactory.getInstance(keyAlgName).generatePublic(new X509EncodedKeySpec(this.getLevel2SignedData().getLevel1Data().level2publicKey.toByteArray())); + } catch (InvalidKeySpecException | NoSuchAlgorithmException e1) { + return Constants.LEVEL2_VALIDATION_KEY_ALG_NOT_IMPLEMENTED; + } + + //find the algorithm name for the signature OID + String algo = null; + try { + algo = AlgorithmNameResolver.getName(AlgorithmNameResolver.TYPE_SIGNATURE_ALG,this.getLevel2SignedData().getLevel1Data().level2SigningAlg); + } catch (Exception e1) { + return Constants.LEVEL2_VALIDATION_SIG_ALG_NOT_IMPLEMENTED; + } + if (algo == null) { + return Constants.LEVEL2_VALIDATION_SIG_ALG_NOT_IMPLEMENTED; + } + + Signature sig; + try { + sig = Signature.getInstance(algo); + } catch (NoSuchAlgorithmException e) { + return Constants.LEVEL2_VALIDATION_SIG_ALG_NOT_IMPLEMENTED; + } + try { + sig.initVerify(key); + } catch (InvalidKeyException e) { + return Constants.LEVEL2_VALIDATION_SIG_ALG_NOT_IMPLEMENTED; + } + + try { + sig.update(UperEncoder.encode(level2SignedData)); + } catch (SignatureException e) { + return Constants.LEVEL2_VALIDATION_SIG_ALG_NOT_IMPLEMENTED; + } catch (IllegalArgumentException e) { + return Constants.LEVEL2_VALIDATION_ENCODING_ERROR; + } catch (UnsupportedOperationException e) { + return Constants.LEVEL2_VALIDATION_ENCODING_ERROR; + } + + byte[] signature = level2Signature.toByteArray(); + try { + if (sig.verify(signature)){ + return Constants.LEVEL2_VALIDATION_OK; + } else { + return Constants.LEVEL2_VALIDATION_FRAUD; + } + } catch (SignatureException e) { + return Constants.LEVEL2_VALIDATION_SIG_ALG_NOT_IMPLEMENTED; + } + } + + /** + * Verify the level 1 signature + * + * Note: an appropriate security provider (e.g. BC) must be registered before + * + */ + public int validateLevel1(PublicKey key) { + + if (this.level2SignedData == null) { + return Constants.LEVEL1_VALIDATION_NO_SIGNATURE; + } + + + if (this.level2SignedData.level1Signature == null || this.level2SignedData.level1Signature.toByteArray().length == 0) { + return Constants.LEVEL1_VALIDATION_NO_SIGNATURE; + } + + byte[] signature = this.getLevel2SignedData().level1Signature.toByteArray(); + + //find the algorithm name for the signature OID + String algo = null; + try { + algo = AlgorithmNameResolver.getSignatureAlgorithmName(getLevel2SignedData().getLevel1Data().level1SigningAlg); + } catch (Exception e1) { + return Constants.LEVEL1_VALIDATION_SIG_ALG_NOT_IMPLEMENTED; + } + if (algo == null) { + return Constants.LEVEL1_VALIDATION_SIG_ALG_NOT_IMPLEMENTED; + } + + Signature sig; + try { + sig = Signature.getInstance(algo); + } catch (NoSuchAlgorithmException e) { + return Constants.LEVEL1_VALIDATION_SIG_ALG_NOT_IMPLEMENTED; + } + try { + sig.initVerify(key); + } catch (InvalidKeyException e) { + return Constants.LEVEL1_VALIDATION_SIG_ALG_NOT_IMPLEMENTED; + } + + try { + sig.update(this.level2SignedData.level1Data.encode()); + } catch (SignatureException e) { + return Constants.LEVEL1_VALIDATION_SIG_ALG_NOT_IMPLEMENTED; + } catch (IllegalArgumentException e) { + return Constants.LEVEL1_VALIDATION_ENCODING_ERROR; + } catch (UnsupportedOperationException e) { + return Constants.LEVEL1_VALIDATION_ENCODING_ERROR; + } + + + try { + if (sig.verify(signature)){ + return Constants.LEVEL2_VALIDATION_OK; + } else { + return Constants.LEVEL2_VALIDATION_FRAUD; + } + } catch (SignatureException e) { + return Constants.LEVEL2_VALIDATION_SIG_ALG_NOT_IMPLEMENTED; + } + } + + public void signLevel2(PrivateKey key) throws Exception { + + //find the algorithm name for the signature OID + String algo = AlgorithmNameResolver.getSignatureAlgorithmName(this.getLevel2SignedData().getLevel1Data().level2SigningAlg); + Signature sig = Signature.getInstance(algo); + sig.initSign(key); + byte[] data = level2SignedData.encode(); + sig.update(data); + this.level2Signature = new OctetString(sig.sign()); + + } + + +} diff --git a/src/org/uic/barcode/dynamicFrame/Level1DataType.java b/src/org/uic/barcode/dynamicFrame/Level1DataType.java new file mode 100644 index 0000000..ac48ba8 --- /dev/null +++ b/src/org/uic/barcode/dynamicFrame/Level1DataType.java @@ -0,0 +1,218 @@ +package org.uic.barcode.dynamicFrame; + +import org.uic.ticket.EncodingFormatException; +import org.uic.ticket.api.utils.UicEncoderUtils; + +import net.gcdc.asn1.datatypes.Asn1Optional; +import net.gcdc.asn1.datatypes.CharacterRestriction; +import net.gcdc.asn1.datatypes.IntRange; +import net.gcdc.asn1.datatypes.RestrictedString; +import net.gcdc.asn1.datatypes.Sequence; +import net.gcdc.asn1.datatypesimpl.OctetString; +import net.gcdc.asn1.uper.UperEncoder; + +/** + * The Class SignedDataType. + */ +@Sequence +public class Level1DataType { + + /** + * The security provider + * numeric codes 1 ...32000 + * + * */ + @IntRange(minValue=1,maxValue=32000) + @Asn1Optional public Long securityProviderNum; + + /** The security provider alphanumeric codes. */ + @RestrictedString(CharacterRestriction.IA5String) + @Asn1Optional public String securityProviderIA5; + + + /** The key id. */ + @IntRange(minValue=1,maxValue=99999) + @Asn1Optional public Long keyId; + + + /** The data. */ + public SequenceOfDataType data; + + /** + * The key generator algorithms + * Object Identifier of the Algorithm + * Number notation: + * + * e.g.: + * -- DSA SHA224 2.16.840.1.101.3.4.3.1 + * -- DSA SHA248 2.16.840.1.101.3.4.3.2 + * -- ECC 256 1.2.840.10045.3.1.7 + * + * + */ + @RestrictedString(CharacterRestriction.ObjectIdentifier) + @Asn1Optional public String level1KeyAlg; + + + @RestrictedString(CharacterRestriction.ObjectIdentifier) + @Asn1Optional public String level2KeyAlg; + + /** + * The signing algorithm + * Object Identifier of the Algorithms + * Number notation: + * + * e.g.: + * -- DSA SHA224 2.16.840.1.101.3.4.3.1 + * -- DSA SHA248 2.16.840.1.101.3.4.3.2 + * -- ECC 256 1.2.840.10045.3.1.7 + * + * + */ + @RestrictedString(CharacterRestriction.ObjectIdentifier) + @Asn1Optional public String level1SigningAlg; + + @RestrictedString(CharacterRestriction.ObjectIdentifier) + @Asn1Optional public String level2SigningAlg; + + + /** The level 2 public key*/ + @Asn1Optional public OctetString level2publicKey; + + + + /** + * Gets the security provider num. + * + * @return the security provider num + */ + public Long getSecurityProviderNum() { + return securityProviderNum; + } + + /** + * Sets the security provider num. + * + * in case the security provider code is encoded in IA5 this will return null + * + * @param securityProviderNum the new security provider num + */ + public void setSecurityProviderNum(Long securityProviderNum) { + this.securityProviderNum = securityProviderNum; + } + + /** + * Gets the security provider IA5. + * + * in case the security provider code is encoded numerically this will return null + * + * @return the security provider IA5 + */ + public String getSecurityProviderIA5() { + return securityProviderIA5; + } + + /** + * Sets the security provider + * + * The security provider code must use the IA5 Alphabet . + * + * @param securityProvider the new security provider + * @throws EncodingFormatException the encoding format exception + */ + public void setSecurityProvider(String securityProvider) throws EncodingFormatException { + this.securityProviderNum = UicEncoderUtils.getNum(securityProvider); + this.securityProviderIA5 = UicEncoderUtils.getIA5NonNum(securityProvider); + } + + + /** + * Gets the security provider. + * + * @return the security provider + */ + public String getSecurityProvider() { + return UicEncoderUtils.mapToString(this.securityProviderNum, this.securityProviderIA5); + } + + + /** + * Sets the security provider IA 5. + * + * @param securityProviderIA5 the new security provider IA 5 + */ + public void setSecurityProviderIA5(String securityProviderIA5) { + this.securityProviderIA5 = securityProviderIA5; + } + + public Long getKeyId() { + return keyId; + } + + public void setKeyId(Long keyId) { + this.keyId = keyId; + } + + public SequenceOfDataType getData() { + return data; + } + + public void setData(SequenceOfDataType data) { + this.data = data; + } + + public String getLevel2KeyAlg() { + return level2KeyAlg; + } + + public void setLevel2KeyAlg(String level2KeyAlg) { + this.level2KeyAlg = level2KeyAlg; + } + + public String getLevel1SigningAlg() { + return level1SigningAlg; + } + + public void setLevel1SigningAlg(String level1SigningAlg) { + this.level1SigningAlg = level1SigningAlg; + } + + public String getLevel2SigningAlg() { + return level2SigningAlg; + } + + public void setLevel2SigningAlg(String level2SigningAlg) { + this.level2SigningAlg = level2SigningAlg; + } + + public OctetString getLevel2publicKey() { + return level2publicKey; + } + + public void setLevel2publicKey(OctetString level2publicKey) { + this.level2publicKey = level2publicKey; + } + + + + public String getLevel1KeyAlg() { + return level1KeyAlg; + } + + public void setLevel1KeyAlg(String level1KeyAlg) { + this.level1KeyAlg = level1KeyAlg; + } + + /** + * Gets the data for signature. + * + * The byte array containing the ASN.1 PER UNALIGNED encoded data of the DataBlock + * + * + * @return the data for signature + */ + public byte[] encode() { + return UperEncoder.encode(this); + + } +} diff --git a/src/org/uic/barcode/dynamicFrame/Level2DataType.java b/src/org/uic/barcode/dynamicFrame/Level2DataType.java new file mode 100644 index 0000000..66e4225 --- /dev/null +++ b/src/org/uic/barcode/dynamicFrame/Level2DataType.java @@ -0,0 +1,98 @@ +package org.uic.barcode.dynamicFrame; + +import java.security.PrivateKey; +import java.security.Signature; + +import org.uic.barcode.utils.AlgorithmNameResolver; + +import net.gcdc.asn1.datatypes.Asn1Optional; +import net.gcdc.asn1.datatypes.Sequence; +import net.gcdc.asn1.datatypesimpl.OctetString; +import net.gcdc.asn1.uper.UperEncoder; + +/** + * The Class DataType. + */ +@Sequence +public class Level2DataType { + + Level1DataType level1Data; + + /** The data. */ + @Asn1Optional public OctetString level1Signature; + + + @Asn1Optional DataType level2Data; + + + public Level1DataType getLevel1Data() { + return level1Data; + } + + + public void setLevel1Data(Level1DataType level1Data) { + this.level1Data = level1Data; + } + + + public OctetString getLevel1Signature() { + return level1Signature; + } + + public byte[] getLevel1SignatureBytes() { + return level1Signature.toByteArray(); + } + + public void setLevel1Signature(OctetString level1Signature) { + this.level1Signature = level1Signature; + } + + public void setLevel1Signature(byte[] level1Signature) { + this.level1Signature = new OctetString(level1Signature); + } + + + public DataType getLevel2Data() { + return level2Data; + } + + + public void setLevel2Data(DataType level2Data) { + this.level2Data = level2Data; + } + + + /** + * Encode. + * + * Encode the header as ASN.1 PER UNALIGNED byte array + * + * @return the byte[] + */ + public byte[] encode() { + return UperEncoder.encode(this); + } + + /** + * Sign the contained data block. + * + * Note: an appropriate security provider (e.g. BC) must be registered before + * + * @param key the key + * @return + * @return the byte[] + * @throws Exception + */ + public void signLevel1(PrivateKey key) throws Exception { + //find the algorithm name for the signature OID + String algo = AlgorithmNameResolver.getSignatureAlgorithmName(getLevel1Data().level1SigningAlg); + Signature sig = Signature.getInstance(algo); + sig.initSign(key); + byte[] data = level1Data.encode(); + sig.update(data); + this.level1Signature = new OctetString(sig.sign()); + } + + + +} diff --git a/src/org/uic/barcode/dynamicFrame/SequenceOfDataType.java b/src/org/uic/barcode/dynamicFrame/SequenceOfDataType.java new file mode 100644 index 0000000..c879ddd --- /dev/null +++ b/src/org/uic/barcode/dynamicFrame/SequenceOfDataType.java @@ -0,0 +1,26 @@ +package org.uic.barcode.dynamicFrame; + + +import java.util.Collection; + +import net.gcdc.asn1.datatypes.Asn1SequenceOf; +// TODO: Auto-generated Javadoc + +/** + * The Class SequenceOfDataType. + */ +public class SequenceOfDataType extends Asn1SequenceOf{ + + /** + * Instantiates a new sequence of data type. + */ + public SequenceOfDataType() { super(); } + + /** + * Instantiates a new sequence of data type. + * + * @param coll the coll + */ + public SequenceOfDataType(Collection coll) { super(coll); } + +} diff --git a/src/org/uic/barcode/dynamicFrame/headerSpec.asn b/src/org/uic/barcode/dynamicFrame/headerSpec.asn new file mode 100644 index 0000000..08d84f6 --- /dev/null +++ b/src/org/uic/barcode/dynamicFrame/headerSpec.asn @@ -0,0 +1,119 @@ +-- Author: ClemensGantert +-- Created: Thu Jun 04 17:19:28 CEST 2020 +ASN-Module DEFINITIONS AUTOMATIC TAGS ::= BEGIN + +-- imports and exports +-- EXPORTS ALL; + + +-- ############################################################################################## +-- # +-- # UIC barcode header - first draft +-- # +-- ############################################################################################## + + +-- ############################################################################################## +-- # +-- # Naming and encoding conventions +-- # +-- # Elements included as String and as Numeric values: +-- # Some elements are included in different formats to reduce the data size. +-- # These elements must be included only once. +-- # These elements are named with the same name and appendix +-- # Num (numeric values) +-- # IA5 (String values according to ASN IA5String (7Bit)) +-- # +-- # RICS codes must be used to encode companies (issuer, product owner, ...) where available +-- # other codes are possible based on bilateral agreements +-- # the format is kept more flexible to cover upcoming extensions of the RICS code by ERA +-- # +-- # +-- # - A bar code which is only static (printed on a paper), and for which the security is in the system, doesn’t need any of these elements. +-- # - A bar code which is only static, and includes its own security, needs: +-- # level1Signature +-- # level1KeyAlg if the associated key does not include the complete certificate in keys.xml but only the public key +-- # (but level1SigningAlg is not necessary as it is in keys.xml) +-- # - A dynamic bar code including static and dynamic signatures needs: +-- # The same elements as a static bar code above, +-- # level2SigningAlg, level2keyAlg, level2PublicKey, and level2Signature. +-- # +-- ######################################################################################### + + +-- ############################################################################################ + + +-- type assignments + + -- ######################################################################################### + -- the basic entry point of the data structure + -- ########################################################################################## + UicBarcodeHeader ::= SEQUENCE { + -- barcode format type + format IA5String, + -- "U1" = UIC ticket + + + level2SignedData Level2DataType, + + -- signature is calculated on the PER unaligned encoding of level2 signature data + level2Signature OCTET STRING OPTIONAL + + + } + + Level2DataType ::= SEQUENCE { + + level1Data Level1DataType, + + -- signature is calculated on the PER unaligned encoding of level1 signature data + level1Signature OCTET STRING OPTIONAL, + + level2Data DataType OPTIONAL + + } + + + Level1DataType ::= SEQUENCE { + + -- provider of the level1 signature (RICS code) + securityProviderNum INTEGER (1..32000) OPTIONAL, + securityProviderIA5 IA5String OPTIONAL, + + keyId INTEGER(0..99999) OPTIONAL, + + dataSequence SEQUENCE OF DataType, + + + -- object identifier of the key algorithms + -- e.g. + -- ECC P-256 1.2.840.10045.3.1.7 + level1KeyAlg OBJECT IDENTIFIER OPTIONAL, + level2KeyAlg OBJECT IDENTIFIER OPTIONAL, + + -- object identifier of the signing algorithm + -- e.g. + -- DSA SHA224 2.16.840.1.101.3.4.3.1 + -- DSA SHA256 2.16.840.1.101.3.4.3.2 + -- ECDSA-256 1.2.840.10045.4.3.2 + -- algorithm used for signing + level1SigningAlg OBJECT IDENTIFIER OPTIONAL, + level2SigningAlg OBJECT IDENTIFIER OPTIONAL, + + level2PublicKey OCTET STRING OPTIONAL + + } + + DataType ::= SEQUENCE { + -- Content of data format: + -- FCBn (FCB1 = FCB version 1, FCB2 = FCB version 2) + -- FDCn dynamic content + -- or proprietary: + -- _RICS company code + addon + dataFormat IA5String, + data OCTET STRING + } + + +END \ No newline at end of file diff --git a/src/org/uic/barcode/dynamicFrame/package.html b/src/org/uic/barcode/dynamicFrame/package.html new file mode 100644 index 0000000..dbe6c06 --- /dev/null +++ b/src/org/uic/barcode/dynamicFrame/package.html @@ -0,0 +1,9 @@ + + + + + +

drafted new header for dynamic content

+

Provides a decoding and encoding of the header data frame. (Draft for UIC IRS 90918-9).

+ + \ No newline at end of file diff --git a/src/org/uic/barcode/package.html b/src/org/uic/barcode/package.html new file mode 100644 index 0000000..4a6ee0d --- /dev/null +++ b/src/org/uic/barcode/package.html @@ -0,0 +1,21 @@ + + + + + +

Provides the decoder and encoder for a UIC barcode

+ + +

Included features:

+

+

+ + + + \ No newline at end of file diff --git a/src/org/uic/barcode/staticFrame/DataRecord.java b/src/org/uic/barcode/staticFrame/DataRecord.java new file mode 100644 index 0000000..27e128f --- /dev/null +++ b/src/org/uic/barcode/staticFrame/DataRecord.java @@ -0,0 +1,199 @@ +package org.uic.barcode.staticFrame; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Arrays; + +import org.uic.ticket.EncodingFormatException; + +/** + * The Class DataRecord implements the basic decoding and encoding + * of the data record structure, the split into tag, version, length and content. + * + * Implementing classes must provide decoding and encoding of the content + * + */ +public abstract class DataRecord { + + /** The id tag. */ + protected String idTag; + + /** The version id. */ + protected String versionId; + + /** The content. */ + protected byte[] content; + + /** + * Instantiates a new data record. + * + * @param idTag the id tag + * @param version the version + */ + public DataRecord (String idTag, String version) { + this.idTag = idTag; + this.versionId = version; + } + + /** + * Instantiates a new data record. + * + * @param idTag the id tag + */ + public DataRecord (String idTag) { + this.idTag = idTag; + } + + /** + * Encode. + * + * @return the byte[] + * @throws IOException Signals that an I/O exception has occurred. + * @throws EncodingFormatException the encoding format exception + */ + public byte[] encode() throws IOException, EncodingFormatException { + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + encodeContent(); + + //size of tag + version + length + int length = 12; + + //size of data + length = length + content.length; + + String lengthElement = String.format("%04d",length); + + while (idTag.length() < 6) { + idTag = idTag + " "; + } + + while (versionId.length() < 2) { + versionId = "0" + versionId; + } + + outputStream.write(idTag.getBytes()); + + outputStream.write(versionId.getBytes()); + + outputStream.write(lengthElement.getBytes()); + + outputStream.write(content); + + return outputStream.toByteArray(); + } + + /** + * Decode. + * + * @param byteData the byte data + * @return the int + * @throws IOException Signals that an I/O exception has occurred. + * @throws EncodingFormatException the encoding format exception + */ + public int decode(byte[] byteData) throws IOException, EncodingFormatException { + + int offset = 0; + String tag = new String(Arrays.copyOfRange(byteData, offset, offset + 6)); + this.setIdTag(tag); + offset = offset + 6; + + String version = new String(Arrays.copyOfRange(byteData, offset, offset + 2)); + this.setVersionId(version); + offset = offset + 2; + + String dataSize = new String(Arrays.copyOfRange(byteData, offset, offset + 4)); + offset = offset + 4; + + int length = Integer.parseInt(dataSize) - 12; + this.setData(Arrays.copyOfRange(byteData, offset, offset + length)); + + decodeContent(); + + return length + 12; + } + + + /** + * Gets the id tag. + * + * @return the id tag + */ + public String getIdTag() { + return idTag; + } + + + /** + * Sets the id tag. + * + * @param idTag the new id tag + */ + public void setIdTag(String idTag) { + this.idTag = idTag; + } + + + /** + * Gets the version id. + * + * @return the version id + */ + public String getVersionId() { + return versionId; + } + + + /** + * Sets the version id. + * + * @param versionId the new version id + */ + public void setVersionId(String versionId) { + this.versionId = versionId; + } + + + /** + * Gets the data. + * + * @return the data + * @throws IOException Signals that an I/O exception has occurred. + * @throws EncodingFormatException the encoding format exception + */ + protected byte[] getData() throws IOException, EncodingFormatException { + return content; + } + + + /** + * Sets the data. + * + * @param data the new data + * @throws IOException Signals that an I/O exception has occurred. + * @throws EncodingFormatException the encoding format exception + */ + protected void setData(byte[] data) throws IOException, EncodingFormatException { + this.content = data; + } + + + + /** + * Decode content. + * + * @throws IOException Signals that an I/O exception has occurred. + * @throws EncodingFormatException the encoding format exception + */ + protected abstract void decodeContent() throws IOException, EncodingFormatException; + + /** + * Encode content. + * + * @throws IOException Signals that an I/O exception has occurred. + * @throws EncodingFormatException the encoding format exception + */ + protected abstract void encodeContent() throws IOException, EncodingFormatException; + +} diff --git a/src/org/uic/barcode/staticFrame/GENERICDataRecord.java b/src/org/uic/barcode/staticFrame/GENERICDataRecord.java new file mode 100644 index 0000000..2477af9 --- /dev/null +++ b/src/org/uic/barcode/staticFrame/GENERICDataRecord.java @@ -0,0 +1,62 @@ +package org.uic.barcode.staticFrame; + +import java.io.IOException; + +import org.uic.ticket.EncodingFormatException; + + +/** + * The Class GENERICDataRecord implements a generic bilateral data record included in a static bar code frame. + */ +public class GENERICDataRecord extends DataRecord { + + /** + * Instantiates a new GENERIC data record. + * + * @param idTag the id tag + */ + public GENERICDataRecord(String idTag) { + super(idTag); + } + + /** + * Decode content. + * + * @throws IOException Signals that an I/O exception has occurred. + * @throws EncodingFormatException the encoding format exception + */ + @Override + protected void decodeContent() throws IOException, EncodingFormatException { + // Do Nothing, needs to be implemented by subclasses + } + + /** + * Encode content. + * + * @throws IOException Signals that an I/O exception has occurred. + * @throws EncodingFormatException the encoding format exception + */ + @Override + protected void encodeContent() throws IOException, EncodingFormatException { + // Do Nothing, needs to be implemented by subclasses + } + + /** + * Gets the content. + * + * @return the content + */ + public byte[] getContent() { + return content; + } + + /** + * Sets the content. + * + * @param content the new content + */ + public void setContent(byte[] content) { + this.content = content; + } + +} diff --git a/src/org/uic/barcode/staticFrame/StaticFrame.java b/src/org/uic/barcode/staticFrame/StaticFrame.java new file mode 100644 index 0000000..7a0f029 --- /dev/null +++ b/src/org/uic/barcode/staticFrame/StaticFrame.java @@ -0,0 +1,762 @@ +package org.uic.barcode.staticFrame; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.Security; +import java.security.Signature; +import java.security.SignatureException; +import java.security.Provider.Service; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.zip.DataFormatException; +import java.util.zip.Deflater; +import java.util.zip.Inflater; + +import org.uic.ticket.EncodingFormatException; + + +/** + * The Class StaticHeader implements the static bar code header frame defined in UIC IRS 90918-9. + * It allows to decode and encode the bar code content and to add sub-records as defined in the IRS 90918-9 for: + * - additional header data + * - Ticket Layout content + * - Flexible content + * - bilateral data records + */ +public class StaticFrame { + + /** The additional header record. */ + private UHEADDataRecord headerRecord; + + /** The bar code version. */ + private int version; + + /** The u_flex. */ + private UFLEXDataRecord uFlex; + + /** The u_tlay. */ + private UTLAYDataRecord uTlay; + + /** The security provider. */ + private String securityProvider; + + /** The signature key. */ + private String signatureKey; + + /** The signature. */ + private byte[] signature; + + /** The data records. */ + private ArrayList 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 = toUnsignedBytes(i1); + int lb1 = b1.length; + byte[] b2 = toUnsignedBytes(i2); + int lb2 = b2.length; + + int sequenceLength = lb1 + lb2 + 4; + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + out.write((byte) sequenceTag); + out.write((byte) sequenceLength); + out.write((byte) integerTag); + out.write((byte) lb1); + out.write(b1); + out.write((byte) integerTag); + out.write((byte) lb2); + out.write(b2); + + return out.toByteArray(); + } + + /** + * Decode signature integer sequence. + * + * Support function to decode a DSA signature + * Provides the two DSA signature parameter encoded in a DSA signature + * + * @param bytes the bytes + * @return the big integer[] + * @throws Exception the exception + */ + public static BigInteger[] decodeSignatureIntegerSequence(byte[] bytes) throws Exception { + + int sequenceTag = (int) bytes[0]; + + if (sequenceTag != 48) throw new Exception("signature is not a sequence"); + + int sequenceLength = (int) bytes[1]; + + if (sequenceLength < 6) throw new Exception("signature sequence too short"); + + BigInteger[] result = new BigInteger[2]; + + int offset = 2; + int i = 0; + while (offset < bytes.length && i < 2) { + int integerTag = (int) bytes[offset]; + if (integerTag != 2) throw new Exception("signature is not an integer sequence"); + int integerLength = (int) bytes[offset + 1]; + byte[] value = Arrays.copyOfRange(bytes, offset + 2, offset + 2 + integerLength); + result[i] = new BigInteger(+1, value); + offset = offset + integerLength + 2; + i++; + } + + return result; + } + + /** + * Decode. + * + * @param inputData the input data + * @throws EncodingFormatException the encoding format exception + * @throws DataFormatException the data format exception + * @throws IOException Signals that an I/O exception has occurred. + */ + public void decode(byte[] inputData) throws EncodingFormatException, DataFormatException, IOException { + + + int offset = 0; + String headerTag = new String( Arrays.copyOfRange(inputData,offset,offset + 3)); + offset = offset + 3; + if (!headerTag.equals("#UT")) { + throw (new EncodingFormatException("not a UIC barcode")); + } + + + String versionValue = new String(Arrays.copyOfRange(inputData,offset,offset + 2)); + offset = offset + 2; + int barcodeVersion = 0; + try { + barcodeVersion = Integer.parseInt(versionValue); + this.setVersion(barcodeVersion); + } catch (NumberFormatException e2) { + throw (new EncodingFormatException(String.format("UIC Barcode Version %s not supported", versionValue))); + } + + String providerValue = new String( Arrays.copyOfRange(inputData,offset,offset + 4)); + this.setSecurityProvider(providerValue); + offset = offset + 4; + + String signatureKeyIdValue = new String( Arrays.copyOfRange(inputData,offset,offset + 5)); + this.setSignatureKey(signatureKeyIdValue); + offset = offset + 5; + + byte[] sealdata = null; + + if (barcodeVersion == 1) { + sealdata = Arrays.copyOfRange(inputData, offset, offset + 50); + signature = trimDsaSignature(sealdata); + offset = offset + 50; + } else if (barcodeVersion == 2) { + sealdata = Arrays.copyOfRange(inputData, offset, offset + 64); + signature = recombineDsaSignature(sealdata); + offset = offset + 64; + } else { + throw (new EncodingFormatException(String.format("UIC Barcode Version %s not supported", versionValue))); + } + + + String lengthValue = new String( Arrays.copyOfRange(inputData,offset,offset + 4)); + offset = offset + 4; + + int dataLength = 0; + dataLength = Integer.parseInt(lengthValue); + + signedData = Arrays.copyOfRange(inputData, offset, offset + dataLength); + + ByteBuffer containedDataBuffer = ByteBuffer.allocate(dataLength); + containedDataBuffer.put(signedData); + + byte[] inflatedDataBuffer = new byte[2000]; + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + Inflater inflater = new Inflater(); + byte[] inflaterInput = containedDataBuffer.array(); + inflater.setInput(inflaterInput); + while (!inflater.finished()) { + int count = inflater.inflate(inflatedDataBuffer,0,2000); + if (inflater.needsDictionary()) { + break; + } + outputStream.write(inflatedDataBuffer, 0, count); + } + + outputStream.close(); + + byte[] byteData = outputStream.toByteArray(); + + offset = 0; + int remainingBytes = byteData.length; + + while (remainingBytes > 0) { + + String tag = new String(Arrays.copyOfRange(byteData, offset, offset + 6)); + int length = 0; + + if (tag.startsWith("U_TLAY")) { + UTLAYDataRecord record = new UTLAYDataRecord(); + length = record.decode(Arrays.copyOfRange(byteData, offset, byteData.length)); + this.uTlay = record; + } else if (tag.startsWith("U_FLEX")) { + UFLEXDataRecord record = new UFLEXDataRecord(); + length = record.decode(Arrays.copyOfRange(byteData, offset, byteData.length)); + this.uFlex = record; + } else if (tag.startsWith("U_HEAD")) { + UHEADDataRecord record = new UHEADDataRecord(); + length = record.decode(Arrays.copyOfRange(byteData, offset, byteData.length)); + this.headerRecord = record; + } else { + DataRecord record = new GENERICDataRecord(tag); + length = record.decode(Arrays.copyOfRange(byteData, offset, byteData.length)); + addDataRecord(record); + } + offset = offset + length; + remainingBytes = remainingBytes - length; + } + } + + + private byte[] recombineDsaSignature(byte[] sealdata) throws IOException { + + //check whether the encoding is wrong and the sealdata contain a signature + //remove trailing zeroes from the signature + BigInteger[] bInts = null; + try { + bInts = decodeSignatureIntegerSequence(sealdata); + byte[] sig = encodeSignatureIntegerSequence(bInts[0],bInts[1]); + //decoding the entire signature was ok, so there was no split + return sig; + } catch (Exception e) { + //the signature is correctly implemented, continue with recombination + } + + // split the data into two blocks + int length = sealdata.length / 2; + byte[] rBytes = Arrays.copyOfRange(sealdata, 0, length); + byte[] sBytes = Arrays.copyOfRange(sealdata, length, length + length); + + //convert to BigInteger to get rid of leading zeroes + BigInteger r = new BigInteger(1,rBytes); + BigInteger s = new BigInteger(1,sBytes); + + //encode as DSA signature structure + //SEQUENCE OF --> tag 16 + int sequenceTag = 16 + 32; // (bits 6 = 1 constructed) + //INTEGER --> tag 2 + int integerTag = 2; + byte[] b1 = toUnsignedBytes(r); + int lb1 = b1.length; + byte[] b2 = toUnsignedBytes(s); + int lb2 = b2.length; + int sequenceLength = lb1 + lb2 + 4; + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write((byte) sequenceTag); + out.write((byte) sequenceLength); + out.write((byte) integerTag); + out.write((byte) lb1); + out.write(b1); + out.write((byte) integerTag); + out.write((byte) lb2); + out.write(b2); + return out.toByteArray(); + + + } + + private static byte[] toUnsignedBytes(BigInteger i) { + byte[] b = i.abs().toByteArray(); + //remove top sign bit + if (b[0] == 0) { + b = Arrays.copyOfRange(b, 1, b.length); + } + return b; + } + + + private byte[] trimDsaSignature(byte[] sealdata) throws EncodingFormatException { + //remove trailing zeroes from the signature + BigInteger[] bInts = null; + try { + bInts = decodeSignatureIntegerSequence(sealdata); + return encodeSignatureIntegerSequence(bInts[0],bInts[1]); + } catch (Exception e) { + throw (new EncodingFormatException(String.format("Invalid DSA signature"))); + } + } + + + + /** + * Verify the signature + * + * Note: an appropriate security provider (e.g. BC) must be registered before + * + * @param key the key + * @param algo the algorithm name + * @return true, if successful + * @throws InvalidKeyException the invalid key exception + * @throws NoSuchAlgorithmException the no such algorithm exception + * @throws SignatureException the signature exception + * @throws IllegalArgumentException the illegal argument exception + * @throws UnsupportedOperationException the unsupported operation exception + * @throws EncodingFormatException + * @throws IOException + */ + public boolean ByAlgorithmName(PublicKey key, String algo) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IllegalArgumentException, UnsupportedOperationException, IOException, EncodingFormatException { + Signature sig = Signature.getInstance(algo); + sig.initVerify(key); + sig.update(this.getDataForSignature()); + return sig.verify(this.getSignature()); + } + + /** + * Verify the signature + * + * Note: an appropriate security provider (e.g. BC) must be registered before + * + * @param key the key + * @param singningAlg the Object ID of the signing algorithm + * @return true, if successful + * @throws InvalidKeyException the invalid key exception + * @throws NoSuchAlgorithmException the no such algorithm exception + * @throws SignatureException the signature exception + * @throws IllegalArgumentException the illegal argument exception + * @throws UnsupportedOperationException the unsupported operatign exception + * @throws EncodingFormatException + * @throws IOException + */ + public boolean verifyByAlgorithmOid(PublicKey key, String signingAlg) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IllegalArgumentException, UnsupportedOperationException, IOException, EncodingFormatException { + //find the algorithm name for the signature OID + String algo = null; + Provider[] provs = Security.getProviders(); + for (Provider prov : provs) { + Service service = prov.getService("Signature",signingAlg); + if (service != null) { + algo = service.getAlgorithm(); + } + } + Signature sig = Signature.getInstance(algo); + sig.initVerify(key); + sig.update(getDataForSignature()); + return sig.verify(this.getSignature()); + } + + /** + * Sign the contained data block. + * + * Note: an appropriate security provider (e.g. BC) must be registered before + * + * @param key the key + * @param singningAlg the Object ID of the signing algorithm + * @return + * @throws NoSuchAlgorithmException the no such algorithm exception + * @throws InvalidKeyException the invalid key exception + * @throws SignatureException the signature exception + * @throws EncodingFormatException + * @throws IOException + */ + public void signByAlgorithmOID(PrivateKey key,String signingAlg) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, IOException, EncodingFormatException { + //find the algorithm name for the signature OID + String algo = null; + Provider[] provs = Security.getProviders(); + for (Provider prov : provs) { + Service service = prov.getService("Signature",signingAlg); + if (service != null) { + algo = service.getAlgorithm(); + } + } + Signature sig = Signature.getInstance(algo); + sig.initSign(key); + signedData = getDataForSignature(); + sig.update(signedData); + signature = sig.sign(); + } + + /** + * Sign the contained data block. + * + * Note: an appropriate security provider (e.g. BC) must be registered before + * + * @param key the key + * @param algo the name of the signing algorithm + * @return + * @throws NoSuchAlgorithmException the no such algorithm exception + * @throws InvalidKeyException the invalid key exception + * @throws SignatureException the signature exception + * @throws EncodingFormatException + * @throws IOException + */ + public void signUsingAlgorithmName(PrivateKey key,String algo) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, IOException, EncodingFormatException { + Signature sig = Signature.getInstance(algo); + sig.initSign(key); + sig.update(getDataForSignature()); + signature = sig.sign(); + } + + + + public UFLEXDataRecord getuFlex() { + return uFlex; + } + + + + public UTLAYDataRecord getuTlay() { + return uTlay; + } + + + + public void setuFlex(UFLEXDataRecord uFlex) { + this.uFlex = uFlex; + } + + + + public void setuTlay(UTLAYDataRecord uTlay) { + this.uTlay = uTlay; + } + + + + public void setHeaderRecord(UHEADDataRecord headerRecord) { + this.headerRecord = headerRecord; + } + + + + +} diff --git a/src/org/uic/barcode/staticFrame/UFLEXDataRecord.java b/src/org/uic/barcode/staticFrame/UFLEXDataRecord.java new file mode 100644 index 0000000..9731198 --- /dev/null +++ b/src/org/uic/barcode/staticFrame/UFLEXDataRecord.java @@ -0,0 +1,90 @@ +package org.uic.barcode.staticFrame; + +import java.io.IOException; + +import org.uic.ticket.EncodingFormatException; +import org.uic.ticket.UicRailTicketCoder; +import org.uic.ticket.api.spec.IUicRailTicket; + +/** + * The Class UFLEXDataRecord implements the dara record to hold the data of an ASN.1 PER encoded UIC ticket. + */ +public class UFLEXDataRecord extends DataRecord { + + + /** The ticket. */ + private IUicRailTicket ticket; + + /** + * Instantiates a new UFLEX data record. + */ + public UFLEXDataRecord() { + super("U_FLEX"); + } + + /** + * Instantiates a new UFLEX data record. + * + * @param version the version + */ + public UFLEXDataRecord(String version) { + super("U_FLEX", version); + } + + + /** + * Gets the ticket. + * + * @return the ticket + * @throws IOException Signals that an I/O exception has occurred. + * @throws EncodingFormatException the encoding format exception + */ + public IUicRailTicket getTicket() throws IOException, EncodingFormatException { + if (ticket != null) { + return ticket; + } + return null; + } + + /** + * Sets the ticket. + * + * @param ticket the new ticket + * @throws IOException Signals that an I/O exception has occurred. + * @throws EncodingFormatException the encoding format exception + */ + public void setTicket(IUicRailTicket ticket) throws IOException, EncodingFormatException { + this.ticket = ticket; + super.setData(null); + } + + + /** + * Decode content. + * + * @throws IOException Signals that an I/O exception has occurred. + * @throws EncodingFormatException the encoding format exception + */ + @Override + protected void decodeContent() throws IOException, EncodingFormatException { + UicRailTicketCoder coder = new UicRailTicketCoder(); + int version = Integer.parseInt(super.getVersionId()); + this.ticket = coder.decodeFromAsn(content,version); + } + + /** + * Encode content. + * + * @throws IOException Signals that an I/O exception has occurred. + * @throws EncodingFormatException the encoding format exception + */ + @Override + protected void encodeContent() throws IOException, EncodingFormatException { + UicRailTicketCoder coder = new UicRailTicketCoder(); + int version = Integer.parseInt(super.getVersionId()); + content = coder.encode(ticket, version); + } + + + +} diff --git a/src/org/uic/barcode/staticFrame/UHEADDataRecord.java b/src/org/uic/barcode/staticFrame/UHEADDataRecord.java new file mode 100644 index 0000000..e2ec301 --- /dev/null +++ b/src/org/uic/barcode/staticFrame/UHEADDataRecord.java @@ -0,0 +1,269 @@ +package org.uic.barcode.staticFrame; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; + +import org.uic.ticket.EncodingFormatException; + +/** + * The Class UHEADDataRecord implements the additional header record of a statis UIC bar code + */ +public class UHEADDataRecord extends DataRecord{ + + + /** The issuing date. */ + private Date issuingDate = null; + + /** The flags. */ + private int flags; + + /** The issuer. */ + private String issuer; + + /** The identifier. */ + private String identifier; + + /** The language. */ + private String language; + + /** The additional language. */ + private String additionalLanguage; + + /** + * Instantiates a new UHEAD data record. + */ + public UHEADDataRecord() { + super("U_HEAD"); + } + + + /** + * Gets the issuing date. + * + * @return the issuing date + */ + public Date getIssuingDate() { + return issuingDate; + } + + /** + * Sets the issuing date. + * + * @param issuingDate the new issuing date + */ + public void setIssuingDate(Date issuingDate) { + this.issuingDate = issuingDate; + } + + /** + * Gets the flags. + * + * @return the flags + */ + public int getFlags() { + return flags; + } + + /** + * Sets the flags. + * + * @param flags the new flags + */ + public void setFlags(int flags) { + this.flags = flags; + } + + /** + * Gets the issuer. + * + * @return the issuer + */ + public String getIssuer() { + return issuer; + } + + /** + * Sets the issuer. + * + * @param issuer the new issuer + */ + public void setIssuer(String issuer) { + this.issuer = issuer; + } + + /** + * Gets the identifier. + * + * @return the identifier + */ + public String getIdentifier() { + return identifier; + } + + /** + * Sets the identifier. + * + * @param identifier the new identifier + */ + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + /** + * Gets the language. + * + * @return the language + */ + public String getLanguage() { + return language; + } + + /** + * Sets the language. + * + * @param language the new language + */ + public void setLanguage(String language) { + this.language = language; + } + + /** + * Gets the additional language. + * + * @return the additional language + */ + public String getAdditionalLanguage() { + return additionalLanguage; + } + + /** + * Sets the additional language. + * + * @param additionalLanguage the new additional language + */ + public void setAdditionalLanguage(String additionalLanguage) { + this.additionalLanguage = additionalLanguage; + } + + + /** + * Decode content. + * + * @throws IOException Signals that an I/O exception has occurred. + * @throws EncodingFormatException the encoding format exception + */ + protected void decodeContent() throws IOException, EncodingFormatException{ + + if (content == null || content.length == 0 ) return; + + issuer = decodeString(content, 0 , 4); + + identifier = decodeString(content, 4 , 20); + + String issuingDateString = decodeString(content, 24 , 12); + + String flagsString = decodeString(content,36 , 1); + + language = decodeString(content, 37 , 2); + + additionalLanguage = decodeString(content,39 , 2); + + + try { + flags = Integer.parseInt(flagsString); + } catch (Exception e) { + flags = 9; + } + + // date format "DDMMYYYYHHMM" + SimpleDateFormat formatter = new SimpleDateFormat("ddMMyyyyhhmm"); + try { + issuingDate = formatter.parse(issuingDateString); + } catch (ParseException e) { + e.printStackTrace(); + } + + } + + + /** + * Decode string. + * + * @param byteData the byte data + * @param offset the offset + * @param length the length + * @return the string + */ + private String decodeString(byte[] byteData, int offset, int length) { + + char[] chars = new char[length]; + + for (int i = 0; i < length && i < byteData.length;i++) { + byte byteValue = byteData[offset + i]; + if (byteValue == '\n') { + byteValue = ' '; + } + chars[i] = (char) byteValue; + } + + return String.copyValueOf(chars); + } + + /** + * Encode content. + * + * @throws IOException Signals that an I/O exception has occurred. + * @throws EncodingFormatException the encoding format exception + */ + protected void encodeContent() throws IOException, EncodingFormatException { + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + String issuerElement = String.format("%4s", this.issuer); + + String idElement = String.format("%20s", this.identifier); + + //DDMMYYYYHHMM" + Calendar now = Calendar.getInstance(); + + // issuing date can be in the ticket or in the header + if (this.issuingDate != null) { + now.setTime(this.issuingDate); + } + + String timeElement = String.format("%02d%02d%04d%02d%02d", + now.get(Calendar.DAY_OF_MONTH), + now.get(Calendar.MONTH), + now.get(Calendar.YEAR), + now.get(Calendar.HOUR), + now.get(Calendar.MINUTE)); + + + String flagsElement = String.format("%01d",this.flags); + + + String languageElement = String.format("%2s%2s" ,this.language, this.additionalLanguage); + + try { + + outputStream.write(issuerElement.getBytes()); + outputStream.write(idElement.getBytes()); + outputStream.write(timeElement.getBytes()); + + outputStream.write(flagsElement.getBytes()); + outputStream.write(languageElement.getBytes()); + + } catch (IOException e) { + e.printStackTrace(); + } + + super.setData(outputStream.toByteArray()); + + + } + +} diff --git a/src/org/uic/barcode/staticFrame/UTLAYDataRecord.java b/src/org/uic/barcode/staticFrame/UTLAYDataRecord.java new file mode 100644 index 0000000..2e9a2dc --- /dev/null +++ b/src/org/uic/barcode/staticFrame/UTLAYDataRecord.java @@ -0,0 +1,272 @@ +/* + * + */ +package org.uic.barcode.staticFrame; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +import org.uic.barcode.staticHeader.ticketLayoutBarcode.FormatType; +import org.uic.barcode.staticHeader.ticketLayoutBarcode.LayoutElement; +import org.uic.barcode.staticHeader.ticketLayoutBarcode.TicketLayout; +import org.uic.ticket.EncodingFormatException; + +/** + * The Class UTLAYDataRecord implements a bar code data record containing the ticket layout. + */ +public class UTLAYDataRecord extends DataRecord { + + /** The ticket layout. */ + private TicketLayout layout; + + /** + * Instantiates a new empty UTLAY data record. + */ + public UTLAYDataRecord() { + super("U_TLAY","01"); + } + + /** + * Decode utf-8 string. + * + * @param byteData the byte data + * @param offset the offset + * @param length the length + * @return the string + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + private static String decodeUtf8String(byte[] byteData, int offset, int length) throws UnsupportedEncodingException { + byte[] bytes = new byte[length]; + for (int i = 0; i < length; i++){ + bytes[i] = byteData[i + offset]; + } + return StandardCharsets.UTF_8.decode(ByteBuffer.wrap(bytes)).toString(); + } + + /** + * Decode string. + * + * @param byteData the byte data + * @param offset the offset + * @param length the length + * @return the string + */ + private static String decodeString(byte[] byteData, int offset, int length) { + byte[] bytes = new byte[length]; + for (int i = 0; i < length; i++){ + bytes[i] = byteData[i + offset]; + } + return StandardCharsets.ISO_8859_1.decode(ByteBuffer.wrap(bytes)).toString(); + } + + /** + * Encode utf-8. + * + * @param value the value + * @return the byte[] + */ + private static byte[] encodeUtf8(String value) { + + try { + return value.getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("UTF8 String encoding wrong!",e); + } + } + + /** + * To string. + * + * @return the string + */ + public String toString() { + + StringBuilder sb = new StringBuilder(); + + sb.append("TLB: ").append(layout.getLayoutStandard()).append('\n'); + + for (LayoutElement e : layout.getElements()){ + sb.append("column: ").append(e.getColumn()).append(" - "); + sb.append("line: ").append(e.getLine()).append(" - "); + sb.append("width: ").append(e.getWidth()).append(" - "); + sb.append("heigth: ").append(e.getHeight()).append(" - "); + sb.append("text: ").append(e.getText()).append(" - "); + sb.append("format: ").append(e.getFormat().toString()).append('\n'); + } + + return sb.toString(); + + } + + /** + * Decode content. + * + * @throws IOException Signals that an I/O exception has occurred. + * @throws EncodingFormatException the encoding format exception + */ + @Override + protected void decodeContent() throws IOException, EncodingFormatException { + + layout = new TicketLayout(); + + if (content == null || content.length == 0 ) return; + + int offset = 0; + + String layoutType = decodeString(content, offset , 4); + layout.setLayoutStandard(layoutType); + offset = offset + 4; + + String numberValue = decodeString(content, offset , 4); + offset = offset + 4; + + int elements = 0; + try { + elements = Integer.parseInt(numberValue); + } catch(NumberFormatException e){ + //Do Nothing + } + + int remainingBytes = content.length - offset; + + for (int i = 0; i < elements && remainingBytes > 0 ;i++){ + + String lineValue = decodeString(content, offset , 2); + offset = offset + 2; + int line = 0; + try { + line = Integer.parseInt(lineValue); + } catch(NumberFormatException e){ + //Do Nothing + } + String columnValue = decodeString(content, offset , 2); + offset = offset + 2; + int column = 0; + try { + column = Integer.parseInt(columnValue); + } catch(NumberFormatException e){ + //Do Nothing + } + String heightValue = decodeString(content, offset , 2); + offset = offset + 2; + int height = 0; + try { + height = Integer.parseInt(heightValue); + } catch(NumberFormatException e){ + //Do Nothing + } + String widthValue = decodeString(content, offset , 2); + offset = offset + 2; + int width = 0; + try { + width = Integer.parseInt(widthValue); + } catch(NumberFormatException e){ + //Do Nothing + } + String formatValue = decodeString(content, offset , 1); + offset = offset + 1; + int format = 0; + try { + format = Integer.parseInt(formatValue); + } catch(NumberFormatException e){ + //Do Nothing + } + String lengthValue = decodeString(content, offset , 4); + offset = offset + 4; + int length = 0; + try { + length = Integer.parseInt(lengthValue); + } catch(NumberFormatException e){ + //Do Nothing + } + + String text; + try { + text = decodeUtf8String(content, offset ,length); + } catch (UnsupportedEncodingException e) { + text = "unsupported character set"; + } + offset = offset + length; + + LayoutElement layoutElement = new LayoutElement(); + + layoutElement.setColumn(column); + layoutElement.setLine(line); + layoutElement.setHeight(height); + layoutElement.setWidth(width); + layoutElement.setText(text); + + layoutElement.setFormat(FormatType.values()[format]); + + layout.addLayoutElement(layoutElement); + + } + + } + + /** + * Encode content. + * + * @throws IOException Signals that an I/O exception has occurred. + * @throws EncodingFormatException the encoding format exception + */ + @Override + protected void encodeContent() throws IOException, EncodingFormatException { + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + if (layout == null || layout.getElements() == null || layout.getElements().isEmpty()) { + return; + } + + //number of text elements + String numberOfFields = String.format("%04d",layout.getElements().size()); + + outputStream.write(layout.getLayoutStandard().getBytes()); + outputStream.write(numberOfFields.getBytes()); + + for (LayoutElement e : layout.getElements()){ + + String line = String.format("%02d",e.getLine()); + String column = String.format("%02d",e.getColumn()); + String heigth = String.format("%02d",e.getHeight()); + String width = String.format("%02d",e.getWidth()); + String format = String.format("%01d",e.getFormat().ordinal()); + String size = String.format("%04d",encodeUtf8(e.getText()).length); + + outputStream.write(line.getBytes()); + outputStream.write(column.getBytes()); + outputStream.write(heigth.getBytes()); + outputStream.write(width.getBytes()); + outputStream.write(format.getBytes()); + outputStream.write(size.getBytes()); + outputStream.write(encodeUtf8(e.getText())); + + } + + content = outputStream.toByteArray(); + } + + /** + * Sets the layout. + * + * @param layout the new layout + */ + public void setLayout(TicketLayout layout) { + this.layout = layout; + } + + + /** + * Gets the layout. + * + * @return the layout + */ + public TicketLayout getLayout() { + return layout; + } + +} diff --git a/src/org/uic/barcode/staticFrame/package.html b/src/org/uic/barcode/staticFrame/package.html new file mode 100644 index 0000000..b76540b --- /dev/null +++ b/src/org/uic/barcode/staticFrame/package.html @@ -0,0 +1,21 @@ + + + + + +

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/org/uic/barcode/staticHeader/ticketLayoutBarcode/FormatType.java b/src/org/uic/barcode/staticHeader/ticketLayoutBarcode/FormatType.java new file mode 100644 index 0000000..c5cc39e --- /dev/null +++ b/src/org/uic/barcode/staticHeader/ticketLayoutBarcode/FormatType.java @@ -0,0 +1,34 @@ +package org.uic.barcode.staticHeader.ticketLayoutBarcode; + + +public enum FormatType { + NORMAL("NORMAL"), + BOLD("BOLD"), + ITALIC("ITALIC"), + BOLDITALIC("BOLDITALIC"), + SMALL("SMALL"), + SMALLBOLD("SMALLBOLD"), + SMALLITALIC("SMALLITALIC"), + SMALLBOLDITALIC("SMALLBOLDITALIC"); + + + + public String text; + + FormatType(String text) { + this.text = text; + } + + public static FormatType getFormatType(int i) { + try { + return FormatType.values()[i]; + } catch (Exception e) { + return null; + } + } + + public String toString(){ + return text; + } + +} diff --git a/src/org/uic/barcode/staticHeader/ticketLayoutBarcode/LayoutElement.java b/src/org/uic/barcode/staticHeader/ticketLayoutBarcode/LayoutElement.java new file mode 100644 index 0000000..04593f2 --- /dev/null +++ b/src/org/uic/barcode/staticHeader/ticketLayoutBarcode/LayoutElement.java @@ -0,0 +1,50 @@ +package org.uic.barcode.staticHeader.ticketLayoutBarcode; + +public class LayoutElement { + + private int column; + private int line; + private int height; + private int width; + private String text; + private FormatType format = FormatType.NORMAL; + + public int getColumn() { + return column; + } + public void setColumn(int column) { + this.column = column; + } + public int getLine() { + return line; + } + public void setLine(int line) { + this.line = line; + } + public int getHeight() { + return height; + } + public void setHeight(int height) { + this.height = height; + } + public int getWidth() { + return width; + } + public void setWidth(int width) { + this.width = width; + } + public String getText() { + return text; + } + public void setText(String text) { + this.text = text; + } + public FormatType getFormat() { + return format; + } + public void setFormat(FormatType format) { + this.format = format; + } + + +} diff --git a/src/org/uic/barcode/staticHeader/ticketLayoutBarcode/TicketLayout.java b/src/org/uic/barcode/staticHeader/ticketLayoutBarcode/TicketLayout.java new file mode 100644 index 0000000..e788f9a --- /dev/null +++ b/src/org/uic/barcode/staticHeader/ticketLayoutBarcode/TicketLayout.java @@ -0,0 +1,61 @@ +package org.uic.barcode.staticHeader.ticketLayoutBarcode; + +import java.util.ArrayList; +import java.util.List; + +public class TicketLayout { + + private String layoutStandard = "RCT2"; + + /** The layout elements. */ + private List 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; + } + + +} diff --git a/src/org/uic/barcode/test/DynamicFrameDoubleSignatureTest.java b/src/org/uic/barcode/test/DynamicFrameDoubleSignatureTest.java new file mode 100644 index 0000000..f74c40f --- /dev/null +++ b/src/org/uic/barcode/test/DynamicFrameDoubleSignatureTest.java @@ -0,0 +1,212 @@ +package org.uic.barcode.test; + +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.security.Security; +import java.security.SignatureException; +import java.util.Arrays; +import java.util.zip.DataFormatException; + +import org.bouncycastle.jce.ECNamedCurveTable; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECParameterSpec; +import org.junit.Before; +import org.junit.Test; +import org.uic.barcode.Decoder; +import org.uic.barcode.Encoder; +import org.uic.barcode.dynamicFrame.Constants; +import org.uic.barcode.dynamicFrame.DataType; +import org.uic.barcode.test.utils.Level2TestDataFactory; +import org.uic.barcode.test.utils.SimpleUICTestTicket; +import org.uic.barcode.utils.AlgorithmNameResolver; +import org.uic.ticket.EncodingFormatException; +import org.uic.ticket.api.spec.IUicRailTicket; + +public class DynamicFrameDoubleSignatureTest { + + public String signatureAlgorithmOID = null; + public String elipticCurve = null; + public String keyPairAlgorithmOID = null; + + public KeyPair keyPairLevel1 = null; + public KeyPair keyPairLevel2 = null; + + public IUicRailTicket testFCBticket = null; + + + @Before public void initialize() { + + signatureAlgorithmOID = Constants.ECDSA_SHA256; + keyPairAlgorithmOID = Constants.KG_EC_256; + elipticCurve = "secp256k1"; + + testFCBticket = SimpleUICTestTicket.getUicTestTicket(); + + Security.addProvider(new BouncyCastleProvider()); + + try { + keyPairLevel1 = generateECKeys(keyPairAlgorithmOID, elipticCurve); + keyPairLevel2 = generateECKeys(keyPairAlgorithmOID, elipticCurve); + } catch (Exception e) { + assert(false); + } + + assert(keyPairLevel1 != null); + + } + + + @Test public void testDynamicHeaderBarcodeEncoding() { + + IUicRailTicket ticket = testFCBticket; + + Encoder enc = null; + + try { + enc = new Encoder(ticket, null, Encoder.UIC_BARCODE_TYPE_DOSIPAS, 1, 1); + } catch (IOException | EncodingFormatException e1) { + assert(false); + } + + assert(enc != null); + + try { + enc.setLevel1Algs(signatureAlgorithmOID, keyPairAlgorithmOID); + enc.setLevel2Algs(signatureAlgorithmOID, keyPairAlgorithmOID,keyPairLevel2.getPublic()); + enc.signLevel1("1080", keyPairLevel1.getPrivate(), signatureAlgorithmOID, "1"); + } catch (Exception e) { + assert(false); + } + + assert(enc != null); + + try { + enc.setLevel2Data(Level2TestDataFactory.getLevel2SimpleTestData()); + enc.signLevel2(keyPairLevel2.getPrivate()); + } catch (Exception e) { + assert(false); + } + + + byte[] encoded = null; + try { + encoded = enc.encode(); + } catch (Exception e) { + assert(false); + } + + assert(encoded != null); + + + + } + + @Test public void testDynamicHeaderBarcodeDecoding() { + + IUicRailTicket ticket = testFCBticket; + + Encoder enc = null; + + try { + enc = new Encoder(ticket, null, Encoder.UIC_BARCODE_TYPE_DOSIPAS, 1, 1); + } catch (IOException | EncodingFormatException e1) { + assert(false); + } + + assert(enc != null); + + try { + enc.setLevel1Algs(signatureAlgorithmOID, keyPairAlgorithmOID); + enc.setLevel2Algs(signatureAlgorithmOID, keyPairAlgorithmOID,keyPairLevel2.getPublic()); + enc.signLevel1("1080", keyPairLevel1.getPrivate(), signatureAlgorithmOID, "1"); + } catch (Exception e) { + assert(false); + } + + assert(enc != null); + + + DataType level2Data = Level2TestDataFactory.getLevel2SimpleTestData(); + try { + enc.setLevel2Data(level2Data); + enc.signLevel2(keyPairLevel2.getPrivate()); + } catch (Exception e) { + assert(false); + } + + + byte[] encoded = null; + try { + encoded = enc.encode(); + } catch (Exception e) { + assert(false); + } + + assert(encoded != null); + + Decoder dec = null; + try { + dec = new Decoder(encoded); + } catch (IOException e) { + assert(false); + } catch (EncodingFormatException e) { + assert(false); + } catch (DataFormatException e) { + assert(false); + } + assert(dec != null); + + int signatureCheck = 0; + try { + signatureCheck = dec.validateLevel1(keyPairLevel1.getPublic(),null); + } catch (InvalidKeyException | NoSuchAlgorithmException | SignatureException | IllegalArgumentException + | UnsupportedOperationException | IOException | EncodingFormatException e) { + assert(false); + } + assert(signatureCheck == Constants.LEVEL1_VALIDATION_OK); + + signatureCheck = 0; + try { + signatureCheck = dec.validateLevel2(); + } catch (IllegalArgumentException | UnsupportedOperationException e) { + assert(false); + } + assert(signatureCheck == Constants.LEVEL2_VALIDATION_OK); + + + DataType level2DataDec = dec.getLevel2Data(); + + assert(level2Data.getFormat().equals(level2DataDec.getFormat())); + assert(Arrays.equals(level2Data.getData().toByteArray(),level2DataDec.getData().toByteArray())); + + SimpleUICTestTicket.compare(ticket, dec.getUicTicket()); + + + } + + public KeyPair generateECDSAKeys(String keyAlgorithmName, String paramName) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException{ + ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec(paramName); + KeyPairGenerator g = KeyPairGenerator.getInstance(keyAlgorithmName, "BC"); + g.initialize(ecSpec, new SecureRandom()); + return g.generateKeyPair(); + } + + public KeyPair generateECKeys(String keyAlgorithmOid, String curve) throws Exception{ + + String keyAlgorithmName = AlgorithmNameResolver.getName(AlgorithmNameResolver.TYPE_KEY_GENERATOR_ALG, keyAlgorithmOid, "BC"); + + keyAlgorithmName = "ECDSA"; + ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec(curve); + KeyPairGenerator g = KeyPairGenerator.getInstance(keyAlgorithmName, "BC"); + g.initialize(ecSpec, new SecureRandom()); + return g.generateKeyPair(); + } + + +} diff --git a/src/org/uic/barcode/test/DynamicFrameLOwLevelTest.java b/src/org/uic/barcode/test/DynamicFrameLOwLevelTest.java new file mode 100644 index 0000000..d3148b2 --- /dev/null +++ b/src/org/uic/barcode/test/DynamicFrameLOwLevelTest.java @@ -0,0 +1,116 @@ +package org.uic.barcode.test; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Security; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import org.bouncycastle.jce.ECNamedCurveTable; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECParameterSpec; +import org.junit.Before; +import org.junit.Test; +import org.uic.barcode.dynamicFrame.Constants; +import org.uic.barcode.dynamicFrame.DynamicFrame; +import org.uic.barcode.test.utils.SimpleDynamicFrameTestBarcode; + +import net.gcdc.asn1.uper.UperEncoder; + +public class DynamicFrameLOwLevelTest { + + public String algorithmOID = Constants.ECDSA_SHA256; + public KeyPair keyPair = null; + public String publicKeyHex = "3081A7301006072A8648CE3D020106052B81040027038192000405B2797BB27F96EC3769B81E563EEB97A4CE3B5BB4EE19BC90A3F496B805644AA042736E5FA9B3A5FBEA5B01CD1D9EC13C009F9655B31FFF9AA52AC6D90B5D6220B58853A1D18BF20779BE5C52356AE70B19242065F82B76961E2A079F42CA9A41A1AB4D5518446AC3721953AE6323C60E15389498DE5F592A24DDDA45F736D93695C797C0F28A712EC25B9CD8078457"; + public String privateKeyHex = "30820109020100301006072A8648CE3D020106052B810400270481F13081EE020101044801EF44914319A5DD528C06419D7B0FD0CDD7F62A231BEB197E45A0074C02E11FE82ABAD916BE94FD8256260AA9191F19241CFC7E372B3A4E0ADA06CCA51678C54198667DFC9B0DA8A00706052B81040027A18195038192000405B2797BB27F96EC3769B81E563EEB97A4CE3B5BB4EE19BC90A3F496B805644AA042736E5FA9B3A5FBEA5B01CD1D9EC13C009F9655B31FFF9AA52AC6D90B5D6220B58853A1D18BF20779BE5C52356AE70B19242065F82B76961E2A079F42CA9A41A1AB4D5518446AC3721953AE6323C60E15389498DE5F592A24DDDA45F736D93695C797C0F28A712EC25B9CD8078457"; + + public String hexReferenceContent = "400EAC986010DF80A021DE008808014374F3E7D72F2A9979F4A13A90086280B40020894DED0DC0688DEEE0AC593368D60220DCF2EED3BF903B6BCA3B937BAB801280EB44AC0505B4200000000100E6F70656E5469636B6574496E666F120220103B830B9B9B0B3B28084A0B6B9BA32B93230B680202F698040100B20004C6C8020404E9979F40201620505B402C80A0F68020AA192338F4100C08008097308194024800DA0C61105BAD7E13ADF9D5A00CBC47732865EA67E8371A5FBE38B4FABBBABD37459D12048DA6664700E787C32962A607A784FD2FC669A9A8EC9F91CD53AF2B922EFECE24FF3D68024800A1F7CF1C0625FB19CF089E74D668F5E8C15179BEF7BA79D9D169A12FA47F6340ED50BADB57CD83110060FEC08B1EF978C6DB08A172B0DE20C442D4507442623A74A624457590040"; + + + @Before public void initialize() { + + Security.addProvider(new BouncyCastleProvider()); + + try { + keyPair = generateECDSAKeys(); + } catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) { + e.printStackTrace(); + } + privateKeyHex = UperEncoder.hexStringFromBytes(keyPair.getPrivate().getEncoded()); + publicKeyHex = UperEncoder.hexStringFromBytes(keyPair.getPublic().getEncoded()); + + assert(keyPair != null); + + try { + PublicKey publicKey = KeyFactory.getInstance("ECDSA").generatePublic(new X509EncodedKeySpec(UperEncoder.bytesFromHexString(publicKeyHex))); + PrivateKey privateKey = KeyFactory.getInstance("ECDSA").generatePrivate(new PKCS8EncodedKeySpec(UperEncoder.bytesFromHexString(privateKeyHex))); + keyPair = new KeyPair(publicKey,privateKey); + } catch (InvalidKeySpecException | NoSuchAlgorithmException e) { + e.printStackTrace(); + } + assert(keyPair != null); + + } + + + @Test public void testDynamicHeaderBarcodeEncoding() { + + DynamicFrame barcode1 = SimpleDynamicFrameTestBarcode.getSimpleDynamicHeaderBarcode(algorithmOID, keyPair); + + byte[] encoded = barcode1.encode(); + + //String hex = UperEncoder.hexStringFromBytes(encoded); + + /* + * check the available implementations + String s = null; + try { + s = AlgorithmNameResolver.getSecurityNames(); + } catch (Exception e) { + e.printStackTrace(); + } + */ + + + //note: hex is different each time due to the random seed in the signature + assert(encoded != null); + + } + + @Test public void testDynamicHeaderBarcodeDecoding() { + + DynamicFrame barcode1 = SimpleDynamicFrameTestBarcode.getSimpleDynamicHeaderBarcode(algorithmOID, keyPair); + + byte[] encoded = barcode1.encode(); + + DynamicFrame barcode = DynamicFrame.decode(encoded); + + int signatureCheck = barcode.validateLevel1(keyPair.getPublic()); + + assert(signatureCheck == Constants.LEVEL1_VALIDATION_OK); + + SimpleDynamicFrameTestBarcode.compareFrame(barcode1, barcode); + + + + + } + + public KeyPair generateECDSAKeys() throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException{ + ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("B-571"); + KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", "BC"); + g.initialize(ecSpec, new SecureRandom()); + return g.generateKeyPair(); + } + + + +} diff --git a/src/org/uic/barcode/test/DynamicFrameSimpleTest.java b/src/org/uic/barcode/test/DynamicFrameSimpleTest.java new file mode 100644 index 0000000..72b137e --- /dev/null +++ b/src/org/uic/barcode/test/DynamicFrameSimpleTest.java @@ -0,0 +1,171 @@ +package org.uic.barcode.test; + +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.security.Security; +import java.security.SignatureException; +import java.util.zip.DataFormatException; + +import org.bouncycastle.jce.ECNamedCurveTable; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECParameterSpec; +import org.junit.Before; +import org.junit.Test; +import org.uic.barcode.Decoder; +import org.uic.barcode.Encoder; +import org.uic.barcode.dynamicFrame.Constants; +import org.uic.barcode.test.utils.SimpleUICTestTicket; +import org.uic.barcode.utils.AlgorithmNameResolver; +import org.uic.ticket.EncodingFormatException; +import org.uic.ticket.api.spec.IUicRailTicket; + +public class DynamicFrameSimpleTest { + + public String signatureAlgorithmOID = null; + public String elipticCurve = null; + public String keyPairAlgorithmOID = null; + + public KeyPair keyPair = null; + + public IUicRailTicket testFCBticket = null; + + + @Before public void initialize() { + + signatureAlgorithmOID = Constants.ECDSA_SHA256; + keyPairAlgorithmOID = Constants.KG_EC_256; + elipticCurve = "secp256k1"; + + testFCBticket = SimpleUICTestTicket.getUicTestTicket(); + + Security.addProvider(new BouncyCastleProvider()); + + try { + keyPair = generateECKeys(Constants.KG_EC, elipticCurve); + //keyPair = generateECDSAKeys("ECDSA", "B-571"); + } catch (Exception e) { + assert(false); + } + + assert(keyPair != null); + + } + + + @Test public void testDynamicHeaderBarcodeEncoding() { + + IUicRailTicket ticket = testFCBticket; + + Encoder enc = null; + + try { + enc = new Encoder(ticket, null, Encoder.UIC_BARCODE_TYPE_DOSIPAS, 1, 1); + } catch (IOException | EncodingFormatException e1) { + assert(false); + } + + assert(enc != null); + + try { + enc.signLevel1("1080", keyPair.getPrivate(), signatureAlgorithmOID, "1"); + } catch (Exception e) { + assert(false); + } + + + byte[] encoded = null; + try { + encoded = enc.encode(); + } catch (Exception e) { + assert(false); + } + + assert(encoded != null); + + + + } + + @Test public void testDynamicHeaderBarcodeDecoding() { + + IUicRailTicket ticket = testFCBticket; + + Encoder enc = null; + + try { + enc = new Encoder(ticket, null, Encoder.UIC_BARCODE_TYPE_DOSIPAS, 1, 1); + } catch (IOException | EncodingFormatException e1) { + assert(false); + } + + assert(enc != null); + + try { + enc.signLevel1("1080", keyPair.getPrivate(), signatureAlgorithmOID, "1"); + } catch (Exception e) { + assert(false); + } + + + byte[] encoded = null; + try { + encoded = enc.encode(); + } catch (Exception e) { + assert(false); + } + + assert(encoded != null); + + Decoder dec = null; + try { + dec = new Decoder(encoded); + } catch (IOException e) { + assert(false); + } catch (EncodingFormatException e) { + assert(false); + } catch (DataFormatException e) { + assert(false); + } + assert(dec != null); + + int signatureCheck = 0; + try { + signatureCheck = dec.validateLevel1(keyPair.getPublic(),null); + } catch (InvalidKeyException | NoSuchAlgorithmException | SignatureException | IllegalArgumentException + | UnsupportedOperationException | IOException | EncodingFormatException e) { + assert(false); + } + + assert(signatureCheck == Constants.LEVEL1_VALIDATION_OK); + + SimpleUICTestTicket.compare(ticket, dec.getUicTicket()); + + + } + + public KeyPair generateECDSAKeys(String keyAlgorithmName, String paramName) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException{ + ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec(paramName); + KeyPairGenerator g = KeyPairGenerator.getInstance(keyAlgorithmName, "BC"); + g.initialize(ecSpec, new SecureRandom()); + return g.generateKeyPair(); + } + + public KeyPair generateECKeys(String keyAlgorithmOid, String curve) throws Exception{ + + String keyAlgorithmName = AlgorithmNameResolver.getName(AlgorithmNameResolver.TYPE_KEY_GENERATOR_ALG, keyAlgorithmOid, "BC"); + + keyAlgorithmName = "ECDSA"; + ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec(curve); + KeyPairGenerator g = KeyPairGenerator.getInstance(keyAlgorithmName, "BC"); + g.initialize(ecSpec, new SecureRandom()); + return g.generateKeyPair(); + } + + +} diff --git a/src/org/uic/barcode/test/SignatureSplitTest.java b/src/org/uic/barcode/test/SignatureSplitTest.java new file mode 100644 index 0000000..2e51525 --- /dev/null +++ b/src/org/uic/barcode/test/SignatureSplitTest.java @@ -0,0 +1,76 @@ +package org.uic.barcode.test; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.math.BigInteger; + +import org.junit.Test; +import org.uic.barcode.staticFrame.StaticFrame; +import org.uic.barcode.test.utils.TestUtils; +import org.uic.ticket.EncodingFormatException; + +public class SignatureSplitTest { + + + /* + * + * + * World-Schema DEFINITIONS AUTOMATIC TAGS ::= + BEGIN + Signature ::= SEQUENCE OF INTEGER + END + + + value Signature ::= { + 340282366920938463, + 134515671861986 + } + + + Encoding using DER encoding rule + Signature SEQUENCE OF: tag = [UNIVERSAL 16] constructed; length = 18 + INTEGER: tag = [UNIVERSAL 2] primitive; length = 8 + 340282366920938463 + INTEGER: tag = [UNIVERSAL 2] primitive; length = 6 + 134515671861986 + Encoded successfully in 20 bytes: 30120208 04B8ED02 83A6D3DF 02067A57 5ED68AE2 + + + * + * + */ + + + @Test public void testSplitSignature() throws IOException, EncodingFormatException{ + + BigInteger i1 = BigInteger.valueOf(340282366920938463L); + BigInteger i2 = BigInteger.valueOf(134515671861986L); + + byte[] encoded = StaticFrame.encodeSignatureIntegerSequence(i1,i2); + + + String hex = TestUtils.hexStringFromBytes(encoded); + + assertEquals(hex,"3012020804B8ED0283A6D3DF02067A575ED68AE2"); + + BigInteger[] ints = null; + try { + ints = StaticFrame.decodeSignatureIntegerSequence(encoded); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + assert(ints != null); + + assert(ints.length == 2); + + assert(i1.equals(ints[0])); + + assert(i2.equals(ints[1])); + + } + + +} \ No newline at end of file diff --git a/src/org/uic/barcode/test/StaticFrameBarcodeTest.java b/src/org/uic/barcode/test/StaticFrameBarcodeTest.java new file mode 100644 index 0000000..a0855de --- /dev/null +++ b/src/org/uic/barcode/test/StaticFrameBarcodeTest.java @@ -0,0 +1,193 @@ +package org.uic.barcode.test; + +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.security.Security; +import java.security.SignatureException; +import java.util.zip.DataFormatException; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.junit.Before; +import org.junit.Test; +import org.uic.barcode.Decoder; +import org.uic.barcode.Encoder; +import org.uic.barcode.dynamicFrame.Constants; +import org.uic.barcode.staticHeader.ticketLayoutBarcode.TicketLayout; +import org.uic.barcode.test.utils.SimpleTestTicketLayout; +import org.uic.barcode.test.utils.SimpleUICTestTicket; +import org.uic.ticket.EncodingFormatException; +import org.uic.ticket.api.spec.IUicRailTicket; + +/** + * The Class StaticFrameBarcodeTest. + */ +public class StaticFrameBarcodeTest { + + /** The algorithm OID. */ + public String algorithmOID = Constants.DSA_SHA224; + + public int keySize = 2048; + + /** The key pair. */ + public KeyPair keyPair = null; + + + public IUicRailTicket testFCBticket = null; + + public TicketLayout testLayout = null; + + + /** + * Initialize. + * + * set the signature algorithm + * generate a key pair + * set the test content + * for ticket and layout + */ + @Before public void initialize() { + + algorithmOID = Constants.DSA_SHA224; + keySize = 2048; + testFCBticket = SimpleUICTestTicket.getUicTestTicket(); + testLayout = SimpleTestTicketLayout.getSimpleTestTicketLayout(); + + Security.addProvider(new BouncyCastleProvider()); + + try { + keyPair = generateDSAKeys(keySize); + } catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) { + e.printStackTrace(); + } + + assert(keyPair != null); + + } + + + /** + * Test dynamic header barcode encoding. + */ + @Test public void testDynamicHeaderBarcodeEncoding() { + + IUicRailTicket ticket = testFCBticket; + + TicketLayout layout = testLayout; + + Encoder enc = null; + + try { + enc = new Encoder(ticket, layout, Encoder.UIC_BARCODE_TYPE_CLASSIC, 2, 1); + } catch (IOException | EncodingFormatException e1) { + assert(false); + } + + enc.setStaticHeaderParams("123456789012", "de"); + + assert(enc != null); + + try { + enc.signLevel1("1080", keyPair.getPrivate(), algorithmOID, "1"); + } catch (Exception e) { + assert(false); + } + + byte[] encoded = null; + try { + encoded = enc.encode(); + } catch (Exception e) { + assert(false); + } + + assert(encoded != null); + + } + + /** + * Test dynamic header barcode decoding. + */ + @Test public void testDynamicHeaderBarcodeDecoding() { + + + IUicRailTicket ticket = testFCBticket; + + TicketLayout layout = testLayout; + + + Encoder enc = null; + + try { + enc = new Encoder(ticket, layout, Encoder.UIC_BARCODE_TYPE_CLASSIC, 2, 1); + } catch (IOException | EncodingFormatException e1) { + assert(false); + } + + enc.setStaticHeaderParams("123456789012", "de"); + + assert(enc != null); + + try { + enc.signLevel1("1080", keyPair.getPrivate(), algorithmOID, "1"); + } catch (Exception e) { + assert(false); + } + + + byte[] encoded = null; + try { + encoded = enc.encode(); + } catch (Exception e) { + assert(false); + } + + assert(encoded != null); + + Decoder dec = null; + try { + dec = new Decoder(encoded); + } catch (IOException e) { + assert(false); + } catch (EncodingFormatException e) { + assert(false); + } catch (DataFormatException e) { + assert(false); + } + assert(dec != null); + + int signatureCheck = 0; + try { + signatureCheck = dec.validateLevel1(keyPair.getPublic(),algorithmOID); + } catch (InvalidKeyException | NoSuchAlgorithmException | SignatureException | IllegalArgumentException + | UnsupportedOperationException | IOException | EncodingFormatException e) { + assert(false); + } + + assert(signatureCheck == Constants.LEVEL1_VALIDATION_OK); + + SimpleUICTestTicket.compare(ticket, dec.getUicTicket()); + + SimpleTestTicketLayout.compare(layout, dec.getLayout()); + + } + + /** + * Generate DSA keys. + * + * @return the key pair + * @throws NoSuchAlgorithmException the no such algorithm exception + * @throws NoSuchProviderException the no such provider exception + * @throws InvalidAlgorithmParameterException the invalid algorithm parameter exception + */ + public KeyPair generateDSAKeys(int keySize) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException{ + KeyPairGenerator g = KeyPairGenerator.getInstance("DSA", "BC"); + g.initialize(keySize, new SecureRandom()); + return g.generateKeyPair(); + } + +} diff --git a/src/org/uic/barcode/test/TicketLayoutTest.java b/src/org/uic/barcode/test/TicketLayoutTest.java new file mode 100644 index 0000000..0210896 --- /dev/null +++ b/src/org/uic/barcode/test/TicketLayoutTest.java @@ -0,0 +1,45 @@ +package org.uic.barcode.test; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; + +import org.junit.Test; +import org.uic.barcode.staticFrame.UTLAYDataRecord; +import org.uic.barcode.staticHeader.ticketLayoutBarcode.TicketLayout; +import org.uic.barcode.test.utils.SimpleTestTicketLayout; +import org.uic.barcode.test.utils.TestUtils; +import org.uic.ticket.EncodingFormatException; + + +public class TicketLayoutTest { + + + @Test public void testTicketLayout() throws IOException, EncodingFormatException{ + + UTLAYDataRecord tl1 = new UTLAYDataRecord(); + + TicketLayout layout = SimpleTestTicketLayout.getSimpleTestTicketLayout(); + tl1.setLayout(layout); + + byte[] encoded = null; + try { + encoded = tl1.encode(); + } catch (IOException e) { + throw (e); + } + + String hex = TestUtils.hexStringFromBytes(encoded); + + assertEquals(hex,"555F544C41593031303034305243543230303031303130313031323030303030374DC3BC6C6C6572"); + + UTLAYDataRecord tl2 = new UTLAYDataRecord(); + + tl2.decode(tl1.encode()); + + + assertEquals(tl1.toString(),tl2.toString()); + + } + +} diff --git a/src/org/uic/barcode/test/utils/Level2TestDataFactory.java b/src/org/uic/barcode/test/utils/Level2TestDataFactory.java new file mode 100644 index 0000000..a361561 --- /dev/null +++ b/src/org/uic/barcode/test/utils/Level2TestDataFactory.java @@ -0,0 +1,20 @@ +package org.uic.barcode.test.utils; + +import org.uic.barcode.dynamicFrame.DataType; + +import net.gcdc.asn1.datatypesimpl.OctetString; + +public class Level2TestDataFactory { + + public static DataType getLevel2SimpleTestData() { + + String testContent = "2020.10.01T12:12.20"; + + DataType level2Data = new DataType(); + level2Data.setFormat("TEST"); + level2Data.setData(new OctetString(testContent.getBytes())); + + return level2Data; + } + +} diff --git a/src/org/uic/barcode/test/utils/SimpleDynamicFrameTestBarcode.java b/src/org/uic/barcode/test/utils/SimpleDynamicFrameTestBarcode.java new file mode 100644 index 0000000..1782ca6 --- /dev/null +++ b/src/org/uic/barcode/test/utils/SimpleDynamicFrameTestBarcode.java @@ -0,0 +1,80 @@ +package org.uic.barcode.test.utils; + +import java.security.KeyPair; + +import org.uic.barcode.dynamicFrame.Constants; +import org.uic.barcode.dynamicFrame.DataType; +import org.uic.barcode.dynamicFrame.DynamicFrame; +import org.uic.barcode.dynamicFrame.Level1DataType; +import org.uic.barcode.dynamicFrame.Level2DataType; +import org.uic.barcode.dynamicFrame.SequenceOfDataType; +import org.uic.ticket.EncodingFormatException; +import org.uic.ticket.api.asn.omv1.UicRailTicketData; +import org.uic.ticket.api.test.SimpleUicTestTicket; + + +public class SimpleDynamicFrameTestBarcode { + + public static DynamicFrame getSimpleDynamicHeaderBarcode(String algorithm, KeyPair keyPair) { + + + + DynamicFrame barcode = null; + + + + try { + barcode = new DynamicFrame(); + barcode.setFormat(Constants.DYNAMIC_BARCODE_FORMAT_DEFAULT); + Level2DataType level2Data = new Level2DataType(); + barcode.setLevel2SignedData(level2Data); + + Level1DataType level1Data = new Level1DataType(); + level2Data.setLevel1Data(level1Data); + + level1Data.setSecurityProvider("1080"); + level1Data.setKeyId(1L); + + level1Data.setLevel1SigningAlg(Constants.ECDSA_SHA256); + + DataType data = new DataType(); + UicRailTicketData ticket = SimpleUicTestTicket.getUicTestTicket(); + byte[] ticketData = ticket.encode(); + data.setByteData(ticketData); + data.setFormat(Constants.DATA_TYPE_FCB_VERSION_1); + SequenceOfDataType dataSequence = new SequenceOfDataType(); + level1Data.setData(dataSequence); + level1Data.getData().add(data); + + try { + level2Data.signLevel1(keyPair.getPrivate()); + } catch (Exception e) { + assert(false); + } + + + } catch (EncodingFormatException e) { + e.printStackTrace(); + return null; + } + + return barcode; + } + + + public static void compareFrame(DynamicFrame frame1, DynamicFrame frame2) { + + assert(frame1.getLevel2SignedData().getLevel1Data().getKeyId() == frame2.getLevel2SignedData().getLevel1Data().getKeyId()); + + assert(frame1.getLevel2SignedData().getLevel1Data().getLevel1SigningAlg().equals(frame2.getLevel2SignedData().getLevel1Data().level1SigningAlg)); + + assert(frame1.getLevel2SignedData().getLevel1Data().getSecurityProvider().equals(frame2.getLevel2SignedData().getLevel1Data().getSecurityProvider())); + + DataType data1 = frame1.getLevel2SignedData().getLevel1Data().getData().get(0); + DataType data2 = frame2.getLevel2SignedData().getLevel1Data().getData().get(0); + + assert(data1.getFormat().equals(data2.getFormat())); + + } + +} diff --git a/src/org/uic/barcode/test/utils/SimpleTestTicketLayout.java b/src/org/uic/barcode/test/utils/SimpleTestTicketLayout.java new file mode 100644 index 0000000..24cbf50 --- /dev/null +++ b/src/org/uic/barcode/test/utils/SimpleTestTicketLayout.java @@ -0,0 +1,33 @@ +package org.uic.barcode.test.utils; + +import org.uic.barcode.staticHeader.ticketLayoutBarcode.LayoutElement; +import org.uic.barcode.staticHeader.ticketLayoutBarcode.TicketLayout; + +public class SimpleTestTicketLayout { + + + public static TicketLayout getSimpleTestTicketLayout() { + + TicketLayout layout = new TicketLayout(); + + layout.setLayoutStandard("RCT2"); + + LayoutElement element = new LayoutElement(); + element.setColumn(1); + element.setLine(1); + element.setHeight(1); + element.setWidth(20); + element.setText("Müller"); + layout.addLayoutElement(element); + + return layout; + + } + + public static void compare(TicketLayout layout1, TicketLayout layout2) { + + assert(layout1.getLayoutStandard().equals(layout2.getLayoutStandard())); + + } + +} diff --git a/src/org/uic/barcode/test/utils/SimpleUICTestTicket.java b/src/org/uic/barcode/test/utils/SimpleUICTestTicket.java new file mode 100644 index 0000000..3f38515 --- /dev/null +++ b/src/org/uic/barcode/test/utils/SimpleUICTestTicket.java @@ -0,0 +1,254 @@ +package org.uic.barcode.test.utils; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.uic.ticket.api.impl.SimpleCardReference; +import org.uic.ticket.api.impl.SimpleControlDetail; +import org.uic.ticket.api.impl.SimpleCustomerStatusDescription; +import org.uic.ticket.api.impl.SimpleExtension; +import org.uic.ticket.api.impl.SimpleIssuingDetail; +import org.uic.ticket.api.impl.SimpleOpenTicket; +import org.uic.ticket.api.impl.SimpleStationPassage; +import org.uic.ticket.api.impl.SimpleTicketLink; +import org.uic.ticket.api.impl.SimpleToken; +import org.uic.ticket.api.impl.SimpleTraveler; +import org.uic.ticket.api.impl.SimpleTravelerDetail; +import org.uic.ticket.api.impl.SimpleUicRailTicket; +import org.uic.ticket.api.spec.ICardReference; +import org.uic.ticket.api.spec.IControlDetail; +import org.uic.ticket.api.spec.ICustomerStatusDescription; +import org.uic.ticket.api.spec.IExtension; +import org.uic.ticket.api.spec.IIssuingDetail; +import org.uic.ticket.api.spec.ILinkMode; +import org.uic.ticket.api.spec.IOpenTicket; +import org.uic.ticket.api.spec.IStationPassage; +import org.uic.ticket.api.spec.ITicketLink; +import org.uic.ticket.api.spec.IToken; +import org.uic.ticket.api.spec.ITraveler; +import org.uic.ticket.api.spec.IUicRailTicket; + +public class SimpleUICTestTicket { + + + public static IUicRailTicket getUicTestTicket() { + IUicRailTicket ticket = new SimpleUicRailTicket(); + populateTicket(ticket); + return ticket; + } + + + private static void populateTicket(IUicRailTicket ticket) { + + ticket.setControlDetails(new SimpleControlDetail()); + populate(ticket.getControlDetails()); + + + ticket.setIssuerDetails(new SimpleIssuingDetail()); + populateIssuingData(ticket.getIssuerDetails()); + + SimpleTravelerDetail td = new SimpleTravelerDetail(); + populateTravelerData(td); + ticket.setTravelerDetails(td); + + + //OpenTicket + IOpenTicket do1 = new SimpleOpenTicket(); + populate(do1); + ticket.addOpenTicket(do1); + + //StationPassage + IStationPassage do2 = new SimpleStationPassage(); + populateStationPassage(do2); + ticket.addStationPassage(do2); + + //token + IToken to = new SimpleToken(); + to.setTokenProvider("VDV"); + byte[] ba = { (byte) 0x82, (byte) 0xDA }; + to.setToken(ba); + + + ticket.addExtension(populateExtension()); + + } + + /* + ticket stationPassage : { + productName "passage" + ,station {8312345} + ,stationNameUTF8 { "Amsterdam" } + ,validFromDay 0 + ,validUntilDay 4 + } + */ + private static void populateStationPassage(IStationPassage sp) { + sp.setProductName("passage"); + + try { + Date date = new SimpleDateFormat("dd/MM/yyyy").parse("01/01/2018"); + sp.setValidFrom(date); + } catch (ParseException e) { + e.printStackTrace(); + } + try { + Date date = new SimpleDateFormat("dd/MM/yyyy").parse("04/01/2018"); + sp.setValidUntil(date); + } catch (ParseException e) { + e.printStackTrace(); + } + + sp.addStation("8312345"); + sp.addStationName("Amsterdam"); + + } + + /* + { + token {tokenProviderIA5 "VDV", token '82DA'H } + ,ticket openTicket : { + returnIncluded FALSE + infoText "openTicketInfo" + } + } + */ + + private static void populate(IOpenTicket otd) { + otd.setInfoText("openTicketInfo"); + otd.setReturnIncluded(false); + } + + + /* + ,travelerDetail{ + traveler { + { + firstName "John" + ,secondName "Dow" + ,idCard "12345" + ,ticketHolder TRUE + ,status {{customerStatusDescr "senior" }} + } + } + ,groupName "myGroup" + } + */ + + private static void populateTravelerData(SimpleTravelerDetail td) { + td.setGroupName("myGroup"); + ITraveler tr = new SimpleTraveler(); + tr.setIdCard("12345"); + tr.setFirstName("John"); + tr.setSecondName("Dow"); + tr.setTicketHolder(true); + ICustomerStatusDescription cst = new SimpleCustomerStatusDescription(); + tr.addStatusDescription(cst); + cst.setDescription("senior"); + td.addTraveler(tr); + } + + /* + ,issuingDetail { + issuerNum 1080 + issuingYear 2018 + issuingDay 1 + specimen TRUE, + securePaperTicket FALSE, + activated TRUE, + issuerPNR "issuerTestPNR", + issuedOnLine 12 + } + */ + private static void populateIssuingData(IIssuingDetail iIssuingDetail) { + + try { + Date date = new SimpleDateFormat("dd/MM/yyyy").parse("01/01/2018"); + iIssuingDetail.setIssuer("1080"); + iIssuingDetail.setIssuingDate(date); + } catch (ParseException e) { + e.printStackTrace(); + } + + + iIssuingDetail.setIssuerPNR("issuerTestPNR"); + iIssuingDetail.setSpecimen(true); + iIssuingDetail.setSecurePaperTicket(false); + iIssuingDetail.setActivated(true); + iIssuingDetail.setIssuedOnLine(12); + } + + /* + ,extension { + { extensionId "1", extensionData '82DA'H } + ,{ extensionId "2", extensionData '83DA'H } + } + */ + private static IExtension populateExtension() { + IExtension ed1 = new SimpleExtension(); + ed1.setId("1"); + byte[] ba1 = { (byte) 0x82, (byte) 0xDA }; + ed1.setBinarydata(ba1); + return ed1; + } + + /* + ,controlDetail { + identificationByCardReference { + { trailingCardIdNum 100 } + } + ,identificationByIdCard FALSE + ,identificationByPassportId FALSE + ,passportValidationRequired FALSE + ,onlineValidationRequired FALSE + ,ageCheckRequired FALSE + ,reductionCardCheckRequired FALSE + ,infoText "cd" + ,includedTickets { + { productOwnerIA5 "test" } + } + } + */ + private static void populate(IControlDetail iControlDetail) { + iControlDetail.setInfoText("cd"); + iControlDetail.setAgeCheckRequired(false); + iControlDetail.setIdentificationByIdCard(false); + iControlDetail.setIdentificationByPassportId(false); + iControlDetail.setOnlineValidationRequired(false); + iControlDetail.setPassportValidationRequired(false); + iControlDetail.setReductionCardCheckRequired(false); + iControlDetail.getIdentificationByCardReference().add(populateCardRefrence()); + iControlDetail.addLinkedTicket(populateLinkedTicket()); + } + + + /* + * + */ + private static ITicketLink populateLinkedTicket() { + ITicketLink it = new SimpleTicketLink(); + it.setProductOwner("test"); + it.setLinkMode(ILinkMode.issuedTogether); + return it; + } + + /* + { + trailingCardIdNum 100 + } + */ + private static ICardReference populateCardRefrence() { + ICardReference cr = new SimpleCardReference(); + cr.setTrailingCardId("100"); + return cr; + } + + + public static void compare(IUicRailTicket ticket1, IUicRailTicket ticket2) { + + assert(ticket1.getIssuerDetails().getIssuer().equals(ticket2.getIssuerDetails().getIssuer())); + + } + + +} diff --git a/src/org/uic/barcode/test/utils/TestUtils.java b/src/org/uic/barcode/test/utils/TestUtils.java new file mode 100644 index 0000000..7311ae4 --- /dev/null +++ b/src/org/uic/barcode/test/utils/TestUtils.java @@ -0,0 +1,38 @@ +package org.uic.barcode.test.utils; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class TestUtils { + + final protected static char[] hexArray = "0123456789ABCDEF".toCharArray(); + + public static String hexStringFromBytes(byte[] bytes) { + char[] hexChars = new char[bytes.length * 2]; + for (int j = 0; j < bytes.length; j++) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = hexArray[v >>> 4]; + hexChars[j * 2 + 1] = hexArray[v & 0x0F]; + } + return new String(hexChars); + } + + + public static Date parseDate (String source){ + + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); + + try { + return formatter.parse(source); + } catch (ParseException e) { + try { + return formatter.parse("2001-01-01"); + } catch (ParseException e1) { + return null; + } + } + + } + +} diff --git a/src/org/uic/barcode/utils/AlgorithmNameResolver.java b/src/org/uic/barcode/utils/AlgorithmNameResolver.java new file mode 100644 index 0000000..300bf7d --- /dev/null +++ b/src/org/uic/barcode/utils/AlgorithmNameResolver.java @@ -0,0 +1,173 @@ +package org.uic.barcode.utils; + +import java.security.Provider; +import java.security.Provider.Service; +import java.security.Security; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class AlgorithmNameResolver { + + + public static final String TYPE_KEY_GENERATOR_ALG = "KeyPairGenerator"; + public static final String TYPE_SIGNATURE_ALG = "Signature"; + + + private static final Pattern KEY_TYPE_PATTERN = Pattern.compile("^(\\w+)[.].*$"); + private static final Pattern KEY_ALIAS_TYPE_PATTERN = Pattern.compile("^Alg[.]Alias[.](\\w+).*$"); + private static final Pattern KEY_OID_PATTERN = Pattern.compile(".*?(\\d+(?:[.]\\d+){3,})$"); + + public static String getSignatureAlgorithmName (String oid) throws Exception { + + Provider[] provs = Security.getProviders(); + for (Provider prov : provs) { + Service service = prov.getService(AlgorithmNameResolver.TYPE_SIGNATURE_ALG,oid); + if (service != null) { + return service.getAlgorithm(); + } + } + return null; + + } + + + public static String getName(String type, String oid) throws Exception { + + Provider[] provs = Security.getProviders(); + + for (Provider prov : provs) { + + SortedSet typeAndOID = getTypeAndOIDStrings(prov); + + for (String entry : typeAndOID) { + String[] typeAndOIDArray = entry.split("-"); + String ptype = typeAndOIDArray[0]; + if (ptype.equalsIgnoreCase(type)) { + String poid = typeAndOIDArray[1]; + Service pservice = prov.getService(ptype, poid); + String palgo = pservice.getAlgorithm(); + + if (ptype.equalsIgnoreCase(type) && poid.equals(oid)) { + return palgo; + } + } + } + } + + + if (oid.startsWith("1.2.840.10045")) { + return "ECDSA"; + } else if (oid.startsWith("1.2.840.10040")) { + return "DSA"; + } + + return null; + + } + + public static String getName(String type, String oid, String providerName) throws Exception { + + Provider[] provs = Security.getProviders(); + + for (Provider prov : provs) { + + if (providerName == null || prov.getName().equals(providerName)) { + + SortedSet typeAndOID = getTypeAndOIDStrings(prov); + + for (String entry : typeAndOID) { + String[] typeAndOIDArray = entry.split("-"); + String ptype = typeAndOIDArray[0]; + if (ptype.equalsIgnoreCase(type)) { + String poid = typeAndOIDArray[1]; + Service pservice = prov.getService(ptype, poid); + String palgo = pservice.getAlgorithm(); + + if (ptype.equalsIgnoreCase(type) && poid.equals(oid)) { + return palgo; + } + } + } + } + } + + + if (oid.startsWith("1.2.840.10045")) { + return "ECDSA"; + } else if (oid.startsWith("1.2.840.10040")) { + return "DSA"; + } + + return null; + + } + + private static SortedSet getTypeAndOIDStrings(Provider prov) { + + SortedSet typeAndOID = new TreeSet<>(); + + Set keys = prov.keySet(); + for (Object key : keys) { + String keyString = key.toString(); + Matcher oidMatcher = KEY_OID_PATTERN.matcher(keyString); + if (oidMatcher.matches()) { + // get OID from matched keyString + String oid = oidMatcher.group(1); + + // determine type + String type; + Matcher aliasTypeMatcher = KEY_ALIAS_TYPE_PATTERN.matcher(keyString); + if (aliasTypeMatcher.matches()) { + type = aliasTypeMatcher.group(1); + } else { + Matcher typeMatcher = KEY_TYPE_PATTERN.matcher(keyString); + typeMatcher.matches(); + type = typeMatcher.group(1); + } + // algorithm parameters are not algorithms, so skip them + if (type.equals("AlgorithmParameters")) { + continue; + } + + // auto-removes dupes + typeAndOID.add(type + "-" + oid); + } + } + return typeAndOID; + } + + + public static String getSecurityNames() throws Exception { + + Provider[] provs = Security.getProviders(); + + StringBuilder sb = new StringBuilder(); + + for (Provider prov : provs) { + + SortedSet typeAndOID = getTypeAndOIDStrings(prov); + + for (String entry : typeAndOID) { + String[] typeAndOIDArray = entry.split("-"); + String ptype = typeAndOIDArray[0]; + String poid = typeAndOIDArray[1]; + Service pservice = prov.getService(ptype, poid); + String palgo = ""; + if ( pservice != null) { + palgo = pservice.getAlgorithm(); + } + sb.append(prov).append(";"); + sb.append(ptype).append(";"); + sb.append(poid).append(";"); + sb.append(palgo).append(";"); + sb.append("\r\n"); + + } + } + return sb.toString(); + + } +} diff --git a/src/org/uic/barcode/utils/package.html b/src/org/uic/barcode/utils/package.html new file mode 100644 index 0000000..45a73a9 --- /dev/null +++ b/src/org/uic/barcode/utils/package.html @@ -0,0 +1,20 @@ + + + + + +

Implementation of the header frames to bundle all data for a bar code.

+ +

+ + Provides a decoding and encoding of the header data frames for:

+ +

    +
  • static header as defined in UIC IRS 90918-9
  • +
  • dynamic header as drafted for the UIC IRS 90918-9
  • + +
+

+ + + \ No newline at end of file -- cgit v1.2.3