My UWP app stores data in encrypted form in local SQLite database on the device. I use Windows.Security.Cryptography.DataProtection
classes for static data and also data streams encryption/decryption (Ref: https://docs.microsoft.com/en-us/windows/uwp/security/cryptography)
I have provided OneDrive data backup facility with the idea that the user can backup entire database to OneDrive from one device and restore it in the app installed on another device. This may help the user use the app on multiple devices and also in case the user acquires a new device.
I use "LOCAL=user"
Descriptor for the DataProtectionProvider
class (Ref: https://docs.microsoft.com/en-us/uwp/api/windows.security.cryptography.dataprotection.dataprotectionprovider)
I was hoping that if I login using my Microsoft Account on two different devices and I encrypt data on one device, then restore data on other then the data should get decrypted; however this is not happening.
I was unable to get any documentation as well (apart from the references listed above). I searched SO as well for MS Support but no luck. Can somebody help me with this?
My requirement: Data encrypted on one (Windows) device should be decrypted in other (Windows) device (when a user is logged in using same Microsoft Account on both the devices).
[UPDATE]
Here's the code sample:
const BinaryStringEncoding encoding = BinaryStringEncoding.Utf8;
const string strDescriptor = "LOCAL=user";
public static async Task<string> ProtectTextAsync(string strClearText)
{
DataProtectionProvider Provider = new DataProtectionProvider(strDescriptor);
IBuffer buffMsg = CryptographicBuffer.ConvertStringToBinary(strClearText, encoding);
IBuffer buffProtected = await Provider.ProtectAsync(buffMsg);
return CryptographicBuffer.EncodeToBase64String(buffProtected);
}
public static async Task<String> UnprotectTextAsync(string strProtected)
{
DataProtectionProvider Provider = new DataProtectionProvider();
IBuffer buffProtected = CryptographicBuffer.DecodeFromBase64String(strProtected);
IBuffer buffUnprotected = await Provider.UnprotectAsync(buffProtected);
String strClearText = CryptographicBuffer.ConvertBinaryToString(encoding, buffUnprotected);
return strClearText;
}
The code is trivial; however, the process of error reproduction is important and is as follows:
I run the App on my Windows 10 Mobile (OS build: 10.0.14393.1770) then Backup data on OneDrive. My mobile shows that I am using a Microsoft Account (say NP3@msft.com) at Settings-->Accounts-->Your Info.
Now, I log-in to my Windows 10 Laptop (OS build: 15063.674 version: 1703 with Fall Creators Update SDK only applied) using NP3@msft.com account when I run the App and Restore the Backup from OneDrive.
Now, when I try to access the data, I get the error in IBuffer buffUnprotected = await Provider.UnprotectAsync(buffProtected);
line of the UnprotectTextAsync
method. The error is:
System.Exception: 'The specified data could not be decrypted. (Excep_FromHResult 0x8009002C)'
Please note that if I restore data backed-up on OneDrive from the same device (Mobile or Laptop), then this code works fine. So Backup/Restore functionality is working correctly with no data modification.
I'll try to keep it brief but there are a few ways you can go about this, first let's talk about the UWP storage
first.
The UWP provides APIs to store user preferences and settings along with data in Three types of storage:
Local
: This is basically storing data as application data in the local storage of the device itself. What can you store here? It's for all sorts of data that can be serialized. It shouldn't be too heavy else it'll throw you an Access Violation Exception
. I once used it to store image as byte streams as well so it provides quite much of flexibility when it comes to storage.PasswordVault
: This is generally to store user credentials across multiple devices so that the use doesn't have to sign in
to your app on every device, if you have the same Microsoft account, it'll log you in right away. You won't need to explicitly encrypt data inside it since the APIs automatically encrypt the data for you while transferring and storing the data across devices.Roaming
: Now this is what you'll be most interested in. Roaming settings are the ones that get transferred across device if you're signed in with the same Microsoft account. The data wouldn't be implicitly encrypted so you might will have to handle the security aspects for it. It's generally used to transfer Settings
for the App and the Preferences
for the user if he/she has something (e.g. Application Theme, Wallpaper). Windows 10 OS
Utilizes this storage to transfer all sorts of stuff when you install windows 10
on another machine you can find a comprehensive list here. It's just amazing.Now that We've had a look at our options, let's try to solution-ate your issue, and how to pick what storage.
Since you have to transfer data over multiple devices, the Local
storage is out of question. Now we have two options left PasswordVault
and RoamingStorage / RoamingSettings
.
The question is, what do you want to transfer (for which you use one drive), is it just a bunch of preferences? or is it file(s) of varied sizes? or is it user credentials?
PasswordVault
is the ideal fit. It'll not only handle DataTransfer
but also provide seamless integrated signIn
across devices if the user is using the same Microsoft Account.RoamingSettings
. They'll transfer the data to the other devices using Microsoft's own APIs
and all you have to do is, fetch them from the RoamingStorage
container and you can start using the data.one drive
and you want to decrypt on other devices below is a solution that I recommend.The approach could be quite simple, if you have files that are stored on one drive
,
When the use logs in to the app for the first time, check if the roamSettings
for that Microsoft account for your app is present or not, since it's not it'll return you a null
. In such a case, create a RoamingStorage
and proceed to step 2.
Create the keys
that'll be needed for encryption. (explained in
detail in the next section below)
Now that you have the keys
, you perform all operations to get the data that needs to be written into the files.
Encrypt the data using the keys
for encryption
and once the data is encrypted, write it into a file and upload it to oneDrive
.
Store the keys
(explained in the next section below) into roaming storage
for that Microsoft Account.
Now when the user signs in to your app using another device, as in point 1
, check if there exists any roamingSettings
. Only this time it won't be null and you'll get the user's roamingSettings
for the app into your other device. extract the stored key
from there and keep it in a variable
.
Download the files from oneDrive
and read their contents as string
.
Use the key
stored in the variable (point 6) to decrypt the data of the file
Now you have the actual data, resume your application flow.
Encryption is out of the scope of this question so I'll just explain a basic joist of it and if any help is needed, please use the comments section.
The above solution would also depend on the type of encryption you're using here, if its
Symmetric
: If you're using symmetric (like AES
), then you generate an encryption key
and an InitializationVector
(also called IV
) on the first device as soon as the user logs in and store them in the RoamingSettings
Asymmetric
: If you're using asymmetric (like RSA
), you'll generate a set of keys publicKey
and privateKey
use the publicKey
to encrypt the data and then store it on one drive
and then store the privateKey
into the roaming settings.
It's not recommended to share private keys over the network incase of Asymmetric encryption but, this is a little unconventional but you're using Microsoft's established APIs to transfer data (which they claim to be secure) so that'll reduce the risk.
Please do let me know if I've skipped out anything.
To be honest, since you mentioned a single key
you're talking about AES-256
. Now if you don't want the developer
to have access to the key, it's kinda a default, you would be using Cryptography APIs
by Microsoft for the AES
. So i in a way you would be calling an API that'll give you the key and you'll be calling another API that'll be encrypting the data. Most Importantly The API would be called at runtime
so either ways the developer has no access to it.
But if your query is that the developer should not even know what kind of encryption and where is being stored then in that case I would recommend you use a Factory Pattern
, where in you abstract
out the implementation of the data that gets encrypted
you just pass in the data, that, class handles all the creating of key, encryption of data and storing of the key to roaming and then returns the encrypted data.
References:
#region AES Encryption
public static async Task<bool> EncryptAesFileAsync(StorageFile fileForEncryption, string aesKey256, string iv16lenght)
{
bool success = false;
try
{
//Initialize key
IBuffer key = Convert.FromBase64String(aesKey256).AsBuffer();
var m_iv = Convert.FromBase64String(iv16lenght).AsBuffer();
SymmetricKeyAlgorithmProvider provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7);
var m_key = provider.CreateSymmetricKey(key);
//secured data
IBuffer data = await FileIO.ReadBufferAsync(fileForEncryption);
IBuffer SecuredData = CryptographicEngine.Encrypt(m_key, data, m_iv);
await FileIO.WriteBufferAsync(fileForEncryption, SecuredData);
success = true;
}
catch (Exception ex)
{
success = false;
DialogHelper.DisplayMessageDebug(ex);
}
return success;
}
public static async Task<bool> DecryptAesFileAsync(StorageFile EncryptedFile, string aesKey256, string iv16lenght)
{
bool success = false;
try
{
//Initialize key
IBuffer key = Convert.FromBase64String(aesKey256).AsBuffer();
var m_iv = Convert.FromBase64String(iv16lenght).AsBuffer();
SymmetricKeyAlgorithmProvider provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7);
var m_key = provider.CreateSymmetricKey(key);
//Unsecured Data
IBuffer data = await FileIO.ReadBufferAsync(EncryptedFile);
IBuffer UnSecuredData = CryptographicEngine.Decrypt(m_key, data, m_iv);
await FileIO.WriteBufferAsync(EncryptedFile, UnSecuredData);
success = true;
}
catch (Exception ex)
{
success = false;
DialogHelper.DisplayMessageDebug(ex);
}
return success;
}
#endregion
User contributions licensed under CC BY-SA 3.0