Home > Software engineering >  How can I decode the DigitalProductId registry key in java?
How can I decode the DigitalProductId registry key in java?

Time:12-25

I'm trying to grab the product key/id from the following registry key:

SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion
Key: DigitalProductId

However, it is encoded in a way that has not yet been decoded in java. I can retrieve the byte array using JNA, however cannot decode it. Here is the C# implementation of the decoder:

            string key = String.Empty;
            const int keyOffset = 52;
            byte keyBytes = (byte)((digitalProductId[66] / 6) & 1);
            digitalProductId[66] = (byte)((digitalProductId[66] & 0xf7) | (keyBytes & 2) * 4);

            const string digits = "BCDFGHJKMPQRTVWXY2346789";
            var last = 0;

            for (var i = 24; i >= 0; i--)
            {
                var current = 0;
                for (var i2 = 14; i2 >= 0; i2--)
                {
                    current *= 256;
                    current = digitalProductId[i2   keyOffset]   current;
                    digitalProductId[i2   keyOffset] = (byte)(current / 24);
                    current %= 24;
                    last = current;
                }
                key = digits[current]   key;
            }

            string keypart1 = key.Substring(1, last);
            string keypart2 = key.Substring(last   1, key.Length - (last   1));
            key = keypart1   "N"   keypart2;

            for (var i3 = 5; i3 < key.Length; i3  = 6)
            {
                key = key.Insert(i3, "-");
            }
            systemInformation.ProductKey = key;
            return systemInformation;

However, when I try to port it to java using the following implementation (FXStringUtil is a class that I made that has an insert function):

        String key = "";
        int keyOffset = 52;
        byte keyBytes = (byte) ((digitalProductId[66] / 6) & 1);
        digitalProductId[66] = (byte) ((digitalProductId[66] & 0xf7) | (keyBytes & 2) * 4);

        String digits = "BCDFGHJKMPQRTVWXY2346789";
        int last = 0;

        for (int i = 24; i >= 0; i--) {
            int current = 0;
            for (int i2 = 14; i2 >= 0; i2--) {
                current *= 256;
                current = digitalProductId[i2   keyOffset]   current;
                digitalProductId[i2   keyOffset] = (byte) (current / 24);
                current %= 24;
                last = current;
            }
            key = digits.charAt(current)   key;
        }

        String keypart1 = key.substring(1, last);
        String keypart2 = key.substring(last   1, key.length() - (last   1));
        key = keypart1   "N"   keypart2;

        for (int i3 = 5; i3 < key.length(); i3  = 6) {
            key = FXStringUtil.insertString(key,"-", i3);
        }

        return key;

I get an error on the line containing:

key = digits.charAt(current)   key;

of which is the following:

Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: -15
    at java.base/java.lang.StringLatin1.charAt(StringLatin1.java:48)
    at java.base/java.lang.String.charAt(String.java:712)
    at lol.foxxy.idfkwasbored.util.KeyUtil.DecodeProductKeyWin8AndUp(KeyUtil.java:60)
    at lol.foxxy.idfkwasbored.util.KeyUtil.getProductKey(KeyUtil.java:31)
    at lol.foxxy.idfkwasbored.Main.main(Main.java:123)

This error doesn't occur in the C# implementation, so I was wondering why the error occured. I pinned it down to this line (of which I believe is causing the issue):

current = digitalProductId[i2   keyOffset]   current;

however I don't know exactly what part of this may be causing the issue.

CodePudding user response:

There are two main differences between C# and Java that cause your Java code not to work.

The first is that in C# a byte is unsigned whereas in Java a byte is signed.

This indeed causes problems in the line you point to in your question:

                current = digitalProductId[i2   keyOffset]   current;

In Java, digitalProductId[i2 keyOffset], and hence also digitalProductId[i2 keyOffset] current, might be negative. These negative numbers will then cause the StringIndexOutOfBoundsExceptions you are getting.

The fix to your Java code is to ensure that digitalProductId[i2 keyOffset] in the above line gets converted to an int in the range 0 to 255. The easiest way to do this is to do a bitwise AND with 0xFF:

                current = (digitalProductId[i2   keyOffset] & 0xFF)   current;

The other difference is that in Java, the second parameter to String.substring(int,int) is the end index (more accurately String.substring(startIndex, endIndex) returns the substring from index startIndex to index endIndex - 1). However, in C#, the second parameter to String.Substring(Int32,Int32) is the length of the substring.

The two lines that extract the parts of the key should therefore be as follows:

        String keypart1 = key.substring(1, last   1);  // not .substring(1, last)
        String keypart2 = key.substring(last   1);

Note that for the second line, I've used the one-argument overload of String.substring that takes only the start index and returns the string from there to the end of the string. You can in fact make the same modification to your C# code:

        string keypart2 = key.Substring(last   1);

I don't have any code to get hold of a product key, so instead I made up some random bytes and ran your C# and modified Java code on this made-up data, and both programs returned the same output.

  • Related