1

How can I convert the following .net usage of Guid.ToByteArray() to Java?

var g= Guid.Parse("9836f2b9-ba8c-42a6-b884-2e9eed9fb95a");
var ga = g.ToByteArray();

.Net Byte array returned

ga= [185,242,54,152,140,186,166,66,184,132,46,158,237,159,185,90]

Attempt in Java (Doesn't match .Net array)

UUID uuid = UUID.fromString("9836f2b9-ba8c-42a6-b884-2e9eed9fb95a");
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
byte[] ga= bb.array();

Java Byte array returned

ga= [-72,-124,46,-98,-19,-97,-71,90,-104,54,-14,-71,-70,-116,66,-90]

Updated w/ solution from Guid to Base64 in Java

UUID uuid = UUID.fromString("9836f2b9-ba8c-42a6-b884-2e9eed9fb95a");
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
byte[] uuid_bytes = bb.array();
byte[] guid_bytes = Arrays.copyOf(uuid_bytes,uuid_bytes.length);
guid_bytes[0] = uuid_bytes[3];
guid_bytes[1] = uuid_bytes[2];
guid_bytes[2] = uuid_bytes[1];
guid_bytes[3] = uuid_bytes[0];
guid_bytes[4] = uuid_bytes[5];
guid_bytes[5] = uuid_bytes[4];
guid_bytes[6] = uuid_bytes[7];
guid_bytes[7] = uuid_bytes[6];
byte[] ga= guid_bytes;      
  • 1
    See [`java.util.UUID`](https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/util/UUID.html) and https://stackoverflow.com/a/29836273/6395627 – Slaw Feb 27 '20 at 00:17
  • Thanks, but I've tried those methods today, but the byte array bytes don't match up. See the updated above. I should have shown this before =) I'm sure it's something stupid I need to change. The bytes have to match in order to create a SecretKeySpec to decrypt some .Net AES encrypted data I have coming in. –  Feb 27 '20 at 00:36
  • I hope there's a way to resolve this, since I have no control over the .net AES encrypted side that I need to generate a key for on the Java side =) –  Feb 27 '20 at 00:46
  • I just found a solution and it works here https://stackoverflow.com/questions/51609674/guid-to-base64-in-java. The byte ordering is a mess. I would have never figured this out =) I'll update the question above to add the solution. Thanks for helping. –  Feb 27 '20 at 01:02

2 Answers2

1

An alternate solution to the problem -

public static byte[] getByteArrayFromUUID(UUID uuid) {
    ByteBuffer mostSignificantBitsByteBuffer = ByteBuffer.allocate(Long.BYTES)
            .putLong(uuid.getMostSignificantBits());
    return ByteBuffer.allocate(Long.BYTES * 2)
            .order(ByteOrder.LITTLE_ENDIAN)
            .putShort(mostSignificantBitsByteBuffer.getShort(2))
            .putShort(mostSignificantBitsByteBuffer.getShort(0))
            .putShort(mostSignificantBitsByteBuffer.getShort(4))
            .putShort(mostSignificantBitsByteBuffer.getShort(6))
            .order(ByteOrder.BIG_ENDIAN)
            .putLong(uuid.getLeastSignificantBits())
            .array();
}
0

The near-equivalent class in Java is java.util.UUID. However, as you've noticed, the two do not give the same byte arrays. But if you execute the following and look at the array given by Java versus the array given by .NET:

import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.UUID;

public class Main {

  // expected from your question
  private static final int[] EXPECTED_BYTES = {
    185, 242, 54, 152, 140, 186, 166, 66, 184, 132, 46, 158, 237, 159, 185, 90
  };

  public static void main(String[] args) {
    UUID uuid = UUID.fromString("9836f2b9-ba8c-42a6-b884-2e9eed9fb95a");

    byte[] array = toByteArray(uuid);

    System.out.println("EXPECTED: " + Arrays.toString(EXPECTED_BYTES));
    System.out.println("ACTUAL  : " + Arrays.toString(toUnsignedInts(array)));
  }

  private static byte[] toByteArray(UUID uuid) {
    return ByteBuffer.allocate(16)
        .putLong(uuid.getMostSignificantBits())
        .putLong(uuid.getLeastSignificantBits())
        .array();
  }

  // for visual purposes only
  private static int[] toUnsignedInts(byte[] array) {
    int[] result = new int[array.length];
    for (int i = 0; i < array.length; i++) {
      result[i] = Byte.toUnsignedInt(array[i]);
    }
    return result;
  }
}

And the output:

EXPECTED: [185, 242, 54, 152, 140, 186, 166, 66, 184, 132, 46, 158, 237, 159, 185, 90]
ACTUAL  : [152, 54, 242, 185, 186, 140, 66, 166, 184, 132, 46, 158, 237, 159, 185, 90]

You'll see the arrays are almost equal, it's just the order of some bytes don't match. The last eight bytes (i.e. the least significant bits) all match, but the first four bytes are reversed, the next two bytes are reversed, and so are the next two bytes. To see it visually:

EXPECTED: [185, 242, 54, 152, 140, 186, 166, 66, 184, 132, 46, 158, 237, 159, 185, 90]
ACTUAL  : [152, 54, 242, 185, 186, 140, 66, 166, 184, 132, 46, 158, 237, 159, 185, 90]
           |---------------|  |------|  |-----|

I don't know enough to explain why this difference exists, but this comment on an answer to a question you linked to says:

See also [Universally unique identifier - Wikipedia] "Many systems encode the UUID entirely in a big-endian format." "Other systems, notably Microsoft's marshalling of UUIDs in their COM/OLE libraries, use a mixed-endian format, whereby the first three components of the UUID are little-endian, and the last two are big-endian." – Denis Dec 20 '19 at 13:06

The answer that comment is on gives a solution to your problem, which you've included in your question. That solution simply swaps bytes around to get the desired effect. Here's another solution that doesn't involve creating a copy array:

private static byte[] toByteArray(UUID uuid) {
  long mostSigBits = uuid.getMostSignificantBits();
  return ByteBuffer.allocate(16)
      .order(ByteOrder.LITTLE_ENDIAN)
      .putInt((int) (mostSigBits >> 32))
      .putShort((short) (((int) mostSigBits) >> 16))
      .putShort((short) mostSigBits)
      .order(ByteOrder.BIG_ENDIAN)
      .putLong(uuid.getLeastSignificantBits())
      .array();
}

Note: I'm not very comfortable with bit-shifting, so there may be a more succinct way of accomplishing the above that I couldn't think of.

Which gives the following output:

EXPECTED: [185, 242, 54, 152, 140, 186, 166, 66, 184, 132, 46, 158, 237, 159, 185, 90]
ACTUAL  : [185, 242, 54, 152, 140, 186, 166, 66, 184, 132, 46, 158, 237, 159, 185, 90]

Warning: Unfortunately, I'm not sure you can rely on either workaround giving the correct bytes 100% of the time.

Slaw
  • 37,820
  • 8
  • 53
  • 80