Converting LDAP objectGUID to Base64 and vice versa

Mike225
Tera Expert

I could never find ES5 compatible code that reliably converts the AD Object GUID that comes in from LDAP to and from Base64. Hope this helps someone out there!

One issue with community questions is service now does not let your body have the letters K L M without spaces 
"

The message body contains K-L-M, which is not permitted in this community. Please remove this content before sending your post.

"

So to make this compatible with this post the variable base64Chars looks funky, but will work.

Here is a script include that will do that for you:

 

var ADUtils = Class.create();
ADUtils.prototype = {
    initialize: function() {},
    base64ToGuid: function(base64String) {
        // Base64 character set
        var base64Chars = 'ABCDEFGHIJ' + 'K' + 'LMNOPQRSTUVWXYZabcdefghij' + 'k' + 'lmnopqrstuvwxyz0123456789+/';

        // Function to decode a Base64 character
        function decodeBase64Char(c) {
            var index = base64Chars.indexOf(c);
            if (index === -1) {
                return -1; // Invalid character
            }
            return index;
        }

        try {
            // Ensure the base64 string is valid
            if (!base64String || base64String.length % 4 !== 0) {
                gs.error("Invalid Base64 string: Length is not a multiple of 4. Input: " + base64String);
                return null; // Invalid Base64 string
            }

            // Remove padding characters
            var padding = 0;
            if (base64String.charAt(base64String.length - 1) === '=') {
                padding++;
                if (base64String.charAt(base64String.length - 2) === '=') {
                    padding++;
                }
                base64String = base64String.substring(0, base64String.length - padding);
            }

            // Calculate the number of bytes
            var byteCount = (base64String.length * 3) / 4 - padding;
            var byteArray = [];
            var b1, b2, b3;
            var i = 0;
            var j = 0;

            // Decode the Base64 string
            for (i = 0; i < base64String.length; i += 4) {
                var c1 = decodeBase64Char(base64String.charAt(i));
                var c2 = decodeBase64Char(base64String.charAt(i + 1));
                var c3 = decodeBase64Char(base64String.charAt(i + 2));
                var c4 = decodeBase64Char(base64String.charAt(i + 3));

                if (c1 === -1 || c2 === -1) {
                    gs.error("Invalid Base64 character at position " + i + ".  c1:" + c1 + " c2:" + c2 + " c3:" + c3 + " c4:" + c4);
                    return null; // Invalid Base64 character
                }

                b1 = (c1 << 2) | (c2 >> 4);
                byteArray[j++] = b1;

                if (c3 !== -1 && j < byteCount) { //check if we are about to write too many bytes
                    b2 = ((c2 & 15) << 4) | (c3 >> 2);
                    byteArray[j++] = b2;
                }
                if (c4 !== -1 && j < byteCount) { //check if we are about to write too many bytes
                    b3 = ((c3 & 3) << 6) | c4;
                    byteArray[j++] = b3;
                }
            }


            // Ensure the byte array is 16 bytes long
            if (byteArray.length !== 16) {
                gs.error("Byte array is not 16 bytes long. Found: " + byteArray.length + ". Expected 16.  Array contents: " + byteArray);
                return null;
            }

            // Reconstruct the GUID parts with the specific byte order reversal
            function toHex(value) {
                var hex = value.toString(16).toUpperCase();
                return hex.length === 1 ? '0' + hex : hex;
            }

            var part1 = (
                toHex(byteArray[3]) +
                toHex(byteArray[2]) +
                toHex(byteArray[1]) +
                toHex(byteArray[0])
            );

            var part2 = (
                toHex(byteArray[5]) +
                toHex(byteArray[4])
            );

            var part3 = (
                toHex(byteArray[7]) +
                toHex(byteArray[6])
            );

            var part4 = (
                toHex(byteArray[8]) +
                toHex(byteArray[9])
            );

            var part5Arr = [];
            for (var k = 10; k < 16; k++) {
                part5Arr.push(toHex(byteArray[k]));
            }
            var part5 = part5Arr.join('');

            // Format the GUID string
            var guid = (part1 + '-' + part2 + '-' + part3 + '-' + part4 + '-' + part5).toLowerCase();
            return guid;
        } catch (error) {
            gs.error("Error during Base64 to GUID conversion:" + JSON.stringify(error, null, 4));
            return null;
        }
    },
    guidToBase64: function(guidToConvert) {
        // Remove hyphens and convert to lowercase
        var hexString = guid.replace(/-/g, '').toLowerCase();

        // Ensure the hex string has 32 characters
        if (hexString.length !== 32) {
            gs.error("Invalid GUID format. Input:" + guid);
            return null;
        }

        // Convert hex string to byte array, applying specific byte order
        var byteArray = [];

        // Part 1 (bytes 0-3): Reverse byte order
        byteArray[0] = parseInt(hexString.substring(6, 8), 16);
        byteArray[1] = parseInt(hexString.substring(4, 6), 16);
        byteArray[2] = parseInt(hexString.substring(2, 4), 16);
        byteArray[3] = parseInt(hexString.substring(0, 2), 16);

        // Part 2 (bytes 4-5): Reverse byte order
        byteArray[4] = parseInt(hexString.substring(10, 12), 16);
        byteArray[5] = parseInt(hexString.substring(8, 10), 16);

        // Part 3 (bytes 6-7): Reverse byte order
        byteArray[6] = parseInt(hexString.substring(14, 16), 16);
        byteArray[7] = parseInt(hexString.substring(12, 14), 16);

        // Part 4 (bytes 8-9): No reversal
        byteArray[8] = parseInt(hexString.substring(16, 18), 16);
        byteArray[9] = parseInt(hexString.substring(18, 20), 16);

        // Part 5 (bytes 10-15): No reversal
        for (var i = 0; i < 6; i++) {
            byteArray[10 + i] = parseInt(hexString.substring(20 + (i * 2), 22 + (i * 2)), 16);
        }

        // Convert byte array to Base64 string (without btoa)
        var base64Chars = 'ABCDEFGHIJ' + 'K' + 'LMNOPQRSTUVWXYZabcdefghij' + 'k' + 'lmnopqrstuvwxyz0123456789+/';
        var base64String = '';
        var padding = '';

        for (var i = 0; i < byteArray.length; i += 3) {
            var a = byteArray[i];
            var b = byteArray[i + 1];
            var c = byteArray[i + 2];

            var c1 = (a >> 2) & 0x3f;
            var c2 = ((a & 0x3) << 4) | ((b & 0xf0) >> 4);
            var c3 = ((b & 0x0f) << 2) | ((c & 0xc0) >> 6);
            var c4 = c & 0x3f;

            base64String += base64Chars.charAt(c1);
            base64String += base64Chars.charAt(c2);

            if (isNaN(b)) {
                padding = '=';
                base64String += padding;
                break;
            }
            base64String += base64Chars.charAt(c3);

            if (isNaN(c)) {
                padding = '=';
                base64String += padding;
                break;
            }
            base64String += base64Chars.charAt(c4);
        }
        while (base64String.length % 4) {
            base64String += '=';
            padding++;
        }
        return base64String;
    },
    type: 'ADUtils'
};
1 REPLY 1

jawnmower
Tera Contributor

I'm implementing this from an OnComplete transform script that calls 

 
ADUtils.base64ToGuid();
So how should I get base64String passed into the script include? Where is base64String declared, AD?