3

Some choices for encrypting data so that it can be decrypted only by the same us...

 1 month ago
source link: https://devblogs.microsoft.com/oldnewthing/20240327-00/?p=109580
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Some choices for encrypting data so that it can be decrypted only by the same user or computer

RaymondChen_5in-150x150.jpg

Raymond Chen

March 27th, 20245 2

One of the functions that Windows makes available to you for encrypting data is Crypt­Protect­Data. (Previously.) This function gives you some options for who can decrypt the data:

  • The same user on the same machine.
  • Any user on the same machine.

The resulting binary blob can be saved anywhere, and can later be loaded back into memory by any process running within the same scope¹ and passed to Crypt­Unprotect­Data to recover the original data.

(Here’s some sample code.)

Alternatively, you can use Windows.Security.Cryptography.Data­Protection.Data­Protection­Provider. When encrypting, you construct the Data­Protection­Provider with a string that describes who can decrypt the data.

  • C++/WinRT: auto protector = DataProtectionProvider(scope);
  • C++/WinRT: DataProtectionProvider protector{ scope };
  • C++/CX: auto protector = ref new DataProtectionProvider(scope);
  • C#: var protector = new DataProtectionProvider(scope);
  • JavaScript: var protector = new DataProtectionProvider(scope);

The Data­Protection­Provider gives you a wider choice of scopes than Crypt­Protect­Data.

  • Current user.
  • Current machine.
  • Specific Active Directory user or group. (Requires enterpriseData capability.)
  • Specific Web site password.

Once you’ve created a Data­Protection­Provider, you can use the Protect­Async and Protect­Stream­Async methods to encrypt a binary blob or data stream (respectively). In both cases, the resulting binary blob or stream can be saved anywhere.

To decrypt, create a Data­Protection­Provider using the default (0-parameter) constructor, and then call Unprotect­Async or Unprotect­Stream­Async to recover the original data.

(Here’s some sample code.)

Really, the Data­Protection­Provider is two different features glued together. First is encryption, which requires a scope. Second is decryption, which rejects a scope. Clearer would have to been to keep the separate functionality in separate objects.

runtimeclass DataProtectionEncryptionProvider
{
    DataProtectionEncryptionProvider(String scope);

    IAsyncOperation<IBuffer> ProtectAsync(IBuffer data);
    IAsyncAction ProtectStreamAsync(IInputStream src, IOutputStream dest);
}

runtimeclass DataProtectionDecryptionProvider
{
    DataProtectionDecryptionProvider();

    IAsyncOperation<IBuffer> UnprotectAsync(IBuffer data);
    IAsyncAction UnprotectStreamAsync(IInputStream src, IOutputStream dest);
}

With this alternate design, you can’t accidentally try to encrypt with an unscoped provider, and it removes the confusion over whether the scope you use for decryption needs to match the one that was used for encryption.

But really, the decryption provider is stateless, so the decryption methods can just be static, and that also avoids the confusion.

runtimeclass DataProtectionProvider
{
    DataProtectionProvider(String scope);

    IAsyncOperation<IBuffer> ProtectAsync(IBuffer data);
    IAsyncAction ProtectStreamAsync(IInputStream src, IOutputStream dest);

    static IAsyncOperation<IBuffer> UnprotectAsync(IBuffer data);
    static UnprotectStreamAsync(IInputStream src, IOutputStream dest);
}

Bonus chatter: The Data­Protection­Provider is just a wrapper around the NCrypt­Protect­Secret and NCrypt­Unprotect­Secret functions (for buffers) and te NCrypt­Stream­Open­To­Protect and NCrypt­Stream­Open­To­Unprotect functions (for streams).

Bonus bonus chatter: Crypt­Protect­Data and Data­Protection­Provider are not interoperable. Data encrypted by one cannot be decrypted by the other.

¹ Sometimes people ask whether the memory must be decrypted from the same buffer returned by Crypt­Protect­Data. No, it doesn’t. You can save the bytes to a file or copy them to another buffer. Just pass those same bytes back when you’re ready to decrypt. (After all, if the memory must be decrypted from literally the same buffer, that would prevent cross-process decryption and render moot all of the discussion in the documentation about roaming users, since roaming users are decrypting from another computer entirely.)


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK