DES Send and Receive Modes for DESFire Authentication - java

I'm trying to authenticate DESFire card with my android application. I use the example in this link to decypher the bytes I got from the card. For that, I ruled out padding in decryption (commented out below), because DESFire documentation points it out. Also, if I don't do so, decryption returns 7 bytes for input of 8 bytes. Below are DES and TripleDES decryption functions I use:
public static byte[] TripleDES_Decrypt(byte[] data,byte[][] keys)
{
int i;
byte[] tmp = new byte[data.length];
byte[] bloc = new byte[8];
K = generateSubKeys(keys[0]);
K1 = generateSubKeys(keys[1]);
K2 = generateSubKeys(keys[2]);
for (i = 0; i < data.length; i++) {
if (i > 0 && i % 8 == 0) {
bloc = encrypt64Bloc(bloc,K2, true);
bloc = encrypt64Bloc(bloc,K1, false);
bloc = encrypt64Bloc(bloc,K, true);
System.arraycopy(bloc, 0, tmp, i - 8, bloc.length);
}
if (i < data.length)
bloc[i % 8] = data[i];
}
bloc = encrypt64Bloc(bloc,K2, true);
bloc = encrypt64Bloc(bloc,K1, false);
bloc = encrypt64Bloc(bloc,K, true);
System.arraycopy(bloc, 0, tmp, i - 8, bloc.length);
//tmp = deletePadding(tmp);
return tmp;
}
public static byte[] decrypt(byte[] data, byte[] key) {
int i;
byte[] tmp = new byte[data.length];
byte[] bloc = new byte[8];
K = generateSubKeys(key);
for (i = 0; i < data.length; i++) {
if (i > 0 && i % 8 == 0) {
bloc = encrypt64Bloc(bloc,K, true);
System.arraycopy(bloc, 0, tmp, i - 8, bloc.length);
}
if (i < data.length)
bloc[i % 8] = data[i];
}
bloc = encrypt64Bloc(bloc,K, true);
System.arraycopy(bloc, 0, tmp, i - 8, bloc.length);
//tmp = deletePadding(tmp);
return tmp;
}
According to DesFire document, I need two modes of decryption, send and receive. This blog post has some explanation about it.
However, the DESFire crypto is a bit different from the normal DES/CBC scheme: The PCD uses DES “send mode” when sending data (xor before DES), and the card uses DES “recieve mode” when recieving data (xor after DES). But when the PCD recieves data, it uses normal DES/CBC mode (xor after DES), and the card uses normal DES send mode when sending data (xor before DES).
And in Android side I follow the examples and recommendations:
// connected to tag and application
// result = encoded(randB) + af
byte[] result = idTag.transceive(Utils.wrapMessage((byte)0x0a, new byte[]{(byte)0x0}));
byte[] b0 = new byte[8];
for(int i = 0; i < 8; i++) {
b0[i] = result[i];
}
// key
byte[] key = new byte[] {(byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0,
(byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0,
(byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0,
(byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0 };
byte[][] keys = new byte[3][];
keys[0]=key; keys[1]=key; keys[2]=key;
// decrypt encoded(randB)
byte[] r0 = DES.TripleDES_Decrypt(b0, keys);
// generate randA (integer 0-7 for trying)
byte[] nr = new byte[8];
for(int i = 0; i < 8; i++) {
nr[i] = Byte.parseByte(Integer.toString(i), 16);
}
// decrypt randA
byte[] b1 = DES.TripleDES_Decrypt(nr, keys);
// shift randB and get randB'
byte[] r1 =new byte[8];
for(int i = 0; i < 7; i++) {
r1[i] = r0[i + 1];
}
r1[7]=r0[0];
// concat (randA + randB')
byte[] b2 = new byte[16];
for(int i = 0; i < 16; i++)
{
if(i <= 7) {
b2[i] = b1[i];
} else {
b2[i] = r1[i - 8];
}
}
// XOR (randA + randB') with IV
// IV is told to be consisting of 0's,
// but XOR something with 0 results the same?
for(int i=0;i<16;i++) {
b2[i] = (byte) (b2[i] ^ (byte)0x0);
}
// send AF and decrypt(A+B)
// wrap message adds needed wrapping to message (90 to left, offset bytes etc.)
result = isodepTag.transceive(Utils.wrapMessage((byte)0xaf, DES.TripleDES_Decrypt(b2, keys)));
I get the first result, the encrypted randB. However, the second "result" is always "91ae", means authentication error. I'm doing something wrong here, send wrong data to card.
Can anyone tell me what must I change in the code to work in these modes? What should I XOR with data before/after TripleDES?
Not the real question, but I read that default "Key" in DesFire card is 16 zero bytes. Also the document points that I need to use TripleDES for 16 bytes of key, DES for 8 bytes of key. So I'm using and need to use TripleDES as I haven't changed the default key, am I right?
For those who need the know about CipherBlockChaining.
EDIT: I found out that I need to do XORing before and after TripleDES and I mustn't touch TripleDES's internal operations at all. I will be trying that in a while.
Deleted the inner TripleDES lines, just saying for the ones seeing the question for the first time.

OK I got the solution. My mistake was that I was sending
3DES(randA + randB')
But I should send
3DES(randA) + 3DES(randB' XOR 3DES(randA))
Here's the authentication code for Android/Java (it's so sad that this is the only one that can be found on the net currently!):
The actual authentication code:
// send initial authentication request
byte[] result = idTag.transceive(Utils.wrapMessage((byte)0x0a, new byte[]{(byte)0x0}));
// get encrypted(randB) from the response
byte[] b0 = new byte[8];
for(int i = 0; i < 8; i++) {
b0[i] = result[i];
}
// 16 bytes default key
byte[] key = new byte[] {(byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0,
(byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0,
(byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0,
(byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0 };
// keys for TripleDes
byte[][] keys = new byte[3][];
keys[0] = key; keys[1] = key; keys[2] = key;
// decrypt encoded(randB)
byte[] r0 = DES.TripleDES_Decrypt(b0, keys);
// generate randA (integer 0-7 for trying, should randomize for real-life use)
byte[] nr = new byte[8];
for(int i = 0; i < 8; i++) {
nr[i] = Byte.parseByte(Integer.toString(i), 16);
}
// decrypt randA, should XOR with IV, but IV is all 0's, not necessary
byte[] b1 = DES.TripleDES_Decrypt(nr, keys);
// shift randB one byte left and get randB'
byte[] r1 =new byte[8];
for(int i = 0; i < 7; i++) {
r1[i] = r0[i + 1];
}
r1[7]=r0[0];
// xor randB' with randA and decrypt
byte[] b2 = new byte[8];
for(int i = 0; i < 8; i++) {
b2[i] = (byte) (b1[i] ^ r1[i]);
}
b2 = DES.TripleDES_Decrypt(b2, keys);
// concat (randA + randB')
byte[] b1b2 = new byte[16];
for (int i = 0; i < b1b2.length; i++) {
if(i <= 7) {
b1b2[i] = b1[i];
} else {
b1b2[i]=b2[i-8];
}
}
result = idTag.transceive(Utils.wrapMessage((byte)0xaf, b1b2));
TripleDes is the one in the question. wrapMessage function:
public static byte[] wrapMessage (byte command, byte[] parameters) throws Exception {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
stream.write((byte) 0x90);
stream.write(command);
stream.write((byte) 0x00);
stream.write((byte) 0x00);
if (parameters != null) {
stream.write((byte) parameters.length);
stream.write(parameters);
}
stream.write((byte) 0x00);
return stream.toByteArray();
}
EDIT: Thanks to VGe0rge, we found out the reason why this authentication doesn't work from time to time. Instead of calling the 3DES function in the question, just call:
Cipher.getInstance("DESede/CBC/NoPadding");

Ismat, did you try to write data to the DESFIRE files?
As you explained, when sending data to the card: The PCD uses DES “send mode” when sending data (xor before DES), and the card uses DES “recieve mode” when recieving data (xor after DES)
So I can't get the proper code in order to implement the TDES with the XOR before..I need to do all the crypt-decrypt-crypt and the process is to slow for my application:
res = criptoTransformDec.TransformBlock(datosEscribir, 0, 8, datosEscribir, 0);
res = criptoTransformEnc.TransformBlock(datosEscribir, 0, 8, datosEscribir, 0);
res = criptoTransformDec1.TransformBlock(datosEscribir, 0, 8, datosEscribir, 0);
int l_iAux = 0;
while (l_iAux < (datosEscribir.Length - 8))
{
criptoTransformDec2 = desDec.CreateDecryptor(claveSes1, tdesInitialVector);
//desEnc2 = new DESCryptoServiceProvider();
criptoTransformEnc2 = desEnc.CreateEncryptor(claveSes2, tdesInitialVector);
//desDec3 = new DESCryptoServiceProvider();
criptoTransformDec3 = desDec.CreateDecryptor(claveSes1, tdesInitialVector);
Array.Copy(datosEscribir, 8 + l_iAux, aux1, 0, 8);
Array.Copy(datosEscribir, l_iAux, aux2, 0, 8);
DesfireBarik.XorStr(ref aux1, ref aux2, 8);
res = criptoTransformDec2.TransformBlock(aux1, 0, 8, datosEscribir, 8 + l_iAux);
res = criptoTransformEnc2.TransformBlock(datosEscribir, 8 + l_iAux, 8, datosEscribir, 8 + l_iAux);
res = criptoTransformDec3.TransformBlock(datosEscribir, 8 + l_iAux, 8, datosEscribir, 8 + l_iAux);
l_iAux += 8;
}
private static void XorStr (ref byte[] str1, ref byte[] str2, int qty )
{
int i = 0;
for (i = 0; i < qty; i++ )
str1[i] = (byte)(str1[i] ^ str2[i]);
}

Related

Unable to generate Image from modified Byte array in java

In my code I'm reading image convert it to byte array and modifying that byte array with some logic and trying to generate image from that modified byte array, but i'm unable to generate image from that code
my code sample:
//1. Convert Image to byte code
ByteArrayOutputStream baos=new ByteArrayOutputStream();
BufferedImage img=ImageIO.read(new File(dirName,"MyImg.png"));
ImageIO.write(img, "png", baos);
baos.flush();
byte[] bytes = baos.toByteArray();
byte[] modified = baos.toByteArray();
String temp_string = new String();
for (int i = 0; i < bytes.length; i++)
{
// conversion of byte to unsign byte
int b = bytes[i] & 0xFF;
/*
* convert byte array to an 8 bit string
*/
int temp,count = 1;
byte b1 = (byte)b;
String uv = String.format("%8s", Integer.toBinaryString(b1 & 0xFF)).replace(' ', '0');
String tempStr = "";
for(int zx = 0 ; zx < uv.length() ; zx++ )
{
temp = Character.getNumericValue(uv.charAt(zx));
if(temp == 1)
{
temp += count;
count = temp;
if(temp % 2 == 0)
temp = 0;
else
temp = 1;
tempStr += temp;
}
else if(temp == 0)
{
tempStr += 0;
}
}
temp_string += tempStr;
if(i < bytes.length)
{
temp_string +=",";
}
}
String[] string_ByteArray = temp_string.split(",");
for(int a =0 ; a < string_ByteArray.length ; a++)
{
int aaa = Integer.parseInt(string_ByteArray[a],2);
modified[a] = (byte) aaa;
}
//3. Convert byte code to Image
ByteArrayInputStream bis = new ByteArrayInputStream(modified);
BufferedImage bImage2 = ImageIO.read(bis);
ImageIO.write(bImage2, "png", new File("output.png") );
in this code i'm getting error in 3rd step:
Exception in thread "main" java.lang.IllegalArgumentException: image == null!
at javax.imageio.ImageTypeSpecifier.createFromRenderedImage(Unknown Source)
at javax.imageio.ImageIO.getWriter(Unknown Source)
at javax.imageio.ImageIO.write(Unknown Source)
at mypack.Img_conversion.main(Img_conversion.java:96)
That is an impressively roundabout way of doing bit manipulation. There is no valid reason to use Strings. I suggest you either use bitwise operators, or use a BitSet.
Iterating through bits mathematically:
int b = bytes[i] & 0xFF;
for (int j = 7; j >= 0; j--) {
int bit = (b >> j) & 1;
temp = /* ... */;
if (temp != 0) {
b |= (1 << j); // set bit j
} else {
b &= ~(1 << j); // clear bit j
}
}
modified[i] = (byte) b;
Iterating through bits with a BitSet:
byte b = bytes[i];
BitSet bits = BitSet.valueOf(new byte[] { b });
for (int j = 7; j >= 0; j--) {
int bit = bits.get(j) ? 1 : 0;
temp = /* ... */;
bits.set(j, temp != 0);
}
modified[i] = bits.toByteArray()[0];
You might notice that since BitSet.valueOf takes an array of bytes, it’s wasteful to keep creating new BitSets. Instead, you could just do BitSet.valueOf(bytes) once, and run through all the bits in that single BitSet:
BitSet bits = BitSet.valueOf(bytes);
for (int i = bits.cardinality() - 1; i >= 0; i--) {
int bit = bits.get(i) ? 1 : 0;
temp = /* ... */;
bits.set(i, temp != 0);
}
byte[] modified = bits.toByteArray();
However…
A PNG image is (usually) compressed. This means the bits do not directly correspond to pixels. Modifying those bits creates an invalid compressed data block, which is why your attempt to read it with ImageIO.read fails and returns null.
If you want bytes you can directly manipulate, get them from the raw BufferedImage, not from a PNG representation:
int[] pixels = img.getData().getPixels(
0, 0, img.getWidth(), img.getHeight(),
new int[0]);
byte[] bytes = pixels.length * 4;
ByteBuffer.wrap(bytes).asIntBuffer().put(pixels);
It would be much easier for others to help you, if you took the time to give your variables meaningful names. temp and uv and zx are cryptic and meaningless. Better names would be:
temp_string → allByteValues
uv → bitsOfByte
tempStr → newBits
zx → bitIndex (or just a typical secondary indexing variable, like j)
temp → bit
When you’re done modifying the bytes, you still have raw image data, not a PNG representation, so you cannot make a ByteArrayInputStream from those bytes and pass them to ImageIO.read. Attempting to pass off those bytes as a PNG representation will always fail.
Instead, overwrite your image with the pixel data:
int[] pixels = new int[bytes.length / 4];
ByteBuffer.wrap(bytes).asIntBuffer().get(pixels);
img.getRaster().setPixels(0, 0, img.getWidth(), img.getHeight(), pixels);
ImageIO.write(img, "png", new File("output.png"));
As stated in the documentation if any of the parameter of the write method is null it will throw IllegalArgumentException.
You call like this:
ImageIO.write(bImage2, "png", new File("output.png") );
The only parameter which can be null is the bImage2.
Please check it if it's really null.

Java: convert byte[] to base36 String

I'm a bit lost. For a project, I need to convert the output of a hash-function (SHA256) - which is a byte array - to a String using base 36.
So In the end, I want to convert the (Hex-String representation of the) Hash, which is
43A718774C572BD8A25ADBEB1BFCD5C0256AE11CECF9F9C3F925D0E52BEAF89
to base36, so the example String from above would be:
3SKVHQTXPXTEINB0AT1P0G45M4KI8U0HR8PGB96DVXSTDJKI1
For the actual conversion to base36, I found some piece of code here on StackOverflow:
public static String toBase36(byte[] bytes) {
//can provide a (byte[], offset, length) method too
StringBuffer sb = new StringBuffer();
int bitsUsed = 0; //will point how many bits from the int are to be encoded
int temp = 0;
int tempBits = 0;
long swap;
int position = 0;
while((position < bytes.length) || (bitsUsed != 0)) {
swap = 0;
if(tempBits > 0) {
//there are bits left over from previous iteration
swap = temp;
bitsUsed = tempBits;
tempBits = 0;
}
//fill some bytes
while((position < bytes.length) && (bitsUsed < 36)) {
swap <<= 8;
swap |= bytes[position++];
bitsUsed += 8;
}
if(bitsUsed > 36) {
tempBits = bitsUsed - 36; //this is always 4
temp = (int)(swap & ((1 << tempBits) - 1)); //get low bits
swap >>= tempBits; //remove low bits
bitsUsed = 36;
}
sb.append(Long.toString(swap, 36));
bitsUsed = 0;
}
return sb.toString();
}
Now I'm doing this:
// this creates my hash, being a 256-bit byte array
byte[] hash = PBKDF2.deriveKey(key.getBytes(), salt.getBytes(), 2, 256);
System.out.println(hash.length); // outputs "256"
System.out.println(toBase36(hash)); // outputs total crap
the "total crap" is something like
-7-14-8-1q-5se81u0e-3-2v-24obre-73664-7-5-5cor1o9s-6h-4k6hr-5-4-rt2z0-30-8-2u-8-onz-4a2j-6-8-18-8trzza3-3-2x-6-4153to-4e3l01me-6-azz-2-k-4ckq-nav-gu-irqpxx-el-1j-6-rmf8hs-1bb5ax-3z25u-2-2r-t5-22-6-6w1v-1p
so it's not even close to what I want. I tried to find a solution now, but it seems I'm a bit lost here. How do I get the base36-encoded String representation of the Hash that I need?
Try using BigInteger:
String hash = "43A718774C572BD8A25ADBEB1BFCD5C0256AE11CECF9F9C3F925D0E52BEAF89";
//use a radix of 16, default would be 10
String base36 = new BigInteger( hash, 16 ).toString( 36 ).toUpperCase();
This might work:
BigInteger big = new BigInteger(your_byte_array_to_hex_string, 16);
big.toString(36);

XXTEA encryption using 128 bit fixed key in java

I must get the output of 56 bytes of raw binary in hexadecimal format.
The below coding is what I have tried, but I am getting wrong output.
This is the output I am expecting:
0xd19f261efa71440103519930f483de39c4deffb333c35b89c88d505ad4203e98b0d1f34233dad8‌​2a4b9ba0a3531ede93203691a2b96fb45a
But I am getting different output:
87168739A1D8972742F476D48F139348B06EDF38545BC3C00434DD5C464B2FDF8512EF75D05D897E‌​A5523F8C1589AB5106A5CC986C859CBB
private byte[] encryptByTea(String info){
byte[] temp = info.getBytes();
int n = 8 - temp.length % 8;//Multiple if temp number less than 8, need to fill in the numbers
byte[] encryptStr = new byte[temp.length + n];
encryptStr[0] = (byte)n;
System.arraycopy(temp, 0, encryptStr, n, temp.length);
byte[] result = new byte[encryptStr.length];
for(int offset = 0; offset <result.length; offset += 8){
byte[] tempEncrpt = tea.encrypt(encryptStr, offset, KEY, 32);
System.arraycopy(tempEncrpt, 0, result, offset, 8);
}
return result;
}
//Through the TEA algorithm decryption information
private String decryptByTea(byte[] secretInfo){
byte[] decryptStr = null;
byte[] tempDecrypt = new byte[secretInfo.length];
for(int offset = 0; offset <secretInfo.length; offset += 8){
decryptStr = tea.decrypt(secretInfo, offset, KEY, 32);
System.arraycopy(decryptStr, 0, tempDecrypt, offset, 8);
}
int n = tempDecrypt[0];
return new String(tempDecrypt, n, decryptStr.length - n);
}

Display image on Pebble via AppMessage

I am trying to display a 128px * 128px image on Pebble, sent from Android over AppMessage.
In Android app, I have a BitmapFactory ARGB_8888 bmp, and I do:
byte pbi[] = new byte[16*128];
for(int i = 0; i < 128; i++) {
for(int j = 0; j < 128; j++) {
if(bmp.getPixel(i, j) >= 0x7FFFFFFF) {
pbi[i*16] |= 1 << (j % 8); //set pixel (white)
}
pbi[i*16] &= ~(1 << (j % 8)); //explicitly set = 0 (black)
}
}
PebbleDictionary data;
byte send[];
for (int k = 0; k < 128; k++){
data = new PebbleDictionary();
send = new byte[16];
send = Arrays.copyOfRange(pbi, k*16, (k+1)*16-1);
data.addBytes(KEY_IMG, send);
data.addInt16(KEY_ROW, (byte)k);
PebbleKit.sendDataToPebble(getApplicationContext(), PEBBLE_APP_UUID, data);
}
Pebble has:
static int transNum;
static uint8_t received[128][16] = {0};
static void in_received_handler(DictionaryIterator *iter, void *context) {
Tuple *img_tuple = dict_find(iter, KEY_IMG);
Tuple *row_tuple = dict_find(iter, KEY_ROW);
if (img_tuple && row_tuple) {
memcpy(img_data[(int)(row_tuple->value->cstring)], img_tuple->value->data, 16);
}
}
Which should be displayed by:
static void window_load(Window *window) {
static Window *window;
static BitmapLayer *img_layer;
static GBitmap img;
static void window_load(Window *window) {
L img_layer = bitmap_layer_create( (GRect) {
.origin = {8, 20},
.size = {128, 128}
});
img = (GBitmap) {
.addr = img_data,
.bounds = GRect(8, 20, 128, 128),
.row_size_bytes = 16,
};
bitmap_layer_set_bitmap(img_layer, &img);
bitmap_layer_set_alignment(img_layer, GAlignCenter);
layer_add_child(window_get_root_layer(window), bitmap_layer_get_layer(img_layer));
display_no_img();
}
But on window_load, I get the expected black square, but I can't figure how to make it load the received data.
Edit:
It's hard for me to keep the code above updated, but it's pretty representative. I'm hoping someone can tell me what to do to make it load the received bytes. I thought the secret was in .addr = img_data, alas.. Thanks.
You should take a look at the Pebble-faces example. It uses JavaScript but you could probably completely reuse the C part and just change the phone code.

Sending a Java UUID to C++ as bytes and back over TCP

I'm trying to send a Java UUID to C++, where it will be used as a GUID, then send it back and see it as a UUID, and I'm hoping to send it across as just 16 bytes.
Any suggestions on an easy way to do this?
I've got a complicated way of doing it, sending from Java to C++, where I ask the UUID for its least and most significant bits, write this into a ByteBuffer, and then read it out as bytes.
Here is my silly-complicated way of getting 2 longs out of a UUID, sending them to C++:
Java
public static byte[] asByteArray(UUID uuid)
{
long msb = uuid.getMostSignificantBits();
long lsb = uuid.getLeastSignificantBits();
byte[] buffer = new byte[16];
for (int i = 0; i < 8; i++) {
buffer[i] = (byte) (msb >>> 8 * (7 - i));
}
for (int i = 8; i < 16; i++) {
buffer[i] = (byte) (lsb >>> 8 * (7 - i));
}
return buffer;
}
byte[] bytesOriginal = asByteArray(uuid);
byte[] bytes = new byte[16];
// Reverse the first 4 bytes
bytes[0] = bytesOriginal[3];
bytes[1] = bytesOriginal[2];
bytes[2] = bytesOriginal[1];
bytes[3] = bytesOriginal[0];
// Reverse 6th and 7th
bytes[4] = bytesOriginal[5];
bytes[5] = bytesOriginal[4];
// Reverse 8th and 9th
bytes[6] = bytesOriginal[7];
bytes[7] = bytesOriginal[6];
// Copy the rest straight up
for ( int i = 8; i < 16; i++ )
{
bytes[i] = bytesOriginal[i];
}
// Use a ByteBuffer to switch our ENDIAN-ness
java.nio.ByteBuffer buffer = java.nio.ByteBuffer.allocate(16);
buffer.order(java.nio.ByteOrder.BIG_ENDIAN);
buffer.put(bytes);
buffer.order(java.nio.ByteOrder.LITTLE_ENDIAN);
buffer.position(0);
UUIDComponents x = new UUIDComponents();
x.id1 = buffer.getLong();
x.id2 = buffer.getLong();
C++
google::protobuf::int64 id1 = id.id1();
google::protobuf::int64 id2 = id.id2();
char* pGuid = (char*) &guid;
char* pGuidLast8Bytes = pGuid + 8;
memcpy(pGuid, &id1, 8);
memcpy(pGuidLast8Bytes, &id2, 8);
This works, but seems way too complex, and I can't yet get it working in the other direction.
(I'm using google protocol buffers to send the two longs back and forth)
Alex
I got something working.
Instead of sending it across as two longs, I send it across as bytes, here is the Java code:
public static UUID fromBytes( ByteString byteString)
{
byte[] bytesOriginal = byteString.toByteArray();
byte[] bytes = new byte[16];
// Reverse the first 4 bytes
bytes[0] = bytesOriginal[3];
bytes[1] = bytesOriginal[2];
bytes[2] = bytesOriginal[1];
bytes[3] = bytesOriginal[0];
// Reverse 6th and 7th
bytes[4] = bytesOriginal[5];
bytes[5] = bytesOriginal[4];
// Reverse 8th and 9th
bytes[6] = bytesOriginal[7];
bytes[7] = bytesOriginal[6];
// Copy the rest straight up
for ( int i = 8; i < 16; i++ )
{
bytes[i] = bytesOriginal[i];
}
return toUUID(bytes);
}
public static ByteString toBytes( UUID uuid )
{
byte[] bytesOriginal = asByteArray(uuid);
byte[] bytes = new byte[16];
// Reverse the first 4 bytes
bytes[0] = bytesOriginal[3];
bytes[1] = bytesOriginal[2];
bytes[2] = bytesOriginal[1];
bytes[3] = bytesOriginal[0];
// Reverse 6th and 7th
bytes[4] = bytesOriginal[5];
bytes[5] = bytesOriginal[4];
// Reverse 8th and 9th
bytes[6] = bytesOriginal[7];
bytes[7] = bytesOriginal[6];
// Copy the rest straight up
for ( int i = 8; i < 16; i++ )
{
bytes[i] = bytesOriginal[i];
}
return ByteString.copyFrom(bytes);
}
private static byte[] asByteArray(UUID uuid)
{
long msb = uuid.getMostSignificantBits();
long lsb = uuid.getLeastSignificantBits();
byte[] buffer = new byte[16];
for (int i = 0; i < 8; i++) {
buffer[i] = (byte) (msb >>> 8 * (7 - i));
}
for (int i = 8; i < 16; i++) {
buffer[i] = (byte) (lsb >>> 8 * (7 - i));
}
return buffer;
}
private static UUID toUUID(byte[] byteArray) {
long msb = 0;
long lsb = 0;
for (int i = 0; i < 8; i++)
msb = (msb << 8) | (byteArray[i] & 0xff);
for (int i = 8; i < 16; i++)
lsb = (lsb << 8) | (byteArray[i] & 0xff);
UUID result = new UUID(msb, lsb);
return result;
}
Doing it this way, the bytes can be used straight up on the C++ side. I suppose the switching around of the order of the bytes could be done on either end.
C++
memcpy(&guid, data, 16);
It's possibly easiest to use getMostSignificantBits and getLeastSignificant bits to get long values, and send those. Likewise you can reconstruct the UUID from those two longs using the appropriate constructor.
It's a shame there isn't a toByteArray/fromByteArray pair of methods :(
Your current way is fine, nothing wrong about doing it that way.
Another approace is yo just communicate with the string representation of the uuid, send the string, parse it in c++.
Btw, bytes do not have endianess, Unless you're casting a byte/char array or similar to an integer type, you just determine the endianess by assigning the bytes back in the approprate order.
Here is what I do to convert a C++ GUID to a Java UUID. On the C++ side, the GUID struct is just converted to bytes. The conversion to C++ can then just go along the same lines.
public static UUID cppGuidBytesToUuid(byte[] cppGuid) {
ByteBuffer b = ByteBuffer.wrap(cppGuid);
b.order(ByteOrder.LITTLE_ENDIAN);
java.nio.ByteBuffer out = java.nio.ByteBuffer.allocate(16);
out.order(ByteOrder.BIG_ENDIAN);
out.putInt(b.getInt());
out.putShort(b.getShort());
out.putShort(b.getShort());
out.put(b);
out.position(0);
return new UUID(out.getLong(), out.getLong());
}
// Here is the JNI code ;-)
jbyteArray GUID2ByteArray(JNIEnv *env,GUID* guid)
{
if (guid == NULL)
return NULL;
jbyteArray jGUID = env->NewByteArray(sizeof(GUID));
if (jGUID == NULL)
return NULL;
env->SetByteArrayRegion(jGUID,0,sizeof(GUID),(signed char*)(guid));
if (env->ExceptionOccurred() != NULL)
return NULL;
return jGUID;
}
Perhaps you could explain why you are not just doing.
UUID uuid =
x.id1 = uuid.getMostSignificantBits();
x.id2 = uuid.getLeastSignificantBits();
P.S. As I read #Jon Skeet's post again, I think this is much the same advice. ;)

Categories

Resources