package org.uic.barcode.ssbFrame;
import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.SignatureException;
import java.security.Provider.Service;
import java.util.Arrays;
import org.uic.barcode.ticket.EncodingFormatException;
import org.uic.barcode.utils.AlgorithmNameResolver;
import org.uic.barcode.utils.SecurityUtils;
public class SsbFrame {
private SsbHeader header = new SsbHeader();
private byte[] signaturePart1 = null;
private byte[] signaturePart2 = null;
private SsbNonUic nonUicData = null;
private SsbNonReservation nonReservationData = null;
private SsbReservation reservationData = null;
private SsbGroup groupData = null;
private SsbPass passData = null;
public void decode(byte[] bytes) throws EncodingFormatException {
if (bytes.length != 114) {
throw new EncodingFormatException("Data size does not fit to SSB");
}
if (header == null) {
header = new SsbHeader();
}
int offset = 0;
offset = offset + header.decodeContent(bytes,0);
if (header.getTicketType().equals(SsbTicketType.UIC_1_IRT_RES_BOA)) {
reservationData = new SsbReservation();
offset = offset + reservationData.decodeContent(bytes,offset);
} else if (header.getTicketType().equals(SsbTicketType.UIC_2_NRT)) {
nonReservationData = new SsbNonReservation();
offset = offset + nonReservationData.decodeContent(bytes,offset);
} else if (header.getTicketType().equals(SsbTicketType.UIC_3_GRP)) {
groupData = new SsbGroup();
offset = offset + groupData.decodeContent(bytes, offset);
} else if (header.getTicketType().equals(SsbTicketType.UIC_4_RPT)) {
passData = new SsbPass();
offset = offset + passData.decodeContent(bytes,offset);
} else {
nonUicData = new SsbNonUic();
offset = offset + nonUicData.decodeContent(bytes,offset);
}
byte[] signatureBytes = new byte[56];
try {
//check for non-standard signature encoding
BigInteger[] bInts = SecurityUtils.decodeSignatureIntegerSequence(signatureBytes);
SecurityUtils.encodeSignatureIntegerSequence(bInts[0],bInts[1]);
signaturePart1 = bInts[0].toByteArray();
signaturePart2 = bInts[1].toByteArray();
//decoding the entire signature was ok, so there was no split
} catch (Exception e) {
//the signature is correctly implemented, continue with recombination
signaturePart1 = new byte[28];
signaturePart2 = new byte[28];
for (int i = 0 ; i < 28;i++) {
signaturePart1[i] = bytes[58 + i];
signaturePart2[i] = bytes[58 + 28 + i];
}
}
}
public byte[] encode() throws EncodingFormatException {
byte[] bytes = new byte[114];
int offset = header.encodeContent(bytes,0);
if (nonUicData != null) {
offset = nonUicData.encodeContent(bytes, offset);
} else if (nonReservationData != null) {
offset = nonReservationData.encodeContent(bytes, offset);
} else if (reservationData != null) {
offset = reservationData.encodeContent(bytes, offset);
} else if (groupData != null) {
offset = groupData.encodeContent(bytes, offset);
} else if (passData != null) {
offset = passData.encodeContent(bytes, offset);
} else {
throw new EncodingFormatException("Data Content for SSB missing");
};
if (signaturePart1.length > 28) {
throw new EncodingFormatException("Signature too large");
}
if (signaturePart2.length > 28) {
throw new EncodingFormatException("Signature too large");
}
for (int i = 1 ; i < 29; i++) {
int sigInd = signaturePart1.length - i;
if (sigInd < signaturePart1.length && sigInd >= 0) {
bytes[58 + 28 - i] = signaturePart1[sigInd];
} else {
bytes[58 + 28 - i] = '\0';
}
sigInd = signaturePart2.length - i;
if (sigInd < signaturePart2.length && sigInd >= 0) {
bytes[58 + 28 + 28 - i] = signaturePart2[sigInd];
} else {
bytes[58 + 28 + 28 - i] = '\0';
}
}
return bytes;
}
public byte[] getDataForSignature() throws EncodingFormatException {
byte[] bytes = new byte[58];
int offset = header.encodeContent(bytes,0);
if (nonUicData != null) {
offset = nonUicData.encodeContent(bytes, offset);
} else if (nonReservationData != null) {
offset = nonReservationData.encodeContent(bytes, offset);
} else if (reservationData != null) {
offset = reservationData.encodeContent(bytes, offset);
} else if (groupData != null) {
offset = groupData.encodeContent(bytes, offset);
} else if (passData != null) {
offset = passData.encodeContent(bytes, offset);
} else {
throw new EncodingFormatException("Data Content for SSB missing");
};
return bytes;
}
public SsbHeader getHeader() {
return header;
}
public void setHeader(SsbHeader header) {
this.header = header;
}
public byte[] getSignaturePart1() {
return signaturePart1;
}
public void setSignaturePart1(byte[] signaturePart1) {
this.signaturePart1 = signaturePart1;
}
public byte[] getSignaturePart2() {
return signaturePart2;
}
public void setSignaturePart2(byte[] signaturePart2) {
this.signaturePart2 = signaturePart2;
}
public SsbNonUic getNonUicData() {
return nonUicData;
}
public void setNonUicData(SsbNonUic nonUicData) {
this.nonUicData = nonUicData;
this.nonReservationData = null;
this.reservationData = null;
this.groupData = null;
this.passData = null;
}
public SsbNonReservation getNonReservationData() {
return nonReservationData;
}
public void setNonReservationData(SsbNonReservation nonReservationData) {
this.nonReservationData = nonReservationData;
header.setTicketType(SsbTicketType.UIC_2_NRT);
this.reservationData = null;
this.nonUicData = null;
this.groupData = null;
this.passData = null;
}
public SsbReservation getReservationData() {
return reservationData;
}
public void setReservationData(SsbReservation reservationData) {
header.setTicketType(SsbTicketType.UIC_1_IRT_RES_BOA);
this.nonReservationData = null;
this.nonUicData = null;
this.groupData = null;
this.passData = null;
this.reservationData = reservationData;
}
public SsbGroup getGroupData() {
return groupData;
}
public void setGroupData(SsbGroup groupData) {
this.groupData = groupData;
header.setTicketType(SsbTicketType.UIC_3_GRP);
this.nonReservationData = null;
this.nonUicData = null;
this.reservationData = null;
this.passData = null;
}
public SsbPass getPassData() {
return passData;
}
public void setPassData(SsbPass passData) {
this.passData = passData;
header.setTicketType(SsbTicketType.UIC_4_RPT);
this.nonReservationData = null;
this.nonUicData = null;
this.groupData = null;
this.reservationData = null;
}
public void signLevel1(PrivateKey key, Provider prov, String keyId, String algorithmOid) throws Exception {
this.header.setKeyId(Integer.parseInt(keyId));
byte[] data = getDataForSignature();
if (prov == null) {
//check for a provider supporting the key
prov = SecurityUtils.findPrivateKeyProvider(key);
}
//find the algorithm name for the signature OID
String algo = AlgorithmNameResolver.getSignatureAlgorithmName(algorithmOid, prov);
Signature sig = null;
if (prov != null) {
sig = Signature.getInstance(algo, prov);
} else {
sig = Signature.getInstance(algo);
}
sig.initSign(key);
sig.update(data);
byte[] signature = sig.sign();
BigInteger[] bInts = SecurityUtils.decodeSignatureIntegerSequence(signature);
signaturePart1 = toUnsignedBytes(bInts[0]);
signaturePart2 = toUnsignedBytes(bInts[1]);
}
private static byte[] toUnsignedBytes(BigInteger i) {
byte[] b = i.abs().toByteArray();
//remove top sign bit
if (b[0] == 0) {
b = Arrays.copyOfRange(b, 1, b.length);
}
return b;
}
/**
* Verify the signature
*
* Note: an appropriate security provider (e.g. BC) must be registered before
*
* @param key the key
* @param singningAlg the Object ID of the signing algorithm
* @param a dedicated security provider to validate the signature
* @return true, if successful
* @throws InvalidKeyException the invalid key exception
* @throws NoSuchAlgorithmException the no such algorithm exception
* @throws SignatureException the signature exception
* @throws IllegalArgumentException the illegal argument exception
* @throws UnsupportedOperationException the unsupported operating exception
* @throws EncodingFormatException
* @throws IOException
*/
public boolean verifyByAlgorithmOid(PublicKey key, String signingAlg, Provider prov) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IllegalArgumentException, UnsupportedOperationException, IOException, EncodingFormatException {
//find the algorithm name for the signature OID
String algo = null;
BigInteger r = new BigInteger(1,signaturePart1);
BigInteger s = new BigInteger(1,signaturePart2);
byte[] signature = SecurityUtils.encodeSignatureIntegerSequence(r,s);
String signatureAlgorithmOid = signingAlg;
// guess the signature algorithm based on the signature size
if ((signingAlg == null || signingAlg.length() < 1) && signature != null) {
signatureAlgorithmOid = SecurityUtils.getDsaAlgorithm(signature);
}
if (prov != null) {
Service service = prov.getService("Signature",signatureAlgorithmOid);
if (service != null) {
algo = service.getAlgorithm();
}
} else {
Provider[] provs = Security.getProviders();
for (Provider p : provs) {
if (algo == null) {
Service service = p.getService("Signature",signatureAlgorithmOid);
if (service != null) {
algo = service.getAlgorithm();
}
}
}
}
if (algo == null) {
throw new NoSuchAlgorithmException("No service for algorithm found: " + signingAlg);
}
Signature sig = Signature.getInstance(algo);
sig.initVerify(key);
sig.update(getDataForSignature());
return sig.verify(signature);
}
}