Convert Int to Guid

I too needed to encode a primary key (a uint) of a database into a GUID. There is much discussion around whether to use a unit as your Primary key or a Guid, I won't get into that here, but I used a uint as primary key, with a guid as column and I wanted to encode the primary key into the guid, and get it back from the guid.

The below solution encodes a 24bit Primary key (a uint) to a Guid, and decodes it back. It uses DES encryption on the uint. I recognize that the question wanted a 32bit int, this works for 24bit uint, but 32 could be achieved by concatenating two GUIDs, on with the most significant 24bit, and one with the least significant 24 bit to get 48 bits in total. The algorithm is fully tested, and could be used for an int, but is not tested for negative numbers.

GUID Format

A GUID format is 32 digits and may be separated by hyphens, and include braces: 00000000-0000-0000-0000-000000000000 (D) which is 8-4-4-12 characters which is 4-2-2-6 bytes as one byte is coded into 2 characters. Other formats of GUIDs exist.

The 3 byte uint encrypted to an 8 byte code, which is written into the guid beginning at the 5th byte (which is the 10th character of the Guid ignoring brackets and '-'delimiters). Let 'z' represent the coded int, and 0 any other hex character. The resultant coded GUID is

00000000-zzzz-zzzz-zzzz-zzzz00000000

It is noted that the GUID may not be globally unique after this. However, given the GUID encodes your primary key, it WILL be unique for your primary key, and will be very difficult to extract the uint from the GUID without the encryption key....as much as DES encryption is secure.

public void Test()
{  
    Guid NewG = Guid.NewGuid();
    Guid EnryptedGuid = Utility.EncodeInt24InGUID(NewG, Num);
    uint RestoredUint = Utility.DecodeInt24FromGUID(EnryptedGuid);
}

ENCRYPTION

public static Guid EncodeInt24InGUID(Guid guid, uint x)
{
    if (x >= Math.Pow(2, 24))
            throw new ArgumentOutOfRangeException("Unsigned integer is greater than 24bit");

    string strGuid = guid.ToString();
    strGuid = Utility.RemoveChars(strGuid, "-{}");//Remove any '-' and '{}' characters
    byte[] bytes = BitConverter.GetBytes(x);
    var encryptedarray = Cypher.EncryptDES(bytes, Cypher.Key);
    string EncryptedGuid = WriteBytesToString(strGuid, encryptedarray, 9);
    Guid outg;
    Guid.TryParse(EncryptedGuid, out outg);
    return outg;
}

The RemoveChars is copied/adapted from here Removing characters from strings with LINQ

WriteBytesToString is as below

public static string WriteBytesToString(string Input, byte[] bytes, int start)
{
    StringBuilder g = new StringBuilder(Input);
    string temp;
    int ByteNum = 0;
    int CharPos = start;
    int NumBytes = (int)bytes.LongLength; 
    for (int i = 0; i < NumBytes; i++)
    {
        temp = string.Format("{0:x2}", bytes[ByteNum++]);
        g[CharPos++] = (temp.ToCharArray())[0];
        g[CharPos++] = (temp.ToCharArray())[1];
    }
    return g.ToString();
}

DECRYPTION

public static uint DecodeInt24FromGUID(Guid guid)
{
    string strGuid = guid.ToString();
    strGuid = Utility.RemoveChars(strGuid, "-{}");
    byte[] EncryptedBytes = GetBytesFromString(strGuid, 9,8);
    var decrypted = Cypher.DecryptDES(EncryptedBytes, Cypher.Key);
    uint DecryptedUint = BitConverter.ToUInt32(decrypted, 0);
    return DecryptedUint;
}

GetBytesFromString. Note that the bytes begin at the 9th index and are 8 bytes long as hard oded in the calling function. See note on the GUID format

