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

Raymond Chen

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.)

9 comments

Discussion is closed. Login to edit/delete existing comments.

  • Cory Riddell 0

    When you say “on the same machine”, what exactly does machine refer to?

    If the machine is a VM does it matter if the host changes?

    Can I reinstall Windows between encrypting and decrypting? Can I update Windows?

    • Ian Boyd 1

      The machine-wide keys are held in the “System” account.

      As long as you don’t delete and re-create that account you can decrypt the data.

      That includes reinstalling Windows.

      • Cory Riddell 0

        So the keys are in Windows and if I’m running a VM, the keys are with the VM instance, not the host machine. If I start two instances of the same VM image, both will have the same keys, correct?

        How are the keys preserved when reinstalling Widows? If I format my disk and reinstall Windows, why would the System account have the same keys again? Are they deterministically derived from the hardware? Does it require some type of TPM continuity through the machine changes?

        For the “same user on the same machine” scenario, does each user account have a set of keys? If so, that user account in every VM instance would be able to decrypt that data, no? I would guess user keys (if they exist) would not be preserved through a wipe and reinstall process.

        • GL 0

          I think the “that” is “delet[ing] and re-creat[ing] [the ‘System’] account”, so reinstalling Windows will make previously machine-encrypted content undecryptable.

          I’m not sure what “machine” means in the domain setting. In Active Directory, joined workstations have accounts. Does the AD account store the machine keys? If I reuse a computer account when joining a reinstalled workstation, does it grant the rejoined installation ability to decrypt content encrypted before reinstallation?

  • Kevin Doyon 1

    When for example I try to view a password stored in Google Chrome, I get a popup saying “Google Chrome is trying to show passwords. Type your Windows password to allow this.”

    Is there an API that allows decryption to work only if the user provides their password/fingerprint/etc? Similar to how you have to re-enter a password on websites even if you are logged it when doing major things like changing your password

    • Ian Boyd 0

      > Is there an API that allows decryption to work only if the user provides their password/fingerprint/etc?

      That’s what the CryptProtectData/CryptUnprotectData function does.

      It is an API that allows decryption to work only if the user provides their password/fingerprint/etc.

      In fact: that’s how *all* encryption works – you cannot access the encrypted data unless you provide the key (e.g. password, fingerprint, etc)

      • GL 1

        I believe what Doyon meant was to force an on-spot re-authentication. Of course, when the user is logged on, the user must have been authenticated.

        The fact that Google Chrome (or any other sane browser) can fill in a saved password in a website without forcing the user to retype the password implies that the process does not need re-authentication to decrypt — the key is available to any process running under the identity of that user (this is the traditional security model of a desktop system, where the security boundary is user identity, not process). The prompt is purely theatrical.

        • 紅樓鍮 0

          More like the traditional security model of a 70s’ mainframe 🙂

          PS: In principle, a browser can choose to implement password auto-fill using the operating system’s keyring, which could in turn require the user to reauthenticate in order to hand out the stored passwords. In this case, the password prompt would not be purely theatrical (at least not from the browser’s point of view).

          Of course, exactly how password saving is implemented is completely up to the author of the browser’s code, though if the OS keyring is not used then the saved credentials become extremely prone to getting snooped by processes running under the same user identity. (It can still be snooped if other processes can e. g. ptrace the browser process(es).)

          • GL 0

            Sure, it’s possible to make the prompt meaningful (and the implementation is more secure), but that’d be at the pain of having to re-authenticate at every password auto-fill. If the user happens to use face/iris recognition and Ctrl+Alt+Delete is not required to enter a credential, then OK; otherwise I doubt the user base for such a secure system.

            Alas, once the password is filled into the web page, it’s again prone to other processes’ access — unless we revamp how password auto-fill works.

Feedback usabilla icon