How to Port the database created using Mono.data.SqlCipher to latest library SQLite.Net.Platform.SQLCipher.XamarinAndroid

We are used the old library Mono.data.SqlCipher to create the database. Now we are upgrading to the latest version which uses SQLite.Net.Core-PCL.

Formerly we use following API to create a database which used byte array as key.
byte[] key; //byte array is generated by randomization algorithm
connection = new SQLiteConnection(databasePath, key);

But the latest library doesn’t accept byte array as the key, it accepts only string as a key.

connection = new SQLiteConnection (new SQLitePlatformAndroid (keyString), databasePath);

Could you please help me, how open the database with the latest library using byte array as key.

1 Like

Hi @krajjark

Internally, SQLitePlatformAndroid constructs retrieves the bytes from the supplied string using Encoding.UTF8.GetBytes. You can construct a String using Encoding.UTF8.GetString for your source material.

Thanks for your reply,

It is working for normal cases but we have different issues.

I created database using the following byte array as key from old library.
byte[] keyArray = { 0x3D, 0x40, 0xA2, 0xE0, 0xC8, 0x25, 0x2C, 0x7C, 0xAC, 0x93, 0x09, 0xCF, 0x4B, 0x87, 0xCD, 0xAA, 0xED, 0xF7, 0xF9, 0xD4, 0x0D, 0x59, 0x0C, 0xF7, 0x7E, 0x34, 0xA6, 0xC9, 0xF5, 0xB9, 0x62, 0x27, 0x2F, 0x95, 0x4C, 0x27, 0x56, 0x91, 0xE9, 0x45, 0x28, 0xAB, 0x3F, 0x5A, 0x2C, 0x79, 0xB2, 0xEF };

But when I not able to open the database using the new library using the string key created generated by the method Encoding.UTF8.GetString .

After a few attempts, I tried to check the byte array generated from the String is different from the original key.
eg:
var tempkeyArray = Encoding.UTF8.GetBytes(Encoding.UTF8.GetString(keyArray));
Output:
tempkeyArray =
{3D, 0x40, 0xEF, 0xBF, 0xBD, 0xEF, 0xBF, 0xBD, 0xEF, 0xBF, 0xBD, 0x25, 0x2C, 0x7C, 0xEF, 0xBF, 0xBD, 0xEF, 0xBF, 0xBD, 0x09, 0xEF, 0xBF, 0xBD, 0x4B, 0xEF, 0xBF, 0xBD, 0xCD, 0xAA, 0xEF, 0xBF, 0xBD, 0xEF, 0xBF, 0xBD, 0xEF, 0xBF, 0xBD, 0xEF, 0xBF, 0xBD, 0x0D, 0x59, 0x0C, 0xEF, 0xBF, 0xBD, 0x7E, 0x34, 0xEF, 0xBF, 0xBD, 0xEF, 0xBF, 0xBD, 0xEF, 0xBF, 0xBD, 0xEF, 0xBF, 0xBD, 0x62, 0x27, 0x2F, 0xEF, 0xBF, 0xBD, 0x4C, 0x27, 0x56, 0xEF, 0xBF, 0xBD, 0xEF, 0xBF, 0xBD, 0x45, 0x28, 0xEF, 0xBF, 0xBD, 0x3F, 0x5A, 0x2C, 0x79, 0xEF, 0xBF, 0xBD, 0xEF, 0xBF, 0xBD}

All the bolded byte in keyArray is replaced with 0xEF, 0xBF, 0xBD in tempkeyArray.

Since the bytes are different, new API are not able to open the database.

Could you help me to point any api or workaround to handle this issue?

Hi @krajjark

Where did you generate your byte array source material from?

Hi,

We are using the following code to generate the byte array.

var key = new byte[48];
var provider = new System.Security.Cryptography.RNGCryptoServiceProvider();
provider.GetBytes(key);

Regards
Vishnu.

Hi @krajjark

What version does your old library report when you execute the following command:

PRAGMA cipher_version;

Hello @krajjark

Not all bytes are valid UTF-8 byte sequeces, for further reference this may be helpful. To address the scenarios where you have provided keys which will not encode properly to UTF-8, please consider computing the raw keyspec necessary for keying the database. An example below in C# would look like this:

private string GetRawKeySpec(string databasePath, byte[] nonUtf8KeyArray)
{
  using (var stream = new FileStream(databasePath, FileMode.Open, FileAccess.Read))
    {
      int saltSize = 16;
      var salt = new byte[saltSize];
      int didRead = 0;

      while (didRead < saltSize)
        {
          var read = stream.Read(salt, didRead, saltSize - didRead);
          didRead += read;
          if (read == 0) break;
        }

      if (didRead != saltSize)
        throw new ApplicationException("unable to read salt");

      var pbkdf2 = new Rfc2898DeriveBytes(nonUtf8KeyArray, salt, 64000);
      var rawKey = pbkdf2.GetBytes(32);

      var keySpec = string.Format("x'{0}{1}'", BitConverter.ToString(rawKey), BitConverter.ToString(salt)).Replace("-","");
      Console.WriteLine("Key spec:{0}", keySpec);
      return keySpec;
    }
}

Once you generate the keyspec string, you can provide that as the password value to the constructor to your SQLitePlatformAndroid. Once you access the database, you could then rekey the database using properly encoded UTF-8 bytes. Would you give that a try and let us know your results?