public static byte[] GetBytesFromString(string Input, int start, int NumBytes)
{
    StringBuilder g = new StringBuilder(Input);
    byte[] Bytes = new byte[NumBytes];
    string temp;
    int CharPos = start;
    for (int i = 0; i < NumBytes; i++)
    {
        temp = g[CharPos++].ToString();
        temp += g[CharPos++].ToString();
        Bytes[i] =  byte.Parse(temp, System.Globalization.NumberStyles.HexNumber);
    }
    return Bytes;
}

The cyber class is copied from here https://www.codeproject.com/Articles/5719/Simple-encrypting-and-decrypting-data-in-C, and adapted to DES encryption. It is pasted below in full

using System.IO;
using System.Security.Cryptography;
namespace UtilityLib
{
    // This class is adapted from https://www.codeproject.com/Articles/5719/Simple-encrypting-and-decrypting-data-in-C
    public static class Cypher
    {
        public static string Key = "Asd9847Fg85ihkn52s";
        // Encrypt a byte array into a byte array using a key and an IV 
        public static byte[] EncryptDES(byte[] clearData, byte[] Key, byte[] IV)
        {
            // Create a MemoryStream to accept the encrypted bytes 
            MemoryStream ms = new MemoryStream();
            TripleDES alg = TripleDES.Create();
            alg.Key = Key;
            alg.IV = IV;
            CryptoStream cs = new CryptoStream(ms,alg.CreateEncryptor(), CryptoStreamMode.Write);
            cs.Write(clearData, 0, clearData.Length);
            cs.Close();
            byte[] encryptedData = ms.ToArray();
            return encryptedData;
        }

        public static byte[] EncryptDES(byte[] clearData, string Password)
        {
            PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,
                new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d,
            0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76});
            return EncryptDES(clearData, pdb.GetBytes(24), pdb.GetBytes(8));
        }
        public static byte[] DecryptDES(byte[] cipherData, byte[] Key, byte[] IV)
        {
            MemoryStream ms = new MemoryStream();
            TripleDES alg = TripleDES.Create(); 
            alg.Key = Key;
            alg.IV = IV;
            CryptoStream cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write);
            cs.Write(cipherData, 0, cipherData.Length);
            cs.Close();
            byte[] decryptedData = ms.ToArray();
            return decryptedData;
        }

        public static byte[] DecryptDES(byte[] cipherData, string Password)
        {
            PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,
                new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d,
            0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76});
            return DecryptDES(cipherData, pdb.GetBytes(24), pdb.GetBytes(8));
        }
    }
}

You can take the digits from an int and format them so that they look like a GUID, but that doesn't make the result a GUID. A GUID is basically a 16 byte number that is computed using an algorithm that guarantees that number is unique. You can generate GUIDs all day long on every computer in the world and not get a duplicate (at least that's the theory). A reformatted int is not unique and it's definitely not a GUID.


Had the same issues, needed a Int to Guid then back to Int. Old data that used int ID's but the processing function required a Guid. It was alot less code to write extra functions plus DB changes. Just easier to pass the Int iD in Guid form, knowing that it was not going to use this as its final saving Guid. The save is an Insert, so it was getting a new Guid at the end.

Heres's the code from above and an idea from another post about Guids to ints and getting the Int back out.

public static Guid Int2Guid(int value)
{
    byte[] bytes = new byte[16];
    BitConverter.GetBytes(value).CopyTo(bytes, 0);
    return new Guid(bytes);
}

public static int Guid2Int(Guid value)
{
    byte[] b = value.ToByteArray();
    int bint = BitConverter.ToInt32(b, 0);
    return bint;
}

This Guid2Int should only be passed a Guid that came from an Int.


Here is a simple way to do it:

public static Guid ToGuid(int value)
{
    byte[] bytes = new byte[16];
    BitConverter.GetBytes(value).CopyTo(bytes, 0);
    return new Guid(bytes);
}

You can change where the copy will happen (vary the index from 0 to 12). It really depends on how you want to define this unusual "int to Guid" conversion.

Tags:

C#