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));
}
}
}
I want to switch the two hexadecimals symbols in a byte, for example if
input = 0xEA
then
output = 0xAE
It has to be in java.
I already have this method I made, but it only works in some cases:
public static final byte convert(byte in){
byte hex1 = (byte) (in << 4);
byte hex2 = (byte) (in >>> 4);
return (byte) (hex1 | hex2);
}
A working example is:
input: 0x3A
hex1: 0xA0
hex2: 0x03
output: 0xA3
A not working example is:
input: 0xEA
hex1: 0xA0
hex2: 0xFE
output: 0xFE
Anyone can shed some lights on why this is not working?
I suspect the problem is the sign extension. Specifically, you probably need to do
byte hex2 = (byte) ((in >>> 4) & 0xF);
try
byte hex1 = (byte) (in << 4);
byte hex2 = (byte) ( in >>> 4);
return (byte) (hex1 | hex2 & 0x0F);
this is like in a known puzzle
byte x = (byte)0xFF;
x = (byte) (x >>> 1);
System.out.println(x);
prints -1 because before unsigned shift 0xFF is promoted to int -> 0xFFFFFFFF; after shift it is 0x7FFFFFFF; cast to byte -> 0xFF
but
byte x = (byte)0xFF;
x = (byte) ((x & 0xFF) >>> 1);
System.out.println(x);
prints 127 because we truncated 0xFFFFFFFF -> 0x000000FF, now shift produces 0x0000007F, cast to byte -> 0x7F
Actually, this promotion is done at compile time. JVM works only with 4 or 8 bytes operands (local variables on stack). Even boolean in bytecode is 0 or 1 int.
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");
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Convert a string representation of a hex dump to a byte array using Java?
I am trying to convert a hex string "FFFFFFFFFFFFFFFF" into byte array with size 8
the result should be
byte[] mKey = { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF };
I have tried the for loop
public static byte[] HexString2Bytes(String src) {
byte[] res = new byte[8];
for (int i = 0; i < 16; i = i + 2) {
res[i] = convertToByte(src.substring(i, i + 2));
}
return res;
}
the problem is, I don't know how to implement the method convertToByte() to convert a hex string, like "FF" to 0xFF, please help, thanks.
int convertToByte(String s){
return Integer.parseString(s, 16);
}
I have an 8 byte array and I want to convert it to its corresponding numeric value.
e.g.
byte[] by = new byte[8]; // the byte array is stored in 'by'
// CONVERSION OPERATION
// return the numeric value
I want a method that will perform the above conversion operation.
One could use the Buffers that are provided as part of the java.nio package to perform the conversion.
Here, the source byte[] array has a of length 8, which is the size that corresponds with a long value.
First, the byte[] array is wrapped in a ByteBuffer, and then the ByteBuffer.getLong method is called to obtain the long value:
ByteBuffer bb = ByteBuffer.wrap(new byte[] {0, 0, 0, 0, 0, 0, 0, 4});
long l = bb.getLong();
System.out.println(l);
Result
4
I'd like to thank dfa for pointing out the ByteBuffer.getLong method in the comments.
Although it may not be applicable in this situation, the beauty of the Buffers come with looking at an array with multiple values.
For example, if we had a 8 byte array, and we wanted to view it as two int values, we could wrap the byte[] array in an ByteBuffer, which is viewed as a IntBuffer and obtain the values by IntBuffer.get:
ByteBuffer bb = ByteBuffer.wrap(new byte[] {0, 0, 0, 1, 0, 0, 0, 4});
IntBuffer ib = bb.asIntBuffer();
int i0 = ib.get(0);
int i1 = ib.get(1);
System.out.println(i0);
System.out.println(i1);
Result:
1
4
Assuming the first byte is the least significant byte:
long value = 0;
for (int i = 0; i < by.length; i++)
{
value += ((long) by[i] & 0xffL) << (8 * i);
}
Is the first byte the most significant, then it is a little bit different:
long value = 0;
for (int i = 0; i < by.length; i++)
{
value = (value << 8) + (by[i] & 0xff);
}
Replace long with BigInteger, if you have more than 8 bytes.
Thanks to Aaron Digulla for the correction of my errors.
If this is an 8-bytes numeric value, you can try:
BigInteger n = new BigInteger(byteArray);
If this is an UTF-8 character buffer, then you can try:
BigInteger n = new BigInteger(new String(byteArray, "UTF-8"));
Simply, you could use or refer to guava lib provided by google, which offers utiliy methods for conversion between long and byte array. My client code:
long content = 212000607777l;
byte[] numberByte = Longs.toByteArray(content);
logger.info(Longs.fromByteArray(numberByte));
You can also use BigInteger for variable length bytes. You can convert it to Long, Integer or Short, whichever suits your needs.
new BigInteger(bytes).intValue();
or to denote polarity:
new BigInteger(1, bytes).intValue();
Complete java converter code for all primitive types to/from arrays
http://www.daniweb.com/code/snippet216874.html
Each cell in the array is treated as unsigned int:
private int unsignedIntFromByteArray(byte[] bytes) {
int res = 0;
if (bytes == null)
return res;
for (int i=0;i<bytes.length;i++){
res = res | ((bytes[i] & 0xff) << i*8);
}
return res;
}
public static long byteArrayToLong(byte[] bytes) {
return ((long) (bytes[0]) << 56)
+ (((long) bytes[1] & 0xFF) << 48)
+ ((long) (bytes[2] & 0xFF) << 40)
+ ((long) (bytes[3] & 0xFF) << 32)
+ ((long) (bytes[4] & 0xFF) << 24)
+ ((bytes[5] & 0xFF) << 16)
+ ((bytes[6] & 0xFF) << 8)
+ (bytes[7] & 0xFF);
}
convert bytes array (long is 8 bytes) to long
You can try use the code from this answer: https://stackoverflow.com/a/68393576/7918717
It parses bytes as a signed number of arbitrary length. A few examples:
bytesToSignedNumber(false, 0xF1, 0x01, 0x04) returns 15794436 (3 bytes as int)
bytesToSignedNumber(false, 0xF1, 0x01, 0x01, 0x04) returns -251592444 (4 bytes as int)
bytesToSignedNumber(false, 0xF1, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04) returns -1080581331768770303 (8 of 9 bytes as long)