At first glance, I have the same problem as many. But my case a bit more complex.
Preconditions:
Project language: Java 11
Network Server: Orbiwise NS (https://eu.saas.orbiwise.com/)
Device: (STM32 + Wifi module) connection via Lorawan gateway to Orbiwise and using TCP socket via wifi.
Input data:
From TCP socket received byte array:
40 24 fa fa 01 c2 c5 25 03 06 01 43 a4 99 5a c1
85 71 0c 87 38 84 53 9a 80 6c 5a 14 da f8 ff 7c
21 83 8f 78 8e ec f2 7d 4e 4e 07
(43 bytes)
On Orbiwise have corresponding to it uplink payload:
31 19 10 07 01 13 51 25 09 01 00 00 00 00 33 04
00 00 5A 00 00 00 EB 0D 00 00 64 EB
(28 bytes)
Task:
Decrypt data from TCP socket to have decrypted payload in the same format as on Orbiwise
Approaches were used without positive result:
https://github.com/jsubercaze/javalora
https://github.com/huahang/crypto-utils/blob/master/crypto-utils/src/main/java/im/chic/utils/crypto/AesUtils.java
https://github.com/matthiaszimmermann/ttn_decoder_java - core code for mine.
All project above was written a long time ago and not helped me. This one helped, but written on Node JS: https://github.com/anthonykirby/lora-packet
From LoraWan specification I got that used "AES/ECB/NoPadding" method and my current code looks like:
package org.thethingsnetwork.main.java.org.thethingsnetwork.util.security;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class Crypto {
/**
* Decrypts TTN data_raw payload to data according to the TTN REST API.
* #param pld encrypted message payload from ttn mqtt message
* #param K the TTN application key
* #param IV
* #return decrypted payload
* #throws Exception
*/
public static byte[] decrypt(byte [] pld, byte [] K, byte [] IV) throws Exception {
byte [] devAddr = getDevAddr(pld);
byte [] frameCounter = getFrameCounter(pld);
byte [] result = initializeResult(pld);
byte [] Ai = new byte[16];
byte [] Si = null;
for(int i = 0; i < result.length; i += 16) {
int blockSeqCnt = (i >> 4) + 1;
computeAi(Ai, devAddr, frameCounter, blockSeqCnt);
Si = encryptAES(Ai, K, IV);
for(int j=0; j < 16 && i+j < result.length; j++) {
result[i+j] ^= Si[j];
}
}
return result;
}
/**
* Converts TTN payload data to data_plain according to the TTN REST API.
* Decode a text using base 64 decoding.
* #param decryptedText
* #return
*/
public static String toPlainText(String decryptedText) {
byte [] data = Base64.getDecoder().decode(decryptedText);
StringBuffer plain = new StringBuffer();
for(int i = 0; i < data.length; i++) {
plain.append((char)data[i]);
}
return plain.toString();
}
public static byte [] getDevAddr(byte [] payload) {
byte [] devAddr = new byte[4];
System.arraycopy(payload, 1, devAddr, 0, 4);
return devAddr;
}
public static byte [] getFrameCounter(byte [] payload) {
byte [] frameCounter = new byte[2];
System.arraycopy(payload, 6, frameCounter, 0, 2);
return frameCounter;
}
public static byte [] initializeResult(byte [] payload) {
byte [] result = new byte[payload.length - 13];
for(int i = 0; i < result.length; i++) {
result[i] = payload[i+9];
}
return result;
}
public static void computeAi(byte [] a, byte [] devAddr, byte [] frameCounter, int blockSeqCnt) {
a[0] = 0x01;
a[1] = 0x00;
a[2] = 0x00;
a[3] = 0x00;
a[4] = 0x00;
a[5] = 0; // 0 for uplink frames 1 for downlink frames;
a[6] = devAddr[0]; // LSB devAddr 4 bytes
a[7] = devAddr[1]; // ..
a[8] = devAddr[2]; // ..
a[9] = devAddr[3]; // MSB
a[10] = frameCounter[0]; // LSB framecounter
a[11] = frameCounter[1]; // MSB framecounter
a[12] = 0x00; // Frame counter upper Bytes
a[13] = 0x00;
a[14] = 0x00;
a[15] = (byte)blockSeqCnt; // block sequence counter 1,2,3...
}
/**
* AES encrpytion.
*/
public static byte[] encryptAES(byte [] data, byte [] key, byte [] iv) throws Exception {
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding", "SunJCE");
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
return cipher.doFinal(data);
}
}
And second class - test:
package org.thethingsnetwork.util.security;
import org.apache.commons.codec.DecoderException;
import org.junit.Assert;
import org.junit.Test;
import org.thethingsnetwork.main.java.org.thethingsnetwork.util.security.Crypto;
import org.apache.commons.codec.binary.Hex;
public class CryptoTest {
private byte [] SEMTECH_DEFAULT_KEY = Hex.decodeHex("2E12E8BD30FE2FB2D8DE609747D2569F".toCharArray());
public static final byte [] IV = new byte [] {
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
}; // initialization vector – IV
// example data from a ttn message
public static final byte[] TEXT_RAW = new byte[] {0x40, 0x24, (byte) 0xfa, (byte) 0xfa, 0x01, (byte) 0xc2,
(byte) 0xc5, 0x25, 0x03, 0x06, 0x01, 0x43, (byte) 0xa4, (byte) 0x99, 0x5a, (byte) 0xc1, (byte) 0x85, 0x71,
0x0c, (byte) 0x87, 0x38, (byte) 0x84, 0x53, (byte) 0x9a, (byte) 0x80, 0x6c, 0x5a, 0x14, (byte) 0xda,
(byte) 0xf8, (byte) 0xff, 0x7c, 0x21, (byte) 0x83, (byte) 0x8f, 0x78, (byte) 0x8e, (byte) 0xec, (byte) 0xf2,
0x7d, 0x4e, 0x4e, 0x07};
public CryptoTest() throws DecoderException {
}
#Test
public void testDecoder() throws Exception {
byte[] decryptedText = Crypto.decrypt(TEXT_RAW, SEMTECH_DEFAULT_KEY, IV);
printTheByteToString(decryptedText);
}
/**
* Method for prin in command line byte array. For debug necessary
* #param b - input byte array
*/
private void printTheByteToString (byte[] b) {
for (byte val : b) {
System.out.print(String.format("%02x ", val));
}
}
}
I've checked the code a hundred times but no positive result - the decrypted message is different from on Orbiwise.
I've checked the data using NodeJs project (https://github.com/anthonykirby/lora-packet) and all is correct. But can't resolve the task using my Java code.
Can some body help me in this situation? Thanks a lot in advance!
Your ciphertext offset is off by 2 (as there are 3 option bytes instead of 1, no doubt). Printing out intermediate results or performing full parsing of the header bytes should show you that, which is why I mentioned it in the comments. Note that increasing the offset of 9 by 2 may also affect the ciphertext size, as the end of the ciphertext is fixed.
Furthermore, you are using Cipher.DECRYPT_MODE while the protocol only uses the cipher in forward mode. Counter mode (used by CCM and CCM* ciphers) only uses the cipher in the forward mode as they generate a key stream that is then XOR-ed with the plaintext stream or ciphertext stream to encrypt / decrypt respectively. The "decryption" part in the protocol only is about performing the final XOR of the generated key stream to the ciphertext rather than the plaintext.
Of course, ECB mode - used to simply implement a single block encrypt in this case - doesn't require an IV, so that part of the code is spurious.
Big thank to Maarten in solving the issue.
as result - correct code looks like:
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class Crypto {
/**
* Decrypts TTN data_raw payload to data according to the TTN REST API.
* #param pld encrypted message payload from ttn mqtt message
* #param K the TTN application key
* #return decrypted payload
* #throws Exception
*/
public static byte[] decrypt(byte [] pld, byte [] K) throws Exception {
byte [] devAddr = getDevAddr(pld);
byte [] frameCounter = getFrameCounter(pld);
byte [] result = initializeResult(pld);
byte [] Ai = new byte[16];
byte [] Si = null;
for(int i = 0; i < result.length; i += 16) {
int blockSeqCnt = (i >> 4) + 1;
computeAi(Ai, devAddr, frameCounter, blockSeqCnt);
Si = encryptAES(Ai, K);
for(int j=0; j < 16 && i+j < result.length; j++) {
result[i+j] ^= Si[j];
}
}
return result;
}
public static byte [] getDevAddr(byte [] payload) {
byte [] devAddr = new byte[4];
System.arraycopy(payload, 1, devAddr, 0, 4);
return devAddr;
}
public static byte [] getFrameCounter(byte [] payload) {
byte [] frameCounter = new byte[2];
System.arraycopy(payload, 6, frameCounter, 0, 2);
return frameCounter;
}
public static byte [] initializeResult(byte [] payload) {
byte [] result = new byte[payload.length - 15];
for(int i = 0; i < result.length; i++) {
result[i] = payload[i+11];
}
return result;
}
public static void computeAi(byte [] a, byte [] devAddr, byte [] frameCounter, int blockSeqCnt) {
a[0] = 0x01;
a[1] = 0x00;
a[2] = 0x00;
a[3] = 0x00;
a[4] = 0x00;
a[5] = 0; // 0 for uplink frames 1 for downlink frames;
a[6] = devAddr[0]; // LSB devAddr 4 bytes
a[7] = devAddr[1]; // ..
a[8] = devAddr[2]; // ..
a[9] = devAddr[3]; // MSB
a[10] = frameCounter[0]; // LSB framecounter
a[11] = frameCounter[1]; // MSB framecounter
a[12] = 0x00; // Frame counter upper Bytes
a[13] = 0x00;
a[14] = 0x00;
a[15] = (byte)blockSeqCnt; // block sequence counter 1,2,3...
}
/**
* AES encrpytion.
* #param data
* #param key
* #return
* #throws Exception
*/
public static byte[] encryptAES(byte [] data, byte [] key) throws Exception {
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding", "SunJCE");
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
return cipher.doFinal(data);
}
}
And test class:
import org.apache.commons.codec.DecoderException;
import org.junit.Assert;
import org.junit.Test;
import org.security.Crypto;
import org.apache.commons.codec.binary.Hex;
public class CryptoTest {
private byte [] SEMTECH_DEFAULT_KEY = Hex.decodeHex("2E12E8BD30FE2FB2D8DE609747D2569F".toCharArray());
// example data from a ttn message
public static final byte[] TEXT_RAW = new byte[] {0x40, 0x24, (byte) 0xfa, (byte) 0xfa, 0x01, (byte) 0xc2,
(byte) 0xc5, 0x25, 0x03, 0x06, 0x01, 0x43, (byte) 0xa4, (byte) 0x99, 0x5a, (byte) 0xc1, (byte) 0x85, 0x71,
0x0c, (byte) 0x87, 0x38, (byte) 0x84, 0x53, (byte) 0x9a, (byte) 0x80, 0x6c, 0x5a, 0x14, (byte) 0xda,
(byte) 0xf8, (byte) 0xff, 0x7c, 0x21, (byte) 0x83, (byte) 0x8f, 0x78, (byte) 0x8e, (byte) 0xec, (byte) 0xf2,
0x7d, 0x4e, 0x4e, 0x07};
public static final String TEXT = "31 19 10 07 01 13 51 25 09 01 00 00 00 00 33 04 00 00 5a 00 00 00 eb 0d 00 00 64 eb";
public CryptoTest() throws DecoderException {
}
#Test
public void testDecoder() throws Exception {
byte[] decryptedText = Crypto.decrypt(TEXT_RAW, SEMTECH_DEFAULT_KEY);
printTheByteToString(decryptedText);
Assert.assertEquals("decrypted text does not match", TEXT, decryptedText);
}
/**
* Method for prin in command line byte array. For debug necessary
* #param b - input byte array
*/
private void printTheByteToString (byte[] b) {
for (byte val : b) {
System.out.print(String.format("%02x ", val));
}
}
}
Related
I have a short[] that contains the pixel data of a tiff image. I want to convert it to a byte[] with the below code. Technically, the below code does the conversion. However, when I write the resulting bytes to a raw file and compare them to the original bytes of the image (before being run through my program), some of the bytes are switched around. It almost looks like the endianness of the bytes is switched, but it's not happening on every byte block. I've tried using different conversion methods from code I've found on StackOverflow and other forums, but I'm consistently getting the same result. Is there something implicit to how Java handles bytes that is causing this problem?
Code:
public static byte[] convert(short[] bytesAsShort) {
int j = 0;
int length = bytesAsShort.length;
byte[] byteData = new byte[length * 2];
for (int i = 0; i < length; i++) {
byteData[j++] = (byte) (bytesAsShort[i] >>> 8);
byteData[j++] = (byte) (bytesAsShort[i] >>> 0);
}
return byteData;
}
Any help is appreciated. Please let me know if there's anything I can do to make my question clearer. Thank you!
UPDATE:
Some sample bytes as hex values (included more for a better sample size), before:
FF 00 01 01 A0 00 A5 00 A0 00 A3 00 8F 00 AB 00 D4 00 08 01
After:
FF 01 01 00 A0 00 A5 00 A0 00 A3 00 8F 00 AB 00 D4 01 08 00
Yeah, definitely looks like the endianness is switched. Gonna test something real quick.
UPDATE 2: Another function I've tried for conversion:
public static byte[] convert(short[] bytesAsShort) {
int index;
int iterations = bytesAsShort.length;
ByteBuffer bb = ByteBuffer.allocate(bytesAsShort.length * 2);
for (index = 0; index != iterations; ++index) {
bb.putShort(bytesAsShort[index]);
}
return bb.array();
}
The following unit test demonstrates that the conversion function listed in the question correctly converts the sample data also from the question.
Therefore the problem that you are experiencing is elsewhere, and is not currently listed in the question. The problem is either in the loading of the data, or the writing of it (perhaps both). I recommend that you write a unit test that knows the starting data, and asserts the output data as it appears on disk. This will help you to track down where the problem lies. If it does not, then please post that complete unit test and we will help you further.
// unmodified convert method
public static byte[] convert(short[] bytesAsShort) {
int j = 0;
int length = bytesAsShort.length;
byte[] byteData = new byte[length * 2];
for (int i = 0; i < length; i++) {
byteData[j++] = (byte) (bytesAsShort[i] >>> 8);
byteData[j++] = (byte) bytesAsShort[i];
}
return byteData;
}
#Test
public void testConversion() {
short[] orig= new short[] {(short) 0xFF00, (short) 0x0101, (short) 0xA000, (short) 0xA500,
(short) 0xA000, (short) 0xA300, (short) 0x8F00, (short) 0xAB00, (short) 0xD400, (short) 0x0801};
// same bytes as orig, but broken up into 8 bits rather than 16
byte[] expected= new byte[] {(byte) 0xFF, 0x00, 0x01, 0x01, (byte)0xA0, 0x00, (byte)0xA5,
0x00, (byte)0xA0, 0x00, (byte)0xA3,
0x00, (byte)0x8F, 0x00, (byte)0xAB, 0x00, (byte)0xD4, 0x00, 0x08, 0x01};
byte[] result = convert( orig );
assertBytesEqual( expected, result );
}
private void assertBytesEqual( byte[] expected, byte[] actual ) {
assertEquals( expected.length, actual.length );
for ( int i=0; i<expected.length; i++ ) {
assertEquals( i+": "+expected[i] + " != " + actual[i], expected[i], actual[i] );
}
}
Takes a sequence of byte values in hex format and writes these to the byte stream.
"01 02 1a" => writes bytes 0x01 0x02 0x1a to the byte stream.
What does this mean?
Here is a possible solution:
String hex = "01 02 1a";
// Remove spaces
hex = hex.replace(" ", "");
// Array containing bytes
byte[] bytes = new byte[hex.length() / 2];
int k = 0;
for(int i=0; i < hex.length(); i = i +2 ) {
// Read and parse each byte
int b = Integer.parseInt(hex.substring(i, i + 2), 16);
bytes[k] = (byte) b;
k++;
}
// Write bytes to an outputStram
OutputStream out;
for(Byte b: bytes) {
out.write(b);
}
Hi I have the following string
String msg = "9192939495"
And i want to create the bellow byte
byte[] texttoprint = {(byte) 0x91, (byte) 0x92,(byte) 0x93,(byte) 0x94,(byte) 0x95}
i try this
public static byte[] hexStringToByteArray(String s) {
/*String input = "0102FFAB";*/
byte[] data = new byte[s.length() / 2];
for( int i = 0; i < s.length(); i+=2)
{
data[i/2] = (byte) Integer.decode( "0x" + s.substring( i, i + 2 ) ).byteValue();
}
return data;
}
but it does not works
Also how can I print texttoprint at eclipse log in order to check if everything is OK?
PS if i send to printer {(byte) 0x91, (byte) 0x92,(byte) 0x93,(byte) 0x94,(byte) 0x95} everything is OK but if i sent the result of hexStringToByteArray nothing happens
static byte[] decode(final String enc) {
final long val = Long.parseLong(enc, 16);
final byte[] raw = new byte[] {
(byte) ((val & 0xff00000000000000L) >> 56),
(byte) ((val & 0xff000000000000L) >> 48),
(byte) ((val & 0xff0000000000L) >> 40),
(byte) ((val & 0xff00000000L) >> 32),
(byte) ((val & 0xff000000) >> 24),
(byte) ((val & 0xff0000) >> 16),
(byte) ((val & 0xff00) >> 8),
(byte) (val & 0xff)
};
final int n = enc.length() >> 1;
final byte[] trimmed = new byte[n];
System.arraycopy(raw, 8 - n, trimmed, 0, n);
return trimmed;
}
You can print the values using Arrays.toString to convert to text form. To verify they're equal, try using Arrays.equals.
System.out.println(Arrays.equals(texttoprint, hexStringToByteArray("9192939495"))
? "success" : "failure");
In my byte array I have the hash values of a message which consists of some negative values and also positive values. Positive values are being printed easily by using the (char)byte[i] statement.
Now how can I get the negative value?
How about Arrays.toString(byteArray)?
Here's some compilable code:
byte[] byteArray = new byte[] { -1, -128, 1, 127 };
System.out.println(Arrays.toString(byteArray));
Output:
[-1, -128, 1, 127]
Why re-invent the wheel...
If you want to print the bytes as chars you can use the String constructor.
byte[] bytes = new byte[] { -1, -128, 1, 127 };
System.out.println(new String(bytes, 0));
Try it:
public static String print(byte[] bytes) {
StringBuilder sb = new StringBuilder();
sb.append("[ ");
for (byte b : bytes) {
sb.append(String.format("0x%02X ", b));
}
sb.append("]");
return sb.toString();
}
Example:
public static void main(String []args){
byte[] bytes = new byte[] {
(byte) 0x01, (byte) 0xFF, (byte) 0x2E, (byte) 0x6E, (byte) 0x30
};
System.out.println("bytes = " + print(bytes));
}
Output: bytes = [ 0x01 0xFF 0x2E 0x6E 0x30 ]
Well if you're happy printing it in decimal, you could just make it positive by masking:
int positive = bytes[i] & 0xff;
If you're printing out a hash though, it would be more conventional to use hex. There are plenty of other questions on Stack Overflow addressing converting binary data to a hex string in Java.
Try this one : new String(byte[])
byte[] buff = {1, -2, 5, 66};
for(byte c : buff) {
System.out.format("%d ", c);
}
System.out.println();
gets you
1 -2 5 66
in Kotlin you can use :
println(byteArray.contentToString())
I have an integer and I want to convert it to hex value. I am building a message header with each byte value of this array below indicating a specific information about the message.
I want to represent the length of the message in 2 bytes len1 and len2 below.
How do I do this?
byte[] headerMsg =new byte [] { 0x0A, 0x01, 0x00, 0x16,
0x11, 0x0d, 0x0e len1 len2};
int lenMsg //in 2 bytes
Thanks
byte[] headerMsg =new byte [] {
0x0A, 0x01, 0x00, 0x16,
0x11, 0x0d, 0x0e,
0x00, 0x00 // to be filled with length bytes
};
int hlen = headerMsg.length;
// I assume the bodyMsg byte array is defined elsewhere
int lenMsg = hlen + bodyMsg.length;
// lobyte of length - mask just one byte with 0xFF
headerMsg[hlen - 1] = (byte) (lenMsg & 0xFF);
// hibyte of length - shift to the right by one byte and then mask
headerMsg[hlen - 2] = (byte) ((lenMsg >> 8) & 0xFF);