I have an Avl Packet that I'm recieving through GPRS from a Device.
The protocol manual says the packet has a 16bit CRC on the last 4 bytes and a source code for CRC calculation is given:
public static int getCrc16(Byte[] buffer) {
return getCrc16(buffer, 0, buffer.length, 0xA001, 0);
}
public synchronized static int getCrc16(Byte[] buffer, int offset, int bufLen, int polynom, int preset) {
preset &= 0xFFFF;
polynom &= 0xFFFF;
int crc = preset;
for (int i = 0; i < bufLen; i++) {
int data = buffer[i + offset] & 0xFF;
crc ^= data;
for (int j = 0; j < 8; j++) {
if ((crc & 0x0001) != 0) {
crc = (crc >> 1) ^ polynom;
} else {
crc = crc >> 1;
}
}
}
return crc & 0xFFFF;
}
So I get the CRC number the packet sends me,then I call getCrc16 for the Byte array in which I have stored the packet and then compare the two numbers right??
Here is the code I use inside my program:
public static String toBinaryString(byte n) {
StringBuilder sb = new StringBuilder("00000000");
for (int bit = 0; bit < 8; bit++) {
if (((n >> bit) & 1) > 0) {
sb.setCharAt(7 - bit, '1');
}
}
return sb.toString();
}
int CalculatedCRC = getCrc16(AvlPacket);
System.out.println("Calculated CRC= " + CalculatedCRC);
int index = (AvlPacket.length)-4;
String BinaryRecievedCRC = "";
for (int j = 0; j < 4; j++) {
BinaryRecievedCRC+= toBinaryString(AvlPacket[index]);
index+=1;
}
int RecievedCRC = Integer.parseInt(BinaryRecievedCRC, 2);
System.out.println("Recieved CRC= " + RecievedCRC);
toBinaryString() converts a byte to it's binary from and puts it into a string!
So I calculate the CRC through getCrc16() given to me from the manual.Then take an index 4 bytes before the end of the packet so I can read the last 4 bytes and get the CRC sent with the packet!
The for loop takes each of the last bytes and with toBinaryString() combines all them in binary form and into a String!So I got something like 0000000000000000101011011101001 (The manual states that first two bytes are always zeroes because its a 16bit CRC)
So I just parse the Binary String into a signed int and Compare the two CRCs...!
Yet I get Results like :
Calculated CRC= 21395
-----Recieved CRC= 30416
or
Calculated CRC= 56084
-----Recieved CRC= 10504
I've tested with many packets and they can't all have loss of data..And I'm parsing the data too so I know that the data I get is correct!
What am I missing in all this??
There is probably something wrong with the documentation wording (or your understanding of it). If there is a 16-Bit CRC in a packet, its most likely occupying two bytes, not four (in binary form). If it were a decimal, even four bytes wouldn't suffice (you would need 5 didgts to store it as unsigned decimal string).
Your code shows you do conversions (but I can't see what kind of conversion its supposed to do):
BinaryRecievedCRC+= toBinaryString(AvlPacket[index]);
I would expect the CRC to be store in the data in binary form, so I assume the only thing you need to figure out are which endianess is used and where the CRC is stored in the data.
Edit: Judging from your comment you would need to extract the CRC like this:
public int getCRC(byte[] data, int index) {
return ((data[index] & 0xFF) << 8)) | (data[index + 1] & 0xFF);
}
So I get the CRC number the packet sends me,then I call getCrc16 for
the Byte array in which I have stored the packet and then compare the
two numbers right??
Wrong. You calculate the CRC over the entire message, including the CRC bytes, and the result should be zero.
Problem Solved!
The problem was that the Packet had 8 other bytes before getting into the Data part!
So I had to exclude those first 8 bytes along with the last 4 bytes of the sent CRC before calculating the CRC!
Now the numbers agree and the above code is correct with the exception that the for loop in the getCrc16 function starts from i=8 (so as to skip the first 8 bytes of the packet which do no belong to the Data part!)
Thank you all for your time!
Related
I have written both encryption and decryption methods to be able to encrypt plain text or decrypt cipher text. I am unsure how to successfully implement this, however, due to taking in the HEX and text as a STRING and unsure how to convert them to int arrays, and then how to successfully print the result out without it being a bunch of jumbled letters. What am I doing incorrectly?
The following code is the two TEA methods for either encryption or decryption:
public void encrypt (int[] block, int[] key) {
int i = block[0];
int j = block[1];
int sum = 0;
int delta = 0x9e3779b9;
for (int k = 0; k < 32; k++) {
sum += delta;
i += (j << 4 & 0xffffff0) + key[0] ^ j + sum ^ (j >> 5 & 0x7fffffff)
+ key[1];
j += (i << 4 & 0xfffffff0) + key[2] ^ i + sum ^ (i >> 5 & 0x7ffffff)
+ key[3];
}
block[0] = i;
block[1] = j;
}
public void decrypt (int[] block, int[] key) {
int i = block[0];
int j = block[1];
int sum = 0;
int delta = 0x9e3779b9;
for (int k = 0; k < 32; k++) {
i -= (i << 4 & 0xfffffff0) + key[2] ^ i + sum ^ (i >> 5 & 0x7ffffff)
+ key[3];
j -= (j << 4 & 0xfffffff0) + key[0] ^ j + sum ^ (j >> 5 & 0x7ffffff)
+ key[1];
sum -= delta;
}
block[0] = i;
block[1] = j;
}
and this is the user interface code:
public void encryptionT() {
p.plainText();
String pText = input.next();
p.hexNumber();
String hexNum = input.next();
byte hex[] = hexNum.getBytes();
byte pTextBytes[] = pText.getBytes();
byte[] encryptedPlainBase = Base64.getEncoder().encode(pTextBytes);
System.out.println(encryptedPlainBase);
}
public void decryptionT() {
p.cipherText();
String cText = input.next();
p.hexNumber();
String hexNum = input.next();
byte hex[] = hexNum.getBytes();
byte cTextBytes[] = cText.getBytes();
BigInteger hexBigInt = new BigInteger(1, hex);
BigInteger plainBigInt = new BigInteger(1, cTextBytes);
BigInteger cTextHexResult = hexBigInt.multiply(plainBigInt);
byte[] decryptedCipherText = cTextHexResult.toByteArray();
byte[] decryptedCipherBase = Base64.getDecoder().decode(decryptedCipherText);
System.out.println(decryptedCipherBase);
EDIT:
I attempted to do the suggested edits but I get the following error. I'm not sure if I just didn't understand.enter image description here
Wow, this is the first time I've seen an implementation of encryption / decryption where the actual encryption / decryption is forgotten entirely.
I'll explain about the key first - usually you start with the key, not the plaintext as the key can commonly be reused for other messages. TEA however only accepts a key, not an IV or nonce, so encrypting multiple messages with the same key will break the cipher.
The hexadecimal key must first be decoded to binary. However, you need a hexadecimal decoder for that. This is however not included in the normal Java package java.util - use e.g. the Apache codec library instead. This should leave you with the binary key of half the size you are obtaining now (the binary encoding of the hexadecimal characters).
Now you need to encode the plaintext message to binary - i.e. a byte array in Java, which you are doing in pText.getBytes().
Now you need to create the ciphertext from the plaintext message by encrypting the binary obtained above.
You are correct in seeing that the ciphertext must be encoded if you want to treat it as text. So using a base64 encoding on the result is good practice - unless binary ciphertext would also suffice (Java I/O streams and files are fine with binary values) in which case the encoding is simply not necessary.
Decryption goes the other way except for the key. You first create the key as explained above. Then you decode the base 64, decrypt the binary result and then use the String constructor that takes a byte array to retrieve the plaintext.
Note that String#getBytes and new String(byte[]) use the platform decoding by default. You may want to specify an exact encoding such as UTF-8 to be compatible between platforms.
I have the following multi-byte represented in hex format:
0xdc, 0xd3
I can process the bytes in little endian () format to get the decimal value 54236:
List<Integer> packet = new LinkedList<Integer>(Arrays.asList(0xdc, 0xd3 ));
int idx=0;
int rpm = (int)readBytes(packet, idx, 2);
private long readBytes(List<Integer> packet, int idx, int size){
long val=0;
int element;
for(int i=0;i<size;i++, idx++){
element = packet.get(idx);
val |= element << (8 * i);
}
return val;
}
The above method produces the value as expected. However, now I want to get the value in the reverse order (big endian format), but it gives me some crazy value of 14471936:
private long bigEndianReadBytes(List<Integer> packet, int idx, int size){
long val=0;
int element;
for(int i=size;i>0;i--, idx++){
element = packet.get(idx);
val |= element << (8 * i);
}
return val;
}
What might be wrong with this method?
With #TimBiegeleisen answer (+1), you have good "manual" code to for converting bytes to values.
Have you considered using a ByteBuffer? It does all this for you and works with both Big Endian and Little Endian.
public static void main(String[] args) throws Exception {
ByteBuffer bb = ByteBuffer.allocate(2);
bb.put(new byte[] {(byte)0xdc, (byte)0xd3});
System.out.println(bb.order(ByteOrder.BIG_ENDIAN).getShort(0) & 0xFFFF);
System.out.println(bb.order(ByteOrder.LITTLE_ENDIAN).getShort(0) & 0xFFFF);
}
ANDing the result against 0xFFFF so the short result is promoted to an int, otherwise the result is a negative number because java works with signed data types.
Results:
56531
54236
The for loop in your bigEndianReadBytes() method should start by shifting size - 1 bytes, and end by shifting zero bytes (in the last iteration). Currently, you are shifting size bytes in the first iteration, and one byte in the last iteration. Try this code instead:
for (int i=size-1; i >= 0; i--, idx++) {
element = packet.get(idx);
val |= element << (8 * i);
}
I am connecting to a device using the modbus protocol. I need to obtain 3 values from the machine. The first value is of the data format int16 and when I send an example byte array:
static byte[] hz = new byte[] { (byte) 0x01, (byte) 0x03, (byte) 0x00,
(byte) 0x33, (byte) 0x00, (byte) 0x01 };
and use a CRC calculation method I obtained from a previous question I asked on the subject.
// Compute the MODBUS RTU CRC
private static int ModRTU_CRC(byte[] buf, int len)
{
int crc = 0xFFFF;
for (int pos = 0; pos < len; pos++) {
crc ^= (int)buf[pos]; // XOR byte into least sig. byte of crc
for (int i = 8; i != 0; i--) { // Loop over each bit
if ((crc & 0x0001) != 0) { // If the LSB is set
crc >>= 1; // Shift right and XOR 0xA001
crc ^= 0xA001;
}
else // Else LSB is not set
crc >>= 1; // Just shift right
}
}
// Note, this number has low and high bytes swapped, so use it accordingly (or swap bytes)
return crc;
}
I can recieve a response. However, the other two values are of the int32 data format and do not return a reply when I use this method. To help troubleshoot I am using a program called Realterm. to fire off the commands as well. I use it to append a Modbus 16 CRC to the end of the byte stream and send it, this works for all three and returns the desired reply. Is this a case of the data format not working with this specific calculation formula? Whats the difference between CRC16 and Modbus16?
Modbus16 is a CRC16. CRC calculations have several parameters:
the bit width, in this case 16
the polynomial, in this case 0xA001
the initial value,in this case 0xFFFF
the bit order
whether the final CRC is inverted with an XOR.
There are quite a number of CRC16s defined, with different values for these parameters, and this appears to be one of them. See the Wikipedia article on Cyclic Redundancy Checks for more informaton.
class Obliczenia {
short POLYNOM = (short) 0x0A001;
short[] TAB = {2,3,8,0x13,0x88,1,0x90,0,0x3c,2,0};
short crc = (short) 0xffff;
short CRC_LByte,CRC_HByte;
public Obliczenia() {
for (short dana : TAB) {
crc= CRC16( crc, dana);
}
System.out.println("KOD CRC="+Integer.toHexString(crc&0xffff));
CRC_LByte=(short)(crc & 0x00ff);
CRC_HByte=(short)((crc & 0xff00)/256);
System.out.println(" W ramce CRC_LByte="+Integer.toHexString(CRC_LByte)+ " CRC_HByte "+Integer.toHexString(CRC_HByte));
}
short CRC16(short crct, short data) {
crct = (short) (((crct ^ data) | 0xff00) & (crct | 0x00ff));
for (int i = 0; i < 8; i++) {
boolean LSB = ((short) (crct & 1)) == 1;
crct=(short) ((crct >>> 1)&0x7fff);
if (LSB) {
crct = (short) (crct ^ POLYNOM);
}
}
return crct;
}
}
I need to send an 8-byte string to an SNMP agent.
My number can be a big integer as a string.
Due to the java limitation of signed bytes, I'm having a problem with some numbers.
For example, if num is "555", the SNMP agent receives the right value.
if num is "666", the SNMP agent receives the wrong value, because, one of the byte in the
array has a -ve value.
I did a bit & with 0xFF, still it doesn't work.
How can I fix this? Thanks for your help!
public static String stringNumToOctetString(String num) {
BigInteger bi = new BigInteger(num);
byte[] b = bi.toByteArray();
int n = 8 - b.length;
byte[] bVal = new byte[8]; //return must be 8 bytes
for(int i=0; i<8; i++) {
bVal[i] = (byte) 0;
}
int k = 0;
for(int j=n; j<8; j++) {
bVal[j] = (byte) (b[k++] & 0xFF);
}
return new String(bVal);
}
Use an array of int to store your octet values, not an array of byte. byte is signed, and has a range of -128 to +127, so it's not going to work here, where you need values to go to 255.
Further Reading
http://www.jguru.com/faq/view.jsp?EID=13647
What's the best way to put an int at a certain point in a byte[] array?
Say you have a byte array:
byte[] bytes = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
int someInt = 12355; //0x43, 0x30
How can I do like bytes[4] = someInt; so that now bytes[4] will equal 0x43 and bytes[5] will be equal to 0x30?
I'm used to just using memcpy with C++ and don't know the alternatives in Java.
Thanks
If you want also high 0-bytes of the int put into the byte[]:
void place(int num, byte[] store, int where){
for(int i = 0; i < 4; ++i){
store[where+i] = (byte)(num & 0xFF);
num >>= 8;
}
}
If you only want the bytes to the highest nonzero byte:
void place(int num, byte[] store, int where){
while(num != 0){
store[where++] = (byte)(num & 0xFF);
num >>>= 8;
}
}
If you want the bytes big-endian (highest byte at lowest index), the version storing all four bytes is very easy, the other one slightly more difficult:
void placeBigEndian(int num , byte[] store, int where){
for(int i = 3; i >= 0; --i){
store[where+i] = (byte)(num & 0xFF);
num >>= 8;
}
}
void placeBigEndian(int num, byte[] store, int where){
in mask = 0xFF000000, shift = 24;
while((mask & num) == 0){
mask >>>= 8;
shift -= 8;
}
while(shift > 0){
store[where++] = (byte)((num & mask) >>> shift);
mask >>>= 8;
shift -= 8;
}
}
Note, you assume a big endian ordering! x86 is little endian... What's more, your int is 32bits long, hence 0x00004330 in big endian.
If this is what you want, use a ByteBuffer (which uses big endian ordering by default):
ByteBuffer buf = ByteBuffer.allocate(8);
// then use buf.putInt(yourint, index)
// buf.get(index) will read byte at index index, starting from 0
I don't see the problem, it looks like you solved it your own way:
public static void putShort(bytes[] array, int position, short value)
{
byte leftByte = (byte) (value >>> 8);
byte rightByte = (byte) (value & 0xFF);
array[position] = leftByte;
array[position + 1] = rightByte;
}
Note that an int is 4 bytes and a short is 2 bytes.
First of all, in Java you don't need to initialize byte arrays to zeroes. All arrays are initialized on construction time to 0/false/null.
Second, ints are signed 32-bit big-endian integers, so 12355 is actually 0x00003043. If you want to use 16-bit integers, use the short type.
Then, to get the individual bytes in your integer, you could do:
bytes[ i ] = (byte) (someInt >> 24);
bytes[ i+1 ] = (byte) (someInt >> 16);
bytes[ i+2 ] = (byte) (someInt >> 8);
bytes[ i+3 ] = (byte) (someInt);
The conversion to byte truncates the remaining bits, so no & 0xFF mask is needed. I'm assuming i is the index of the array. To change the endianness, swap the offsets at the indices.
One approach would be to use a DataOutputStream and it's writeInt() method, wrapped around a ByteArrayOutputStream. e.g. (with no error-handling)
public byte[] writeIntAtPositionX(int position, int iVal) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
// now, advancing to a specific spot is awkward.
// presumably you are actually writing other stuff out before the integer
// but if you really want to advance to a specific position
for (int i = 0; i < position; i++)
dos.writeByte(0);
dos.writeInt(iVal);
dos.flush();
dos.close();
return baos.toByteArray();
}
The big advantage of this method is that the guys who wrote Java figured out the byte ordering and the masking with 0xFF and all that stuff. Plus, if you ever envision writing doubles, shorts, longs or Strings etc. to your buffer you won't need to add all those methods, the work is already done.