Windows Information Protection (WIP) developer guide (C++)
An enlightened app differentiates between corporate and personal data and knows which to protect based on Windows Information Protection (WIP) policies defined by the administrator.
In this guide, we'll show you how to build one. When you're done, policy administrators will be able to trust your app to consume their organization's data. And employees will love that you've kept their personal data intact on their device even if they un-enroll from the organization's mobile device management (MDM) or leave the organization entirely.
You'll do these things to enlighten your app:
- Gather what you need
- Setup your development environment
- Determine whether to use WIP APIs in your app
- Read enterprise data
- Protect enterprise data
- Handle enterprise data when protected content is revoked
- Indicate that your app is enlightened
Note
This guide helps you enlighten a C++ Windows desktop app. If you want to enlighten a UWP app, see Windows Information Protection developer guide (UWP).
You can read more about WIP and enlightened apps here: Windows Information Protection (WIP).
If you're ready, let's start.
Gather what you need
You'll need these:
- A test Virtual Machine (VM) that runs Windows 10, version 1607. You'll debug your app against this test VM.
- A development computer that runs Windows 10, version 1607. This could be your test VM if you have Visual Studio installed on it.
Setup your development environment
You'll do these things:
- Install the WIP Setup Developer Assistant onto your test VM
- Create a protection policy by using the WIP Setup Developer Assistant.
- Setup a Visual Studio project (C++ / CX only).
- Setup remote debugging.
- Include the right header files.
- Add the right namespaces.
Install the WIP Setup Developer Assistant onto your test VM
Use this tool to setup a Windows Information Protection policy on your test VM.
Download the tool here: WIP Setup Developer Assistant.
Create a protection policy by using the WIP Setup Developer Assistant
Define your policy by adding information to each section in the WIP setup developer assistant. Choose the help icon next to any setting to learn more about how to use it.
For more general guidance about how to use this tool, see the Version notes section on the app download page.
Setup a Visual Studio project (C++/CX only)
If you plan to use C++/CX, you'll have to configure a few things in your project. If you're planing to use standard C++, you can skip this section.
On your development computer, open your project.
Open the properties pages for your project.
In the General settings of the C/C++ settings group, set the Consume Windows Runtime Extension setting to Yes(/ZW).
In the Code Generation settings of the C/C++ settings group, set the Enable Minimal Rebuild setting to No(/GM-).
Add the path to the Windows.winmd and Platform.winmd files to the Additional #using Directories location.
By default, you'll find the Windows.winmd file in the C:\Program Files (x86)\Windows Kits\10\UnionMetadata directory and the Platform.winmd file in the C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcpackages directory.
Decorate the main function of your app with an attribute that indicates which threading apartment model you're using.
Single-threaded apartment model
[Platform::STAThread] int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
Multi-threaded apartment model
[Platform::MTAThread] int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
Setup remote debugging
Install Visual Studio Remote Tools on your test VM only if you are developing your app on a computer other than your VM. Then, on your development computer start the remote debugger and see if your app runs on the target computer.
Include the right header files
C++ projects
#include <string>
#include "ppltasks.h"
#include "stdafx.h"
#include <cstdlib>
#include <stdio.h>
#include <efswrtinterop.h>
#include <Windows.Foundation.h>
#include <Windows.Security.EnterpriseData.h>
#include <windows.web.h>
#include <windows.web.http.h>
#include <windows.web.http.filters.h>
#include <windows.web.http.headers.h>
#include <Windows.Foundation.Metadata.h>
#include <windows.security.cryptography.h>
#include <Windows.System.Threading.h>
#include <windows.ui.h>
#include <windows.ui.xaml.h>
#include <wrl\wrappers\corewrappers.h>
#include <wrl\client.h>
#include <wrl\event.h>
C++/CX projects
#include "stdafx.h"
#include "ppltasks.h"
#include <string>
#include <stdio.h>
#include <cstdlib>
#include <new>
Add the right namespaces
C++ projects
using namespace Concurrency;
using namespace ABI::Windows::ApplicationModel::Activation;
using namespace ABI::Windows::ApplicationModel::DataTransfer;
using namespace ABI::Windows::Data::Xml::Dom;
using namespace ABI::Windows::Devices::Enumeration;
using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::Foundation::Metadata;
using namespace ABI::Windows::Networking;
using namespace ABI::Windows::Security::Cryptography;
using namespace ABI::Windows::Security::EnterpriseData;
using namespace ABI::Windows::System::Threading;
using namespace ABI::Windows::Storage;
using namespace ABI::Windows::Web::Http;
using namespace ABI::Windows::Web::Http::Filters;
using namespace ABI::Windows::Web::Http::Headers;
using namespace ABI::Windows::Storage::Streams;
using namespace ABI::Windows::UI::Xaml;
using namespace ABI::Windows::UI::Xaml::Controls;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
C++/ CX projects
using namespace Concurrency;
using namespace Windows;
using namespace Windows::ApplicationModel::Activation;
using namespace Windows::ApplicationModel::DataTransfer;
using namespace Windows::Data::Xml::Dom;
using namespace Windows::Devices::Enumeration;
using namespace Windows::Foundation;
using namespace Windows::Networking;
using namespace Windows::Security::Cryptography;
using namespace Windows::Security::EnterpriseData;
using namespace Windows::Storage;
using namespace Windows::Storage::Streams;
using namespace Windows::System::Threading;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml;
using namespace Windows::Web::Http;
using namespace Windows::Web::Http::Headers;
using namespace Windows::Web::Http::Filters;
using namespace Windows::Web::Http::Headers;
Determine whether to use WIP APIs in your app
Ensure that the operating system that runs your app supports WIP and that WIP is enabled on the device.
C++ snippet
RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
ComPtr<IApiInformationStatics> apiStatics;
Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Foundation_Metadata_ApiInformation).Get(), &apiStatics);
boolean operating_System_Support;
HString contractName;
contractName.Set(L"Windows.Security.EnterpriseData.EnterpriseDataContract");
apiStatics->IsApiContractPresentByMajor(contractName.Get(), 3, &operating_System_Support);
ComPtr<IProtectionPolicyManagerStatics2> ppmStatics;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_ProtectionPolicyManager).Get(), &ppmStatics);
boolean WIP_Enabled_On_Device;
ppmStatics->get_IsProtectionEnabled(&WIP_Enabled_On_Device);
if (operating_System_Support && WIP_Enabled_On_Device
{
// WIP is supported on the platform
}
else
{
// WIP is not supported on the platform
}
C++/CX snippet
bool use_WIP_APIs = false;
if ((Windows::Foundation::Metadata::ApiInformation::IsApiContractPresent("Windows.Security.EnterpriseData.EnterpriseDataContract", 3) && ProtectionPolicyManager::IsProtectionEnabled))
{
use_WIP_APIs = true;
}
else
{
use_WIP_APIs = false;
}
Don't call WIP APIs if the operating system doesn't support WIP or if WIP is not enabled on the device.
Read enterprise data
To read protected files, network endpoints, clipboard data and data that you accept from a Share contract, your app will have to request access.
Windows Information Protection gives your app permission if your app is on the protection policy's allowed list.
To read data from any of those sources, your app will have to verify that the enterprise ID is managed by policy.
You'll do these things:
- Read data from a file
- Read data from a network endpoint
- Read data from the clipboard
- Read data that is dragged into your application
Let's start with files.
Read data from a file
File, network endpoints, and clipboard data all have an enterprise ID.
To read data from any of those sources, your app will have to verify that the enterprise ID is managed by policy.
Let's start with files.
STEP 1: GET THE FILE HANDLE
C++ snippet
int WIP::GetFileHandle(HSTRING fileName, IStorageFile **file)
{
RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
HString hstrKnownFolders;
hstrKnownFolders.Set(RuntimeClass_Windows_Storage_KnownFolders);
ComPtr<IActivationFactory> pKnownFoldersActivationFactory;
Windows::Foundation::GetActivationFactory(hstrKnownFolders.Get(), &pKnownFoldersActivationFactory);
ComPtr<IKnownFoldersStatics> pKnownFolders;
pKnownFoldersActivationFactory.As(&pKnownFolders);
ComPtr<IStorageFolder> pStorageFolder;
pKnownFolders->get_DocumentsLibrary(&pStorageFolder);
__FIAsyncOperation_1_Windows__CStorage__CStorageFile_t* operation;
pStorageFolder->GetFileAsync(fileName, &operation);
Event operationCompleted(CreateEvent(nullptr, true, false, nullptr));
operation->put_Completed(Callback<IAsyncOperationCompletedHandler<StorageFile*>>
([&](IAsyncOperation<StorageFile*> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(operationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(operationCompleted.Get(), INFINITE);
HRESULT hr = operation->GetResults(file);
return 0;
}
C++/CX snippet
Windows::Storage::StorageFolder ^storageFolder = KnownFolders::DocumentsLibrary;
auto getFileTask = create_task(storageFolder->GetFileAsync(fileName));
STEP 2: DETERMINE WHETHER YOUR APP CAN OPEN THE FILE
Determine whether the file is protected. If it is, your app can open that file if these two things are true:
- The file's identity is managed by policy.
- Your app is on the allowed list of that policy.
If either of these conditions aren't true, ProtectionPolicyManager.IsIdentityManaged returns false and you can't open that file.
C++ snippet
int WIP::GetProtectionInfo(HSTRING identity, IStorageFile * file, bool **canRead)
{
RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
ComPtr<IFileProtectionManagerStatics> fpmStatics;
Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_FileProtectionManager).Get(), &fpmStatics);
__FIAsyncOperation_1_Windows__CSecurity__CEnterpriseData__CFileProtectionInfo_t *operation;
ComPtr<IStorageItem> storageItem;
ComPtr<IStorageFile> x(file);
x.As(&storageItem);
fpmStatics->GetProtectionInfoAsync(storageItem.Get(), &operation);
Event operationCompleted(CreateEvent(nullptr, true, false, nullptr));
operation->put_Completed(Callback<IAsyncOperationCompletedHandler<FileProtectionInfo*>>
([&](IAsyncOperation<FileProtectionInfo*> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(operationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(operationCompleted.Get(), INFINITE);
IFileProtectionInfo * fileProtectionInfo;
FileProtectionStatus fileProtectionStatus;
operation->GetResults(&fileProtectionInfo);
fileProtectionInfo->get_Status(&fileProtectionStatus);
bool readable = true;
if ((fileProtectionStatus != FileProtectionStatus::FileProtectionStatus_Protected)
&& fileProtectionStatus != FileProtectionStatus::FileProtectionStatus_Unprotected)
{
ComPtr<IProtectionPolicyManagerStatics> ppmStatics;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_ProtectionPolicyManager).Get(), &ppmStatics);
readable = false;
}
*canRead = &readable;
return 0;
}
C++/CX snippet
getFileTask.then([](StorageFile ^file)
{
auto getProtectionInfoTask = create_task(FileProtectionManager::GetProtectionInfoAsync(file));
getProtectionInfoTask.then([file](FileProtectionInfo ^protectionInfo)
{
if (protectionInfo->Status != FileProtectionStatus::Protected &&
(protectionInfo->Status != FileProtectionStatus::Unprotected))
{
// Can't read file. Inform the user, log a message, perform some other action.
}
else
{
// Next snippet goes here.
}
});
});
APIs:
FileProtectionManager.GetProtectionInfoAsync
FileProtectionInfo
FileProtectionStatus
ProtectionPolicyManager.IsIdentityManaged
STEP 3: READ THE FILE INTO A STREAM OR BUFFER
Read the file into a stream
C++ snippet
int WIP::ReadFileIntoStream(IStorageFile * file)
{
RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
__FIAsyncOperation_1_Windows__CStorage__CStreams__CIRandomAccessStream_t *operation;
file->OpenAsync(FileAccessMode::FileAccessMode_ReadWrite, &operation);
Event operationCompleted(CreateEvent(nullptr, true, false, nullptr));
operation->put_Completed(Callback<IAsyncOperationCompletedHandler<IRandomAccessStream*>>
([&](IAsyncOperation<IRandomAccessStream*> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(operationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(operationCompleted.Get(), INFINITE);
IRandomAccessStream * pRandomAccessStream;
operation->GetResults(&pRandomAccessStream);
// Do something with the stream here.
return 0;
}
C++/CX snippet
auto fileStreamTask = create_task(file->OpenAsync(Windows::Storage::FileAccessMode::ReadWrite))
fileStreamTask.then([](auto stream)
{
// Do something with the file stream.
});
Read the file into a buffer
C++ snippet
int WIP::GetFileHandle(HSTRING fileName, IStorageFile **file)
{
RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
HString hstrKnownFolders;
hstrKnownFolders.Set(RuntimeClass_Windows_Storage_KnownFolders);
ComPtr<IActivationFactory> pKnownFoldersActivationFactory;
Windows::Foundation::GetActivationFactory(hstrKnownFolders.Get(), &pKnownFoldersActivationFactory);
ComPtr<IKnownFoldersStatics> pKnownFolders;
pKnownFoldersActivationFactory.As(&pKnownFolders);
ComPtr<IStorageFolder> pStorageFolder;
pKnownFolders->get_DocumentsLibrary(&pStorageFolder);
__FIAsyncOperation_1_Windows__CStorage__CStorageFile_t* operation;
pStorageFolder->GetFileAsync(fileName, &operation);
Event operationCompleted(CreateEvent(nullptr, true, false, nullptr));
operation->put_Completed(Callback<IAsyncOperationCompletedHandler<StorageFile*>>
([&](IAsyncOperation<StorageFile*> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(operationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(operationCompleted.Get(), INFINITE);
operation->GetResults(file);
return 0;
}
C++/CX snippet
auto fileBufferTask = create_task(Windows::Storage::FileIO::ReadBufferAsync(file));
fileBufferTask.then([&](auto buffer)
{
// do whatever with the buffer.
});
Read data from a network endpoint
Create a protected thread context to read from an enterprise endpoint.
STEP 1: GET THE IDENTITY OF THE NETWORK ENDPOINT
C++ snippet
RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
// --------------
// Get URI object.
// --------------
ComPtr<IUriRuntimeClassFactory> uriFactory;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Foundation_Uri).Get(), &uriFactory);
ComPtr<IUriRuntimeClass> uriRuntime;
uriFactory->CreateUri(resourceURI, &uriRuntime);
// --------------
// Get host name.
// --------------
HSTRING hostNameString;
uriRuntime->get_Host(&hostNameString);
ComPtr<IHostNameFactory> hostNameFactory;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Networking_HostName).Get(), &hostNameFactory);
ComPtr<IHostName> hostName;
hostNameFactory->CreateHostName(hostNameString, &hostName);
// --------------------------------
// Get identity of network endpoint
// --------------------------------
ComPtr<IProtectionPolicyManagerStatics> ppmStatics;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_ProtectionPolicyManager).Get(), &ppmStatics);
__FIAsyncOperation_1_HSTRING_t * result;
ppmStatics->GetPrimaryManagedIdentityForNetworkEndpointAsync(hostName.Get(), &result);
Event operationCompleted(CreateEvent(nullptr, true, false, nullptr));
result->put_Completed(Callback<IAsyncOperationCompletedHandler<HSTRING>>
([&](IAsyncOperation<HSTRING> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(operationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(operationCompleted.Get(), INFINITE);
C++/CX snippet
Windows::Networking::HostName ^hostName = ref new Windows::Networking::HostName(resourceURI->Host);
auto getNetworkEndPointTask = create_task
(ProtectionPolicyManager::GetPrimaryManagedIdentityForNetworkEndpointAsync(hostName));
If the endpoint isn't managed by policy, you'll get back an empty string.
APIs:
STEP 2: CREATE A PROTECTED THREAD CONTEXT
If the endpoint is managed by policy, create a protected thread context. This tags any network connections that you make on the same thread to the identity.
It also gives you access to enterprise network resources that are managed by that policy.
C++ snippet
// --------------------------
// Read from network endpoint
// --------------------------
HSTRING identity;
result->GetResults(&identity);
if (identity != NULL)
{
IThreadNetworkContext * result;
ppmStatics->CreateCurrentThreadNetworkContext(identity, &result);
return GetDataFromNetworkHelperMethod(uriRuntime);
}
else
{
return GetDataFromNetworkHelperMethod(uriRuntime);
}
C++/CX snippet
getNetworkEndPointTask.then([resourceURI](Platform::String ^identity)
{
if ((identity) && !(identity->IsEmpty()))
{
{
ThreadNetworkContext ^threadNetworkContext =
ProtectionPolicyManager::CreateCurrentThreadNetworkContext(identity);
GetDataFromRedirectHelperMethod(resourceURI);
}
}
else
{
GetDataFromRedirectHelperMethod(resourceURI);
}
});
This example encloses socket calls in a {}
block. If you don't do this, make sure that you close the thread context after you've retrieved your resource. See ThreadNetworkContext::Close.
Don't create any personal files on that protected thread because those files will be automatically encrypted.
The ProtectionPolicyManager.CreateCurrentThreadNetworkContext method returns a ThreadNetworkContext object whether or not the endpoint is being managed by policy. If your app handles both personal and enterprise resources, call ProtectionPolicyManager.CreateCurrentThreadNetworkContext for all identities. After you get the resource, dispose the ThreadNetworkContext to clear any identity tag from the current thread.
APIs:
STEP 3: READ THE RESOURCE INTO A BUFFER
C++ snippet
int WIP::GetDataFromNetworkHelperMethod(ComPtr<IUriRuntimeClass> uriRuntime)
{
//----------------------
// Get HttpClient object
//----------------------
ComPtr<IHttpClientFactory> httpClientFactory;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Web_Http_HttpClient).Get(), &httpClientFactory);
ComPtr<IHttpFilter> filter;
RoActivateInstance
(HStringReference(RuntimeClass_Windows_Web_Http_Filters_HttpBaseProtocolFilter).Get(), &filter);
ComPtr<IHttpClient> httpClient;
httpClientFactory->Create(filter.Get(), &httpClient);
//--------------------------------
// Read network file into a buffer
//--------------------------------
__FIAsyncOperationWithProgress_2_Windows__CStorage__CStreams__CIBuffer_Windows__CWeb__CHttp__CHttpProgress_t *operation;
HRESULT hr = httpClient->GetBufferAsync(uriRuntime.Get(), &operation);
Event operationCompleted(CreateEvent(nullptr, true, false, nullptr));
operation->put_Completed(Callback<IAsyncOperationWithProgressCompletedHandler<IBuffer*, HttpProgress>>
([&](IAsyncOperationWithProgress<IBuffer*, HttpProgress> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(operationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(operationCompleted.Get(), INFINITE);
IBuffer * pBuffer;
operation->GetResults(&pBuffer);
return 0;
}
C++/CX snippet
void GetDataFromNetworkHelperMethod(Uri ^ resourceURI)
{
HttpClient ^client = nullptr;
client = ref new HttpClient();
try
{
auto getBufferTask = create_task(
client->GetBufferAsync(resourceURI));
getBufferTask.then([](IBuffer ^data)
{
// Add code to work with the buffer here.
});
}
catch (Platform::Exception ^ exception) {}
}
(Optional) USE A HEADER TOKEN INSTEAD OF CREATING A PROTECTED THREAD CONTEXT
Instead of creating a protected thread context, you can provide the enterprise ID by using an HTTP request header.
C++ snippet
...
if (identity != NULL)
{
IHttpRequestHeaderCollection * headerCollection;
httpClient->get_DefaultRequestHeaders(&headerCollection);
headerCollection->Append(HStringReference(L"X-MS-Windows-HttpClient-EnterpriseId").Get(), identity);
return GetDataFromNetworkbyUsingHeaderHelperMethod(httpClient, uriRuntime);
}
...
int WIP::GetDataFromNetworkbyUsingHeaderHelperMethod(ComPtr<IHttpClient> httpClient, ComPtr<IUriRuntimeClass> uriRuntime)
{
//--------------------------------
// Read network file into a buffer
//--------------------------------
__FIAsyncOperationWithProgress_2_Windows__CStorage__CStreams__CIBuffer_Windows__CWeb__CHttp__CHttpProgress_t *operation;
httpClient->GetBufferAsync(uriRuntime.Get(), &operation);
Event operationCompleted(CreateEvent(nullptr, true, false, nullptr));
operation->put_Completed(Callback<IAsyncOperationWithProgressCompletedHandler<IBuffer*, HttpProgress>>
([&](IAsyncOperationWithProgress<IBuffer*, HttpProgress> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(operationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(operationCompleted.Get(), INFINITE);
IBuffer * pBuffer;
operation->GetResults(&pBuffer);
return 0;
}
C++ / CX snippet
...
if ((identity) && !(identity->IsEmpty()))
{
client = ref new HttpClient();
HttpRequestHeaderCollection ^ headerCollection =
client->DefaultRequestHeaders;
headerCollection->Append("X-MS-Windows-HttpClient-EnterpriseId", identity);
GetDataFromNetworkbyUsingHeaderHelperMethod(client, resourceURI);
}
...
void GetDataFromNetworkbyUsingHeaderHelperMethod(HttpClient ^ client, Uri ^resourceURI)
{
try
{
auto getBufferTask = create_task(
client->GetBufferAsync(resourceURI));
getBufferTask.then([resourceURI](IBuffer ^data)
{
// Add code to work with the buffer here.
});
}
catch (Platform::Exception ^ exception) { }
}
HANDLE PAGE REDIRECTS
Sometimes a web server will redirect traffic to a more current version of a resource.
To handle this, make requests until the response status of your request has a value of OK.
Then use the URI of that response to get the identity of the endpoint. Here's one way to do this:
C++ snippet
int WIP::ReadNetworkResourceWithRedirects(HSTRING resourceURL, HWND hWnd)
{
RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
bool finalURL = false;
HttpResponseMessage * response = nullptr;
ComPtr<IUriRuntimeClassFactory> uriFactory;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Foundation_Uri).Get(), &uriFactory);
ComPtr<IUriRuntimeClass> uriRuntime;
uriFactory->CreateUri(resourceURL, &uriRuntime);
while (!finalURL)
{
ComPtr<IHostNameFactory> hostNameFactory;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Networking_HostName).Get(), &hostNameFactory);
ComPtr<IHostName> hostName;
HSTRING hostNameString;
uriRuntime->get_Host(&hostNameString);
hostNameFactory->CreateHostName(hostNameString, &hostName);
ComPtr<IProtectionPolicyManagerStatics> ppmStatics;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_ProtectionPolicyManager).Get(), &ppmStatics);
__FIAsyncOperation_1_HSTRING_t * result;
ppmStatics->GetPrimaryManagedIdentityForNetworkEndpointAsync(hostName.Get(), &result);
Event operationCompleted(CreateEvent(nullptr, true, false, nullptr));
result->put_Completed(Callback<IAsyncOperationCompletedHandler<HSTRING>>
([&](IAsyncOperation<HSTRING> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(operationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(operationCompleted.Get(), INFINITE);
ComPtr<IProtectionPolicyManagerInterop> ppmInterop;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_ProtectionPolicyManager).Get(), &ppmInterop);
ComPtr<IProtectionPolicyManager> ppmInstance;
ppmInterop->GetForWindow(hWnd, IID_PPV_ARGS(&ppmInstance));
ComPtr<IHttpClientFactory> httpClientFactory;
HSTRING identity;
result->GetResults(&identity);
if (identity == NULL)
{
ppmInstance->put_Identity(NULL);
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Web_Http_HttpClient).Get(), &httpClientFactory);
}
else
{
ppmInstance->put_Identity(identity);
IThreadNetworkContext * ThreadNetworkContextResult;
ppmStatics->CreateCurrentThreadNetworkContext(identity, &ThreadNetworkContextResult);
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Web_Http_HttpClient).Get(), &httpClientFactory);
}
ComPtr<IHttpBaseProtocolFilter> baseProtocolFilter;
RoActivateInstance
(HStringReference(RuntimeClass_Windows_Web_Http_Filters_HttpBaseProtocolFilter).Get(), &baseProtocolFilter);
ComPtr<IHttpFilter> filter;
baseProtocolFilter.As(&filter);
ComPtr<IHttpClient> httpClient;
httpClientFactory->Create(filter.Get(), &httpClient);
ComPtr<IHttpRequestMessageFactory> requestMessageFactory;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Web_Http_HttpRequestMessage).Get(), &requestMessageFactory);
ComPtr<IHttpMethodFactory> requestMethodFactory;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Web_Http_HttpMethod).Get(), &requestMethodFactory);
ComPtr<IHttpMethod> httpMethod;
HString str;
str.Set(L"GET");
requestMethodFactory->Create(str.Get(), &httpMethod);
IHttpRequestMessage * requestMessage;
requestMessageFactory->Create(httpMethod.Get(), uriRuntime.Get(), &requestMessage);
__FIAsyncOperationWithProgress_2_Windows__CWeb__CHttp__CHttpResponseMessage_Windows__CWeb__CHttp__CHttpProgress_t * requestOperation;
httpClient->SendRequestAsync(requestMessage, &requestOperation);
Event requestOperationCompleted(CreateEvent(nullptr, true, false, nullptr));
requestOperation->put_Completed(Callback<IAsyncOperationWithProgressCompletedHandler<HttpResponseMessage*, HttpProgress>>
([&](IAsyncOperationWithProgress<HttpResponseMessage*, HttpProgress> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(requestOperationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(requestOperationCompleted.Get(), INFINITE);
IHttpResponseMessage * response;
requestOperation->GetResults(&response);
HttpStatusCode value;
response->get_StatusCode(&value);
if (value == HttpStatusCode::HttpStatusCode_MultipleChoices &&
value == HttpStatusCode::HttpStatusCode_MovedPermanently &&
value == HttpStatusCode::HttpStatusCode_Found &&
value == HttpStatusCode::HttpStatusCode_SeeOther &&
value == HttpStatusCode::HttpStatusCode_NotModified &&
value == HttpStatusCode::HttpStatusCode_UseProxy &&
value == HttpStatusCode::HttpStatusCode_TemporaryRedirect &&
value == HttpStatusCode::HttpStatusCode_PermanentRedirect)
{
requestMessage->get_RequestUri(&uriRuntime);
}
else
{
finalURL = true;
}
}
return 0;
}
C++/CX snippet
void GetDataFromRedirectHelperMethod(Uri ^resourceURI)
{
HttpClient ^client = nullptr;
HttpBaseProtocolFilter ^filter = ref new HttpBaseProtocolFilter();
filter->AllowAutoRedirect = false;
HttpResponseMessage ^response = nullptr;
client = ref new HttpClient(filter);
HttpRequestMessage ^message = ref new HttpRequestMessage(HttpMethod::Get, resourceURI);
auto sendAsyncRequestTask = create_task(client->SendRequestAsync(message));
sendAsyncRequestTask.then([resourceURI, client](Windows::Web::Http::HttpResponseMessage ^response)
{
if (response->StatusCode == HttpStatusCode::MultipleChoices ||
response->StatusCode == HttpStatusCode::MovedPermanently ||
response->StatusCode == HttpStatusCode::Found ||
response->StatusCode == HttpStatusCode::SeeOther ||
response->StatusCode == HttpStatusCode::NotModified ||
response->StatusCode == HttpStatusCode::UseProxy ||
response->StatusCode == HttpStatusCode::TemporaryRedirect ||
response->StatusCode == HttpStatusCode::PermanentRedirect)
{
HttpRequestMessage ^message = ref new HttpRequestMessage(HttpMethod::Get, message->RequestUri);
auto sendAsyncRequestTask = create_task(client->SendRequestAsync(message));
sendAsyncRequestTask.then([&](Windows::Web::Http::HttpResponseMessage ^response)
{
auto readBufferTask = create_task(response->Content->ReadAsBufferAsync());
readBufferTask.then([&](IBuffer ^data)
{
// Add code to work with the buffer here.
});
});
}
else
{
auto readBufferTask = create_task(response->Content->ReadAsBufferAsync());
readBufferTask.then([&](IBuffer ^data)
{
// Add code to work with the buffer here.
});
}
});
}
APIs:
ProtectionPolicyManager.GetPrimaryManagedIdentityForNetworkEndpointAsync
ProtectionPolicyManager.CreateCurrentThreadNetworkContext
ProtectionPolicyManager.Identity
CONFIGURE YOUR APP TO RUN IN PERMISSIVE MODE
Permissive mode eliminates the need for your app to call the ProtectionPolicyManager.GetPrimaryManagedIdentityForNetworkEndpointAsync or the ProtectionPolicyManager.CreateCurrentThreadNetworkContext method. In certain situations, this can make it easier for you to debug your app. You can also publish an app that runs in permissive mode if your app does not show users data retrieved from network endpoints.
To configure your app to run in permissive mode, open the resource file of your project and add the following flag.
MICROSOFTEDPPERMISSIVEAPPINFO EDPPERMISSIVEAPPINFOID
BEGIN
0X0001
END
Read data from the clipboard
GET PERMISSION TO USE DATA FROM THE CLIPBOARD
To get data from the clipboard, ask Windows for permission. Use DataPackageView.RequestAccessAsync to do that.
C++ snippet
int WIP::PasteText(void)
{
ComPtr<IClipboardStatics> pClipboardStatics;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_ApplicationModel_DataTransfer_Clipboard).Get(), &pClipboardStatics);
ComPtr<IDataPackageView> pDataPackageView;
pClipboardStatics->GetContent(&pDataPackageView);
boolean containsText = false;
pDataPackageView->Contains(HStringReference(L"Text").Get(), &containsText);
if (containsText)
{
ComPtr<IDataPackageView3> pDataPackageView3;
pDataPackageView.As(&pDataPackageView3);
__FIAsyncOperation_1_Windows__CSecurity__CEnterpriseData__CProtectionPolicyEvaluationResult_t * protectionPolicyOperation;
pDataPackageView3->RequestAccessAsync(&protectionPolicyOperation);
Event operationCompleted(CreateEvent(nullptr, true, false, nullptr));
protectionPolicyOperation->put_Completed(Callback<IAsyncOperationCompletedHandler<ProtectionPolicyEvaluationResult>>
([&](IAsyncOperation<ProtectionPolicyEvaluationResult> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(operationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(operationCompleted.Get(), INFINITE);
ProtectionPolicyEvaluationResult result;
protectionPolicyOperation->GetResults(&result);
if (result == ProtectionPolicyEvaluationResult::ProtectionPolicyEvaluationResult_Allowed)
{
__FIAsyncOperation_1_HSTRING_t * stringOperation;
Event stringOperationCompleted(CreateEvent(nullptr, true, false, nullptr));
stringOperation->put_Completed(Callback<IAsyncOperationCompletedHandler<HSTRING>>
([&](IAsyncOperation<HSTRING> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(stringOperationCompleted.Get());
return S_OK;
}).Get());
pDataPackageView->GetTextAsync(&stringOperation);
WaitForSingleObject(stringOperationCompleted.Get(), INFINITE);
HSTRING clipBoardText;
stringOperation->GetResults(&clipBoardText);
}
}
return 0;
}
C++/ CX snippet
void PasteText(TextBox ^textBox)
{
DataPackageView ^dataPackageView = Clipboard::GetContent();
if (dataPackageView->Contains(StandardDataFormats::Text))
{
auto requestAccessTask = create_task(dataPackageView->RequestAccessAsync());
requestAccessTask.then([dataPackageView, textBox](ProtectionPolicyEvaluationResult result)
{
if (result == ProtectionPolicyEvaluationResult::Allowed)
{
auto getTextTask = create_task(dataPackageView->GetTextAsync());
getTextTask.then([textBox](Platform::String ^contentsOfClipboard)
{
textBox->Text = contentsOfClipboard;
});
}
});
}
}
APIs:
HIDE OR DISABLE FEATURES THAT USE CLIPBOARD DATA
Determine whether your current view has permission to get data that is on the clipboard.
If it doesn't, you can disable or hide controls that let users paste information from the clipboard or preview its contents.
C++ snippet
int WIP::IsClipboardAllowedAsync(ProtectionPolicyEvaluationResult **evalResult, HWND hWnd)
{
ComPtr<IClipboardStatics> pClipboardStatics;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_ApplicationModel_DataTransfer_Clipboard).Get(), &pClipboardStatics);
ComPtr<IDataPackageView> pDataPackageView;
HRESULT hr = pClipboardStatics->GetContent(&pDataPackageView);
boolean containsText = false;
hr = pDataPackageView->Contains(HStringReference(L"Text").Get(), &containsText);
if (containsText)
{
ComPtr<IProtectionPolicyManagerStatics> ppmStatics;
hr = ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_ProtectionPolicyManager).Get(), &ppmStatics);
ComPtr<IDataPackagePropertySetView> dataPackagePropertySetView;
hr = pDataPackageView->get_Properties(&dataPackagePropertySetView);
ComPtr<IDataPackagePropertySetView3> dataPackagePropertySetView3;
dataPackagePropertySetView.As(&dataPackagePropertySetView3);
ComPtr<IProtectionPolicyManagerInterop> ppmInterop;
hr = ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_ProtectionPolicyManager).Get(), &ppmInterop);
ComPtr<IProtectionPolicyManager> ppmInstance;
HSTRING identity;
if (SUCCEEDED(ppmInterop->GetForWindow(hWnd, IID_PPV_ARGS(&ppmInstance))))
{
HRESULT hRet = ppmInstance->get_Identity(&identity);
}
ProtectionPolicyEvaluationResult evaluationResult;
HSTRING enterpriseID;
dataPackagePropertySetView3->get_EnterpriseId(&enterpriseID);
hr = ppmStatics->CheckAccess(enterpriseID, identity, &evaluationResult);
*evalResult = &evaluationResult;
}
return 0;
}
C++/ CX snippet
bool IsClipboardAllowedAsync(Platform::String ^identity)
{
ProtectionPolicyEvaluationResult protectionPolicyEvaluationResult = ProtectionPolicyEvaluationResult::Blocked;
DataPackageView ^dataPackageView = Clipboard::GetContent();
if (dataPackageView->Contains(StandardDataFormats::Text))
{
protectionPolicyEvaluationResult = ProtectionPolicyManager::CheckAccess
(dataPackageView->Properties->EnterpriseId, identity);
}
return (protectionPolicyEvaluationResult == ProtectionPolicyEvaluationResult::Allowed |
protectionPolicyEvaluationResult == ProtectionPolicyEvaluationResult::ConsentRequired);
}
APIs:
PREVENT USERS FROM BEING PROMPTED WITH A CONSENT DIALOG BOX
A new document isn't personal or enterprise. It's just new. If a user pastes enterprise data into it, Windows enforces policy and the user is prompted with a consent dialog. This code prevents that from happening. This task is not about helping to protect data. It's more about keeping users from receiving the consent dialog box in cases where your app creates a brand new item.
C++ snippet
int WIP::PasteText(bool isNewEmptyDocument)
{
ComPtr<IClipboardStatics> pClipboardStatics;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_ApplicationModel_DataTransfer_Clipboard).Get(), &pClipboardStatics);
ComPtr<IDataPackageView> pDataPackageView;
pClipboardStatics->GetContent(&pDataPackageView);
boolean containsText = false;
pDataPackageView->Contains(HStringReference(L"Text").Get(), &containsText);
if (containsText)
{
ComPtr<IDataPackagePropertySetView> dataPackagePropertySetView;
pDataPackageView->get_Properties(&dataPackagePropertySetView);
ComPtr<IDataPackagePropertySetView3> dataPackagePropertySetView3;
dataPackagePropertySetView.As(&dataPackagePropertySetView3);
HSTRING enterpriseID;
dataPackagePropertySetView3->get_EnterpriseId(&enterpriseID);
if (enterpriseID != NULL)
{
ComPtr<IProtectionPolicyManagerStatics> ppmStatics;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_ProtectionPolicyManager).Get(), &ppmStatics);
boolean result;
ppmStatics->TryApplyProcessUIPolicy(enterpriseID, &result);
// Call the GetTextAsync method of the IDataPackageView interface. See earlier example.
// Then, add text to the new item or document.
}
else
{
ComPtr<IDataPackageView3> pDataPackageView3;
pDataPackageView.As(&pDataPackageView3);
__FIAsyncOperation_1_Windows__CSecurity__CEnterpriseData__CProtectionPolicyEvaluationResult_t * protectionPolicyOperation;
pDataPackageView3->RequestAccessAsync(&protectionPolicyOperation);
Event operationCompleted(CreateEvent(nullptr, true, false, nullptr));
protectionPolicyOperation->put_Completed(Callback<IAsyncOperationCompletedHandler<ProtectionPolicyEvaluationResult>>
([&](IAsyncOperation<ProtectionPolicyEvaluationResult> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(operationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(operationCompleted.Get(), INFINITE);
ProtectionPolicyEvaluationResult result;
protectionPolicyOperation->GetResults(&result);
if (result == ProtectionPolicyEvaluationResult::ProtectionPolicyEvaluationResult_Allowed)
{
// Call the GetTextAsync method of the IDataPackageView interface. See earlier example.
// Then, add text to the new item or document.
}
}
}
return 0;
}
C++/ CX snippet
void PasteText(bool isNewEmptyDocument)
{
DataPackageView ^dataPackageView = Clipboard::GetContent();
if (dataPackageView->Contains(StandardDataFormats::Text))
{
if ((dataPackageView->Properties->EnterpriseId) &&
!(dataPackageView->Properties->EnterpriseId->IsEmpty()))
{
if (isNewEmptyDocument)
{
ProtectionPolicyManager::TryApplyProcessUIPolicy(dataPackageView->Properties->EnterpriseId);
auto getTextTask = create_task(dataPackageView->GetTextAsync());
getTextTask.then([](Platform::String ^contentsOfClipboard)
{
// add this string to the item.
});
}
else
{
auto requestAccessTask = create_task(dataPackageView->RequestAccessAsync());
requestAccessTask.then([&](ProtectionPolicyEvaluationResult policyResult)
{
if (policyResult == ProtectionPolicyEvaluationResult::Allowed)
{
auto getTextTask = create_task(dataPackageView->GetTextAsync());
getTextTask.then([](Platform::String ^contentsOfClipboard)
{
// add this string to the item.
});
}
});
}
}
}
}
APIs:
DataPackageView.RequestAccessAsync
ProtectionPolicyEvaluationResult
ProtectionPolicyManager.TryApplyProcessUIPolicy
Read data from an OLE clipboard
If your app makes use of the OLE Component Object Model, you can use the OleGetClipboardWithEnterpriseInfo function instead of having to use a DataPackageView object.
C++ snippet
int WIP::OLEPasteText(void)
{
IDataObject *pDataObject;
PWSTR enterpriseID;
PWSTR sourceDescription;
PWSTR targetDescription;
PWSTR dataDescription;
HRESULT hr = OleGetClipboardWithEnterpriseInfo
(&pDataObject, &enterpriseID, &sourceDescription, &targetDescription, &dataDescription);
if (hr == S_OK)
{
FORMATETC fmtetc = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
STGMEDIUM stgmed;
hr = pDataObject->GetData(&fmtetc, &stgmed);
if (hr == S_OK)
{
char *charData = (char*)GlobalLock(stgmed.hGlobal);
GlobalUnlock(stgmed.hGlobal);
ReleaseStgMedium(&stgmed);
}
pDataObject->Release();
}
return 0;
}
APIs:
Read data that is dragged into your application
If you want your app to take over policy evaluation for data that users drag into your application, implement the IEnterpriseDropTarget interface and two methods.
IMPLEMENT THE IENTERPRISEDROPTARGET INTERFACE
class CDropTarget : public IDropTarget, IEnterpriseDropTarget
{
// IDropTarget methods
// IEnterpriseDropTarget methods
PWSTR m_enterpriseId; // A variable to cache the enterprise ID for the current drag operation.
};
INDICATE THAT YOU WANT TO HANDLE WIP POLICY EVALUATION
HRESULT CDropTarget::IsEvaluatingEdpPolicy(_Out_ BOOL *isEvaluatingEdpPolicy)
{
*isEvaluatingEdpPolicy = TRUE;
return S_OK;
}
CACHE THE ENTERPISE ID OF THE DRAG OPERATION
HRESULT CDropTarget::SetDropSourceEnterpriseId(_In_ PCWSTR enterpriseId)
{
return SHStrDup(enterpriseId, &m_enterpriseId);
}
MAKE WIP POLICY DECISIONS ON THE DATA THAT THE USER DRAGS INTO YOUR APPLICATION
HRESULT CDropTarget::Drop(_In_ LPDATAOBJECT /* pDataObject */, _In_ DWORD /* dwKeyState */, _In_ POINTL /* pt */, _Inout_ LPDWORD /* pdwEffect */)
{
if (m_enterpriseId)
{
// Make EDP policy decisions using m_enterpriseId.
// Clear the current enterprise id so that it is reset
// for the next drag operation.
CoTaskMemFree(m_enterpriseId);
}
return S_OK;
}
Protect enterprise data
Protect enterprise data that leaves your app.
Data leaves your app when you show it in a page, save it to a file or network endpoint.
In this section, we'll show you how to do these things:
- Protect data that appears in pages
- Protect data to a file
- Protect data that users save by using a file save dialog box
- Protect data to a file as a background process
- Protect part of a file
- Read the protected part of a file
- Protect data to a folder
- Protect data to a network end point
- Protect files that you copy to another location
- Protect enterprise data when the screen is locked
Protect data that appears in pages
When you show data in a page, let Windows know what type of data it is (personal or enterprise). To do that, tag the current app view or tag the entire app process.
When you tag the view or the process, Windows enforces policy on it. This helps prevent data leaks that result from actions that your app doesn't control. For example, on a computer, a user could use CTRL-V to copy enterprise information from a view and then paste that information to another app. Windows protects against that. Windows also helps to enforce share contracts.
TAG THE CURRENT VIEW
Do this if your app has multiple views where some views consume enterprise data and some consume personal data.
int WIP::TagWindow(HSTRING identity, HWND hWnd, bool tagAsEnterprise)
{
RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
ComPtr<IProtectionPolicyManagerInterop> ppmInterop;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_ProtectionPolicyManager).Get(), &ppmInterop);
ComPtr<IProtectionPolicyManager> ppmInstance;
ppmInterop->GetForWindow(hWnd, IID_PPV_ARGS(&ppmInstance));
if (tagAsEnterprise)
{
// tag as enterprise data. "identity" the string that contains the enterprise ID.
// You'd get that from a file, network endpoint, or clipboard data package.
ppmInstance->put_Identity(identity);
}
else
{
// tag as personal data.
ppmInstance->put_Identity(NULL);
}
return 0;
}
APIs:
TAG THE PROCESS
Do this if all views in your app will work with only one type of data (personal or enterprise).
This prevents you from having to manage independently tagged views.
C++ snippet
int WIP::TagProcess(HSTRING identity, bool tagAsEnterprise)
{
RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
ComPtr<IProtectionPolicyManagerStatics> ppmStatics;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_ProtectionPolicyManager).Get(), &ppmStatics);
boolean result;
if (tagAsEnterprise)
{
// tag as enterprise data. "identity" the string that contains the enterprise ID.
// You'd get that from a file, network endpoint, or clipboard data package.
ppmStatics->TryApplyProcessUIPolicy(identity, &result);
}
else
{
// tag as personal data.
ppmStatics->TryApplyProcessUIPolicy(HStringReference(L"").Get(), &result);
}
return 0;
}
C++/ CX snippet
// tag as enterprise data. "identity" the string that contains the enterprise ID.
// You'd get that from a file, network endpoint, or clipboard data package.
bool result = ProtectionPolicyManager::TryApplyProcessUIPolicy(identity);
// tag as personal data.
bool result = ProtectionPolicyManager::TryApplyProcessUIPolicy("");
APIs:
Protect data to a file
Create a protected file and then write to it.
This prevents you from having to manage independently tagged views.
STEP 1: DETERMINE IF YOUR APP CAN CREATE AN ENTERPRISE FILE
Your app can create an enterprise file if the identity string is managed by policy and your app is on the Allowed list of that policy.
C++ snippet
ComPtr<IProtectionPolicyManagerStatics> ppmStatics;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_ProtectionPolicyManager).Get(), &ppmStatics);
boolean result;
ppmStatics->IsIdentityManaged(identity, &result);
if (!result)
return -1;
C++/ CX snippet
if (!ProtectionPolicyManager::IsIdentityManaged(identity))
{
// Identity is not managed so no need to protect the file.
// Prompt the user, log a message, or perform some other action.
}
APIs:
STEP 2: CREATE THE FILE AND PROTECT IT TO THE ENTITY
C++ snippet
HString hstrKnownFolders;
hstrKnownFolders.Set(RuntimeClass_Windows_Storage_KnownFolders);
ComPtr<IActivationFactory> pKnownFoldersActivationFactory;
ABI::Windows::Foundation::GetActivationFactory(hstrKnownFolders.Get(), &pKnownFoldersActivationFactory);
ComPtr<IKnownFoldersStatics> pKnownFolders;
pKnownFoldersActivationFactory.As(&pKnownFolders);
ComPtr<IStorageFolder> pStorageFolder;
pKnownFolders->get_DocumentsLibrary(&pStorageFolder);
__FIAsyncOperation_1_Windows__CStorage__CStorageFile_t *operation;
pStorageFolder->CreateFileAsync(HStringReference(L"sample.txt").Get(),
CreationCollisionOption::CreationCollisionOption_ReplaceExisting, &operation);
Event operationCompleted(CreateEvent(nullptr, true, false, nullptr));
operation->put_Completed(Callback<IAsyncOperationCompletedHandler<StorageFile*>>
([&](IAsyncOperation<StorageFile*> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(operationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(operationCompleted.Get(), INFINITE);
ComPtr<IStorageFile> pFile;
operation->GetResults(&pFile);
ComPtr<IFileProtectionManagerStatics> fpmStatics;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_FileProtectionManager).Get(), &fpmStatics);
__FIAsyncOperation_1_Windows__CSecurity__CEnterpriseData__CFileProtectionInfo_t * protectOperation;
ComPtr<IStorageItem> storageItem;
pFile.As(&storageItem);
fpmStatics->ProtectAsync(storageItem.Get(), identity, &protectOperation);
Event protectOperationCompleted(CreateEvent(nullptr, true, false, nullptr));
protectOperation->put_Completed(Callback<IAsyncOperationCompletedHandler<FileProtectionInfo*>>
([&](IAsyncOperation<FileProtectionInfo*> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(protectOperationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(protectOperationCompleted.Get(), INFINITE);
ComPtr<IFileProtectionInfo> fileProtectionInfo;
protectOperation->GetResults(&fileProtectionInfo);
FileProtectionStatus status;
fileProtectionInfo->get_Status(&status);
if (status == FileProtectionStatus::FileProtectionStatus_Protected)
{
// write to stream or buffer
}
C++/ CX snippet
StorageFolder ^storageFolder = KnownFolders::DocumentsLibrary;
auto createFileTask = create_task(storageFolder->CreateFileAsync
("sample.txt", CreationCollisionOption::ReplaceExisting));
createFileTask.then([identity, enterpriseData](StorageFile ^storageFile)
{
auto protectTask = create_task(FileProtectionManager::ProtectAsync(storageFile, identity));
});
STEP 3: WRITE THAT STREAM OR BUFFER TO A FILE
Write to a stream
C++ snippet
int WIP::SaveDataToAStream(HSTRING enterpriseData, IStorageFile * filePtr)
{
__FIAsyncOperation_1_Windows__CStorage__CStreams__CIRandomAccessStream_t * operation;
filePtr->OpenAsync(FileAccessMode::FileAccessMode_ReadWrite, &operation);
Event operationCompleted(CreateEvent(nullptr, true, false, nullptr));
operation->put_Completed(Callback<IAsyncOperationCompletedHandler<IRandomAccessStream*>>
([&](IAsyncOperation<IRandomAccessStream*> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(operationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(operationCompleted.Get(), INFINITE);
ComPtr<IRandomAccessStream> stream;
operation->GetResults(&stream);
IOutputStream * outputStream;
stream->GetOutputStreamAt(0, &outputStream);
ComPtr<IDataWriter> dataWriter;
ComPtr<IDataWriterFactory> dataWriterFactory;
Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Storage_Streams_DataWriter).Get(), &dataWriterFactory);
dataWriterFactory->CreateDataWriter(outputStream, &dataWriter);
UINT32 byteCount;
dataWriter->WriteString(enterpriseData, &byteCount);
__FIAsyncOperation_1_UINT32_t * storeOperation;
Event storeOperationCompleted(CreateEvent(nullptr, true, false, nullptr));
dataWriter->StoreAsync(&storeOperation);
storeOperation->put_Completed(Callback<IAsyncOperationCompletedHandler<UINT32>>
([&](IAsyncOperation<UINT32> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(storeOperationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(storeOperationCompleted.Get(), INFINITE);
return 0;
}
C++/ CX snippet
protectTask.then([storageFile, enterpriseData](FileProtectionInfo ^fileProtectionInfo)
{
if (fileProtectionInfo->Status == FileProtectionStatus::Protected)
{
auto openTask = create_task(storageFile->OpenAsync(FileAccessMode::ReadWrite));
openTask.then([enterpriseData](IRandomAccessStream ^stream)
{
{
auto outputStream = stream->GetOutputStreamAt(0);
{
auto dataWriter = ref new DataWriter(outputStream);
dataWriter->WriteString(enterpriseData);
delete dataWriter;
}
}
});
}
});
Write to a buffer
C++ snippet
int WIP::SaveDataToABuffer(HSTRING enterpriseData, IStorageFile * filePtr)
{
ComPtr<ICryptographicBufferStatics> cryptographicBuffer;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_Cryptography_CryptographicBuffer).Get(), &cryptographicBuffer);
ComPtr<IBufferFactory> bufferFactory;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(), &bufferFactory);
ComPtr<IBuffer> buffer;
bufferFactory->Create(sizeof(enterpriseData), &buffer);
cryptographicBuffer->ConvertBinaryToString(BinaryStringEncoding::BinaryStringEncoding_Utf8, buffer.Get(), &enterpriseData);
ComPtr<IFileIOStatics> fileIOStatics;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Storage_FileIO).Get(), &fileIOStatics);
IAsyncAction *operation;
fileIOStatics->WriteBufferAsync(filePtr, buffer.Get(), &operation);
Event operationCompleted(CreateEvent(nullptr, true, false, nullptr));
operation->put_Completed(Callback<IAsyncActionCompletedHandler>
([&](IAsyncAction *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(operationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(operationCompleted.Get(), INFINITE);
operation->GetResults();
return 0;
}
C++/ CX snippet
if (fileProtectionInfo->Status == FileProtectionStatus::Protected)
{
auto buffer = Windows::Security::Cryptography::CryptographicBuffer::ConvertStringToBinary
(enterpriseData, Windows::Security::Cryptography::BinaryStringEncoding::Utf8);
auto writeTask = create_task(FileIO::WriteBufferAsync(storageFile, buffer));
writeTask.then([]()
{
// Successfully wrote file.
});
}
APIs:
Protect data that users save by using a file save dialog box
If your app opens a file save as dialog box, add code that collects the EncrptionOwners property from the user.
In the dialog box, a file protection button appears next to the file name. Users choose that button to select an enterprise ID.
After the user saves the file, your app can get the enterprise ID that the user chooses by retrieving the PKEY_Security_EncryptionOwners property of the dialog's property collection.
Then, you can protect the file by using that enterprise ID.
STEP 1: SET THE INITIAL ENCRYPTION SETTING
HRESULT InvokeSaveDialog(HWND hwnd, PCWSTR pszDefaultFileName, _Outptr_ LPWSTR *ppszNewFileName)
{
// Set the initial encryption setting to false
IFileSaveDialog *pfsd = NULL;
ComPtr<IPropertyDescriptionList> propertyDescriptionList;
if (SUCCEEDED(PSGetPropertyDescriptionListFromString
(L"prop:System.Security.EncryptionOwners", IID_PPV_ARGS(&propertyDescriptionList))))
pfsd->SetCollectedProperties(propertyDescriptionList.Get(), false);
}
STEP 2: GET THE ENTEPRISE ID THAT THE USER CHOOSES IN THE DIALOG BOX
WCHAR enterpriseId[22] = { L'\0' };
if (pfsd != NULL)
{
*enterpriseId = L'\0';
ComPtr<IPropertyStore> propertyStore;
FAILED(pfsd->GetProperties(&propertyStore));
PROPVARIANT pv;
PropVariantInit(&pv);
PWSTR enterpriseId = nullptr;
if (SUCCEEDED(propertyStore->GetValue(PKEY_Security_EncryptionOwners, &pv)) &&
(pv.vt == (VT_VECTOR | VT_LPWSTR)) && (pv.calpwstr.cElems > 0))
{
enterpriseId = pv.calpwstr.pElems[0];
}
}
STEP 3: PROTECT THE DATA TO A FILE
Protect data to a file as a background process
This code can run while the screen of the device is locked. If the administrator configured a secure "Data protection under lock" (DPL) policy, Windows removes the encryption keys required to access protected resources from device memory. This prevents data leaks if the device is lost. This same feature also removes keys associated with protected files when their handles are closed.
You'll have to use an approach that keeps the file handle open when you create a file.
STEP 1: DETERMINE IF YOU CAN CREATE AN ENTERPRISE FILE
You can create an enterprise file if the identity that you're using is managed by policy and your app is on the allowed list of that policy.
C++ snippet
ComPtr<IProtectionPolicyManagerStatics> ppmStatics;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_ProtectionPolicyManager).Get(), &ppmStatics);
boolean result;
ppmStatics->IsIdentityManaged(identity, &result);
if (!result)
return -1;
C++/ CX snippet
if (!ProtectionPolicyManager::IsIdentityManaged(identity))
{
// Identity is not managed so no need to protect the file.
// Prompt the user, log a message, or perform some other action.
}
APIs:
STEP 2: CREATE A FILE AND PROTECT IT TO THE ENTITY
The FileProtectionManager.CreateProtectedAndOpenAsync creates a protected file and keeps the file handle open while you write to it.
C++ snippet
HString hstrKnownFolders;
hstrKnownFolders.Set(RuntimeClass_Windows_Storage_KnownFolders);
ComPtr<IActivationFactory> pKnownFoldersActivationFactory;
ABI::Windows::Foundation::GetActivationFactory(hstrKnownFolders.Get(), &pKnownFoldersActivationFactory);
ComPtr<IKnownFoldersStatics> pKnownFolders;
pKnownFoldersActivationFactory.As(&pKnownFolders);
ComPtr<IStorageFolder> pStorageFolder;
pKnownFolders->get_DocumentsLibrary(&pStorageFolder);
ComPtr<IFileProtectionManagerStatics> fpmStatics;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_FileProtectionManager).Get(), &fpmStatics);
__FIAsyncOperation_1_Windows__CSecurity__CEnterpriseData__CProtectedFileCreateResult_t * protectOperation;
fpmStatics->CreateProtectedAndOpenAsync(pStorageFolder.Get(), HStringReference(L"sample.txt").Get(), identity,
CreationCollisionOption::CreationCollisionOption_ReplaceExisting, &protectOperation);
Event protectOperationCompleted(CreateEvent(nullptr, true, false, nullptr));
protectOperation->put_Completed(Callback<IAsyncOperationCompletedHandler<ProtectedFileCreateResult*>>
([&](IAsyncOperation<ProtectedFileCreateResult*> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(protectOperationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(protectOperationCompleted.Get(), INFINITE);
ComPtr<IProtectedFileCreateResult> fileProtectionResult;
protectOperation->GetResults(&fileProtectionResult);
IFileProtectionInfo * fileProtectionInfo;
fileProtectionResult->get_ProtectionInfo(&fileProtectionInfo);
FileProtectionStatus status;
fileProtectionInfo->get_Status(&status);
if (status == FileProtectionStatus::FileProtectionStatus_Protected)
{
SaveDataToAStreamInBackground(enterpriseData, fileProtectionResult.Get());
}
else if (status == FileProtectionStatus::FileProtectionStatus_AccessSuspended)
{
// Perform any special processing for the access suspended case.
}
C++/ CX snippet
StorageFolder ^storageFolder = KnownFolders::DocumentsLibrary;
auto createProtectedTask = create_task(FileProtectionManager::CreateProtectedAndOpenAsync
(storageFolder, "sample.txt", identity, CreationCollisionOption::ReplaceExisting));
APIs:
STEP 3: WRITE A STREAM OR BUFFER TO THE FILE
This example writes a stream to a file.
C++ snippet
int WIP::SaveDataToAStreamInBackground(HSTRING enterpriseData, IProtectedFileCreateResult * protectedFileCreateResult)
{
RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
ComPtr<IRandomAccessStream> stream;
IOutputStream * outputStream;
protectedFileCreateResult->get_Stream(&stream);
stream->GetOutputStreamAt(0, &outputStream);
ComPtr<IDataWriter> dataWriter;
ComPtr<IDataWriterFactory> dataWriterFactory;
Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Storage_Streams_DataWriter).Get(), &dataWriterFactory);
dataWriterFactory->CreateDataWriter(outputStream, &dataWriter);
UINT32 byteCount;
dataWriter->WriteString(enterpriseData, &byteCount);
__FIAsyncOperation_1_UINT32_t * storeOperation;
Event storeOperationCompleted(CreateEvent(nullptr, true, false, nullptr));
dataWriter->StoreAsync(&storeOperation);
storeOperation->put_Completed(Callback<IAsyncOperationCompletedHandler<UINT32>>
([&](IAsyncOperation<UINT32> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(storeOperationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(storeOperationCompleted.Get(), INFINITE);
__FIAsyncOperation_1_boolean_t * flushOperation;
dataWriter->FlushAsync(&flushOperation);
Event flushOperationCompleted(CreateEvent(nullptr, true, false, nullptr));
flushOperation->put_Completed(Callback<IAsyncOperationCompletedHandler<bool>>
([&](IAsyncOperation<bool> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(flushOperationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(flushOperationCompleted.Get(), INFINITE);
return 0;
}
C++/ CX snippet
createProtectedTask.then([enterpriseData](ProtectedFileCreateResult ^protectedFileCreateResult)
{
if (protectedFileCreateResult->ProtectionInfo->Status == FileProtectionStatus::Protected)
{
IOutputStream ^outputStream = protectedFileCreateResult->Stream->GetOutputStreamAt(0);
{
DataWriter ^writer = ref new DataWriter(outputStream);
writer->WriteString(enterpriseData);
auto storeTask = create_task(writer->StoreAsync());
storeTask.then([writer](task<unsigned int>bytesWritten)
{
auto flushTask = create_task(writer->FlushAsync());
flushTask.then([&](task<bool>result)
{
delete writer;
});
});
}
delete outputStream;
}
else if (protectedFileCreateResult->ProtectionInfo->Status == FileProtectionStatus::AccessSuspended)
{
// Perform any special processing for the access suspended case.
}
});
APIs:
Protect part of a file
In most cases, it's cleaner to store enterprise and personal data separately but you can store them to the same file if you want. For example, Microsoft Outlook can store enterprise mails alongside of personal mails in a single archive file.
Encrypt the enterprise data but not the entire file. That way, users can continue using that file even if they un-enroll from MDM or their enterprise data access rights are revoked. Also, your app should keep track of what data it encrypts so that it knows what data to protect when it reads the file back into memory.
STEP 1: ADD ENTERPRISE DATA TO AN ENCRYPTED STREAM OR BUFFER
C++ snippet
RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
HSTRING enterpriseDataString = HStringReference(L"<employees><employee><name>Bill</name><social>xxx-xxx-xxxx</social></employee></employees>").Get();
ComPtr<ICryptographicBufferStatics> cryptographicBuffer;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_Cryptography_CryptographicBuffer).Get(), &cryptographicBuffer);
ComPtr<IBufferFactory> bufferFactory;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(), &bufferFactory);
ComPtr<IBuffer> buffer;
bufferFactory->Create(sizeof(enterpriseDataString), &buffer);
cryptographicBuffer->ConvertStringToBinary(enterpriseDataString, BinaryStringEncoding::BinaryStringEncoding_Utf8, &buffer);
ComPtr<IDataProtectionManagerStatics> pdpStatics;
HRESULT hr = ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_DataProtectionManager).Get(), &pdpStatics);
__FIAsyncOperation_1_Windows__CSecurity__CEnterpriseData__CBufferProtectUnprotectResult_t *operation;
// This is enterprise data - encrypt it.
pdpStatics->ProtectAsync(buffer.Get(), identity, &operation);
Event operationCompleted(CreateEvent(nullptr, true, false, nullptr));
operation->put_Completed(Callback<IAsyncOperationCompletedHandler<BufferProtectUnprotectResult*>>
([&](IAsyncOperation<BufferProtectUnprotectResult*> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(operationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(operationCompleted.Get(), INFINITE);
IBufferProtectUnprotectResult * pIBProtectUnprotectResult;
operation->GetResults(&pIBProtectUnprotectResult);
IBuffer * enterpriseBuffer;
pIBProtectUnprotectResult->get_Buffer(&enterpriseBuffer);
C++/ CX snippet
Platform::String ^enterpriseDataString =
"<employees><employee><name>Bill</name><social>xxx-xxx-xxxx</social></employee></employees>";
auto enterpriseData = Windows::Security::Cryptography::CryptographicBuffer::ConvertStringToBinary
(enterpriseDataString, Windows::Security::Cryptography::BinaryStringEncoding::Utf8);
auto protectTask = create_task(DataProtectionManager::ProtectAsync(enterpriseData, identity));
protectTask.then([enterpriseData](BufferProtectUnprotectResult ^result)
{
enterpriseData = result->Buffer;
// next snippet goes in here.
});
APIs:
STEP 2: ADD PERSONAL DATA TO AN UNENCRYPTED STREAM OR BUFFER
C++snippet
HSTRING personalDataString = HStringReference(L"<recipies><recipe><name>BillsCupCakes</name><cooktime>30</cooktime></recipe></recipies>").Get();
IBuffer * personalBuffer;
bufferFactory->Create(sizeof(personalDataString), &personalBuffer);
cryptographicBuffer->ConvertStringToBinary(personalDataString, BinaryStringEncoding::BinaryStringEncoding_Utf8, &personalBuffer);
C++/ CX snippet
Platform::String ^personalDataString =
"<recipies><recipe><name>BillsCupCakes</name><cooktime>30</cooktime></recipe></recipies>";
auto personalData = Windows::Security::Cryptography::CryptographicBuffer::ConvertStringToBinary
(personalDataString, Windows::Security::Cryptography::BinaryStringEncoding::Utf8);
STEP 3: WRITE BOTH STREAMS OR BUFFERS TO THE FILE
C++ snippet
__FIAsyncOperation_1_Windows__CStorage__CStreams__CIRandomAccessStream_t * openOperation;
pFile->OpenAsync(FileAccessMode::FileAccessMode_ReadWrite, &openOperation);
Event OpenOperationCompleted(CreateEvent(nullptr, true, false, nullptr));
openOperation->put_Completed(Callback<IAsyncOperationCompletedHandler<IRandomAccessStream*>>
([&](IAsyncOperation<IRandomAccessStream*> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(OpenOperationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(OpenOperationCompleted.Get(), INFINITE);
ComPtr<IRandomAccessStream> stream;
openOperation->GetResults(&stream);
IOutputStream * outputStream;
stream->GetOutputStreamAt(0, &outputStream);
ComPtr<IDataWriter> dataWriter;
ComPtr<IDataWriterFactory> dataWriterFactory;
Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Storage_Streams_DataWriter).Get(), &dataWriterFactory);
dataWriterFactory->CreateDataWriter(outputStream, &dataWriter);
UINT32 byteCount;
dataWriter->WriteBuffer(enterpriseBuffer);
dataWriter->WriteBuffer(personalBuffer);
__FIAsyncOperation_1_UINT32_t * storeOperation;
Event storeOperationCompleted(CreateEvent(nullptr, true, false, nullptr));
dataWriter->StoreAsync(&storeOperation);
storeOperation->put_Completed(Callback<IAsyncOperationCompletedHandler<UINT32>>
([&](IAsyncOperation<UINT32> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(storeOperationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(storeOperationCompleted.Get(), INFINITE);
__FIAsyncOperation_1_boolean_t * flushOperation;
dataWriter->FlushAsync(&flushOperation);
Event flushOperationCompleted(CreateEvent(nullptr, true, false, nullptr));
flushOperation->put_Completed(Callback<IAsyncOperationCompletedHandler<bool>>
([&](IAsyncOperation<bool> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(flushOperationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(flushOperationCompleted.Get(), INFINITE);
C++/ CX snippet
StorageFolder ^storageFolder = KnownFolders::DocumentsLibrary;
auto createFileTask = create_task(storageFolder->CreateFileAsync("data.xml", CreationCollisionOption::ReplaceExisting));
createFileTask.then([enterpriseDataResult, personalData](StorageFile ^storageFile)
{
// Write both buffers to the file and save the file.
auto openTask = create_task(storageFile->OpenAsync(FileAccessMode::ReadWrite));
openTask.then([enterpriseDataResult, personalData](IRandomAccessStream ^stream)
{
{
auto outputStream = stream->GetOutputStreamAt(0);
{
auto dataWriter = ref new DataWriter(outputStream);
dataWriter->WriteBuffer(enterpriseData);
dataWriter->WriteBuffer(personalData);
auto storeTask = create_task(dataWriter->StoreAsync());
storeTask.then([dataWriter](task<unsigned int>bytesWritten)
{
auto flushTask = create_task(dataWriter->FlushAsync());
flushTask.then([dataWriter](task<bool>result)
{
delete dataWriter;
});
});
}
}
});
STEP 4: KEEP TRACK OF THE LOCATION OF YOUR ENTERPRISE DATA IN THE FILE
It's the responsibility of your app to keep track of which data in that file that is enterprise owned.
You can store that information in a property associated with the file, in a database, or in some header text in the file.
Read the protected part of a file
Here's how you'd read the enterprise data out of that file.
STEP 1: OPEN THE DATA FILE AND MAKE SURE IT'S NOT PROTECTED
C++ snippet
int WIP::OpenDataFile(IStorageFolder * pStorageFolder)
{
__FIAsyncOperation_1_Windows__CStorage__CStorageFile_t* dataFileOperation;
pStorageFolder->GetFileAsync(HStringReference(L"data.xml").Get(), &dataFileOperation);
Event dataFileOperationCompleted(CreateEvent(nullptr, true, false, nullptr));
dataFileOperation->put_Completed(Callback<IAsyncOperationCompletedHandler<StorageFile*>>
([&](IAsyncOperation<StorageFile*> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(dataFileOperationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(dataFileOperationCompleted.Get(), INFINITE);
IStorageFile * dataFile;
dataFileOperation->GetResults(&dataFile);
return 0;
}
int WIP::GetProtectionInfo(HSTRING identity, IStorageFile * file, bool **canRead)
{
RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
ComPtr<IFileProtectionManagerStatics> fpmStatics;
Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_FileProtectionManager).Get(), &fpmStatics);
__FIAsyncOperation_1_Windows__CSecurity__CEnterpriseData__CFileProtectionInfo_t *operation;
ComPtr<IStorageItem> storageItem;
ComPtr<IStorageFile> x(file);
x.As(&storageItem);
fpmStatics->GetProtectionInfoAsync(storageItem.Get(), &operation);
Event operationCompleted(CreateEvent(nullptr, true, false, nullptr));
operation->put_Completed(Callback<IAsyncOperationCompletedHandler<FileProtectionInfo*>>
([&](IAsyncOperation<FileProtectionInfo*> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(operationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(operationCompleted.Get(), INFINITE);
IFileProtectionInfo * fileProtectionInfo;
FileProtectionStatus fileProtectionStatus;
operation->GetResults(&fileProtectionInfo);
fileProtectionInfo->get_Status(&fileProtectionStatus);
bool readable = true;
if (fileProtectionStatus == FileProtectionStatus::FileProtectionStatus_Protected)
{
ComPtr<IProtectionPolicyManagerStatics> ppmStatics;
Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_ProtectionPolicyManager).Get(), &ppmStatics);
boolean result;
ppmStatics->IsIdentityManaged(identity, &result);
if (result == 0)
readable = false;
}
*canRead = &readable;
return 0;
}
C++/ CX snippet
auto getFileTask = create_task(storageFolder->GetFileAsync("data.xml"));
getFileTask.then([&](Windows::Storage::StorageFile ^dataFile)
{
auto getProtectionInfoTask = create_task(FileProtectionManager::GetProtectionInfoAsync(dataFile));
getProtectionInfoTask.then([dataFile, startPosition, endPosition](FileProtectionInfo ^protectionInfo)
{
if (protectionInfo->Status == FileProtectionStatus::Protected)
{
// Prompt the user, log an error, or perform some other action.
}
// Next snippet goes in here.
});
APIs:
STEP 2: READ THE ENTERPRISE DATA FROM THE FILE
C++ snippet
RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
__FIAsyncOperation_1_Windows__CStorage__CStreams__CIRandomAccessStream_t *operation;
file->OpenAsync(FileAccessMode::FileAccessMode_ReadWrite, &operation);
Event operationCompleted(CreateEvent(nullptr, true, false, nullptr));
operation->put_Completed(Callback<IAsyncOperationCompletedHandler<IRandomAccessStream*>>
([&](IAsyncOperation<IRandomAccessStream*> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(operationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(operationCompleted.Get(), INFINITE);
IRandomAccessStream * pRandomAccessStream;
operation->GetResults(&pRandomAccessStream);
IInputStream * inputStream;
pRandomAccessStream->GetInputStreamAt(0, &inputStream);
ComPtr<IBufferFactory> bufferFactory;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(), &bufferFactory);
ComPtr<IBuffer> buffer;
bufferFactory->Create(50000, &buffer);
__FIAsyncOperationWithProgress_2_Windows__CStorage__CStreams__CIBuffer_UINT32_t * readOperation;
// endposition is an unsigned int that you provide. It identifies the position of the enterprise data in the file.
inputStream->ReadAsync(buffer.Get(), endPosition, InputStreamOptions_None, &readOperation);
Event readOperationCompleted(CreateEvent(nullptr, true, false, nullptr));
readOperation->put_Completed(Callback<IAsyncOperationWithProgressCompletedHandler<IBuffer*, UINT32>>
([&](IAsyncOperationWithProgress<IBuffer*, UINT32> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(readOperationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(readOperationCompleted.Get(), INFINITE);
ComPtr<IBuffer> enterpriseBuffer;
readOperation->GetResults(&enterpriseBuffer);
ComPtr<IDataProtectionManagerStatics> pdpStatics;
HRESULT hr = ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_DataProtectionManager).Get(), &pdpStatics);
__FIAsyncOperation_1_Windows__CSecurity__CEnterpriseData__CDataProtectionInfo_t * dataProtectionInfoOperation;
hr = pdpStatics->GetProtectionInfoAsync(enterpriseBuffer.Get(), &dataProtectionInfoOperation);
Event dataProtectionInfoOperationCompleted(CreateEvent(nullptr, true, false, nullptr));
dataProtectionInfoOperation->put_Completed(Callback<IAsyncOperationCompletedHandler<DataProtectionInfo*>>
([&](IAsyncOperation<DataProtectionInfo*> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(dataProtectionInfoOperationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(dataProtectionInfoOperationCompleted.Get(), INFINITE);
C++/ CX snippet
auto streamTask = create_task(dataFile->OpenAsync(Windows::Storage::FileAccessMode::ReadWrite));
streamTask.then([startPosition, endPosition](IRandomAccessStream ^stream)
{
stream->Seek(startPosition);
Windows::Storage::Streams::Buffer ^tempBuffer = ref new Windows::Storage::Streams::Buffer(50000);
// endposition is an unsigned int that you provide. It identifies the position of the enterprise data in the file.
auto readTask = create_task(stream->ReadAsync(tempBuffer, endPosition, InputStreamOptions::None));
readTask.then([](IBuffer ^enterpriseData)
{
// Next snippet goes in here.
});
}
STEP 3: DECRYPT THE BUFFER THAT CONTAINS ENTERPRISE DATA
C++ snippet
ComPtr<IDataProtectionInfo> info;
hr = dataProtectionInfoOperation->GetResults(&info);
DataProtectionStatus status;
info->get_Status(&status);
if (status == DataProtectionStatus::DataProtectionStatus_Protected)
{
__FIAsyncOperation_1_Windows__CSecurity__CEnterpriseData__CBufferProtectUnprotectResult_t *unProtectOperation;
pdpStatics->UnprotectAsync(enterpriseBuffer.Get(), &unProtectOperation);
Event unProtectOperationCompleted(CreateEvent(nullptr, true, false, nullptr));
unProtectOperation->put_Completed(Callback<IAsyncOperationCompletedHandler<BufferProtectUnprotectResult*>>
([&](IAsyncOperation<BufferProtectUnprotectResult*> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(unProtectOperationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(unProtectOperationCompleted.Get(), INFINITE);
IBufferProtectUnprotectResult * unProtectResults;
unProtectOperation->GetResults(&unProtectResults);
}
C++/ CX snippet
auto dataProtectionManagerTask = create_task(DataProtectionManager::GetProtectionInfoAsync(enterpriseData));
dataProtectionManagerTask.then([enterpriseData](DataProtectionInfo ^dataProtectionInfo)
{
if (dataProtectionInfo->Status == DataProtectionStatus::Protected)
{
auto unprotectTask = create_task(DataProtectionManager::UnprotectAsync(enterpriseData));
unprotectTask.then([](BufferProtectUnprotectResult ^result)
{
IBuffer ^ decryptedEnterpriseData = result->Buffer;
});
}
}
APIs:
Protect data to a folder
You can create a folder and protect it. That way any items that you add to that folder are automatically protected.
C++ snippet
int WIP::CreateANewFolderAndProtectAsync(HSTRING folderName, HSTRING identity)
{
RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
ComPtr<IProtectionPolicyManagerStatics> ppmStatics;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_ProtectionPolicyManager).Get(), &ppmStatics);
boolean result;
ppmStatics->IsIdentityManaged(identity, &result);
HString hstrKnownFolders;
hstrKnownFolders.Set(RuntimeClass_Windows_Storage_KnownFolders);
ComPtr<IActivationFactory> pKnownFoldersActivationFactory;
ABI::Windows::Foundation::GetActivationFactory(hstrKnownFolders.Get(), &pKnownFoldersActivationFactory);
ComPtr<IKnownFoldersStatics> pKnownFolders;
pKnownFoldersActivationFactory.As(&pKnownFolders);
ComPtr<IStorageFolder> pStorageFolder;
pKnownFolders->get_DocumentsLibrary(&pStorageFolder);
__FIAsyncOperation_1_Windows__CStorage__CStorageFolder_t * folderOperation;
pStorageFolder->CreateFolderAsync(folderName, CreationCollisionOption_OpenIfExists, &folderOperation);
Event folderOperationCompleted(CreateEvent(nullptr, true, false, nullptr));
folderOperation->put_Completed(Callback<IAsyncOperationCompletedHandler<StorageFolder*>>
([&](IAsyncOperation<StorageFolder*> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(folderOperationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(folderOperationCompleted.Get(), INFINITE);
ComPtr<IStorageFolder> pNewFolder;
HRESULT hr = folderOperation->GetResults(&pNewFolder);
ComPtr<IFileProtectionManagerStatics> fpmStatics;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_FileProtectionManager).Get(), &fpmStatics);
__FIAsyncOperation_1_Windows__CSecurity__CEnterpriseData__CFileProtectionInfo_t * protectOperation;
ComPtr<IStorageItem> storageItem;
pNewFolder.As(&storageItem);
fpmStatics->ProtectAsync(storageItem.Get(), identity, &protectOperation);
Event protectOperationCompleted(CreateEvent(nullptr, true, false, nullptr));
protectOperation->put_Completed(Callback<IAsyncOperationCompletedHandler<FileProtectionInfo*>>
([&](IAsyncOperation<FileProtectionInfo*> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(protectOperationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(protectOperationCompleted.Get(), INFINITE);
ComPtr<IFileProtectionInfo> fileProtectionInfo;
protectOperation->GetResults(&fileProtectionInfo);
FileProtectionStatus status;
fileProtectionInfo->get_Status(&status);
if (status != FileProtectionStatus::FileProtectionStatus_Protected)
{
// Protection failed.
return -1;
}
return 0;
}
C++/ CX snippet
task<bool> CreateANewFolderAndProtectItAsync(Platform::String ^folderName, Platform::String ^identity)
{
if (!ProtectionPolicyManager::IsIdentityManaged(identity))
{
// Identity is not managed so no need to protect the file.
// Prompt the user, log a message, or perform some other action.
}
StorageFolder ^storageFolder = KnownFolders::DocumentsLibrary;
auto createFolderTask = create_task(storageFolder->CreateFolderAsync(folderName));
createFolderTask.then([identity](StorageFolder ^newStorageFolder)
{
auto protectTask = create_task(FileProtectionManager::ProtectAsync(newStorageFolder, identity));
protectTask.then([](FileProtectionInfo ^fileProtectionInfo)
{
if (fileProtectionInfo->Status != FileProtectionStatus::Protected)
{
// Protection failed.
}
});
});
return task_from_result<bool>(true);
}
APIs:
ProtectionPolicyManager.IsIdentityManaged
FileProtectionManager.ProtectAsync
FileProtectionInfo.Identity
FileProtectionInfo.Status
Protect data to a network end point
Create a protected thread context to send data to an enterprise endpoint.
STEP 1: GET THE IDENTITY OF THE NETWORK ENDPOINT
C++ snippet
RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
ComPtr<IHostNameFactory> hostNameFactory;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Networking_HostName).Get(), &hostNameFactory);
ComPtr<IHostName> hostName;
hostNameFactory->CreateHostName(resourceURL, &hostName);
ComPtr<IProtectionPolicyManagerStatics> ppmStatics;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_ProtectionPolicyManager).Get(), &ppmStatics);
__FIAsyncOperation_1_HSTRING_t * result;
ppmStatics->GetPrimaryManagedIdentityForNetworkEndpointAsync(hostName.Get(), &result);
Event operationCompleted(CreateEvent(nullptr, true, false, nullptr));
result->put_Completed(Callback<IAsyncOperationCompletedHandler<HSTRING>>
([&](IAsyncOperation<HSTRING> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(operationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(operationCompleted.Get(), INFINITE);
C++/ CX snippet
Windows::Networking::HostName ^hostName = ref new Windows::Networking::HostName(resourceURI->Host);
auto getPrimaryNetworkEndPointTask =
create_task(ProtectionPolicyManager::GetPrimaryManagedIdentityForNetworkEndpointAsync(hostName));
// "resourceURI is a URI object and "dataToWrite" is an IInputStream.
getPrimaryNetworkEndPointTask.then([resourceURI, dataToWrite](Platform::String ^identity)
{
// Next snippet goes in here.
});
APIs:
STEP 2: CREATE A PROTECTED THREAD CONTEXT AND SEND DATA TO THE NETWORK ENDPOINT
C++ snippet
ComPtr<IProtectionPolicyManagerInterop> ppmInterop;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_ProtectionPolicyManager).Get(), &ppmInterop);
ComPtr<IProtectionPolicyManager> ppmInstance;
ppmInterop->GetForWindow(hWnd, IID_PPV_ARGS(&ppmInstance));
ppmInstance->put_Identity(identity);
ComPtr<IHttpClientFactory> httpClientFactory;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Web_Http_HttpClient).Get(), &httpClientFactory);
IThreadNetworkContext * threadNetworkContextResult;
ppmStatics->CreateCurrentThreadNetworkContext(identity, &threadNetworkContextResult);
ComPtr<IHttpBaseProtocolFilter> baseProtocolFilter;
RoActivateInstance
(HStringReference(RuntimeClass_Windows_Web_Http_Filters_HttpBaseProtocolFilter).Get(), &baseProtocolFilter);
ComPtr<IHttpFilter> filter;
baseProtocolFilter.As(&filter);
ComPtr<IHttpClient> httpClient;
httpClientFactory->Create(filter.Get(), &httpClient);
ComPtr<IHttpRequestMessageFactory> requestMessageFactory;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Web_Http_HttpRequestMessage).Get(), &requestMessageFactory);
ComPtr<IHttpMethodFactory> requestMethodFactory;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Web_Http_HttpMethod).Get(), &requestMethodFactory);
ComPtr<IHttpMethod> httpMethod;
HString str;
str.Set(L"PUT");
requestMethodFactory->Create(str.Get(), &httpMethod);
IHttpRequestMessage * requestMessage;
ComPtr<IUriRuntimeClassFactory> uriFactory;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Foundation_Uri).Get(), &uriFactory)
ComPtr<IUriRuntimeClass> uriRuntime;
uriFactory->CreateUri(resourceURL, &uriRuntime);
requestMessageFactory->Create(httpMethod.Get(), uriRuntime.Get(), &requestMessage);
ComPtr<IHttpStreamContentFactory> streamContentFactory;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Web_Http_HttpStreamContent).Get(), &streamContentFactory);
ComPtr<IHttpContent> content;
streamContentFactory->CreateFromInputStream(dataToWrite, &content); // dataToWrite variable is an IInputStream
requestMessage->put_Content(content.Get());
__FIAsyncOperationWithProgress_2_Windows__CWeb__CHttp__CHttpResponseMessage_Windows__CWeb__CHttp__CHttpProgress_t * requestOperation;
httpClient->SendRequestAsync(requestMessage, &requestOperation);
Event requestOperationCompleted(CreateEvent(nullptr, true, false, nullptr));
requestOperation->put_Completed(Callback<IAsyncOperationWithProgressCompletedHandler<HttpResponseMessage*, HttpProgress>>
([&](IAsyncOperationWithProgress<HttpResponseMessage*, HttpProgress> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(requestOperationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(requestOperationCompleted.Get(), INFINITE);
C++/ CX snippet
HttpClient ^client = nullptr;
if ((identity) && !(identity->IsEmpty()))
{
ThreadNetworkContext ^threadNetworkContext =
ProtectionPolicyManager::CreateCurrentThreadNetworkContext(identity);
client = ref new HttpClient();
HttpRequestMessage ^message = ref new HttpRequestMessage(HttpMethod::Put, resourceURI);
message->Content = ref new HttpStreamContent(dataToWrite);
auto sendRequestTask = create_task(client->SendRequestAsync(message));
sendRequestTask.then([](HttpResponseMessage ^response)
{
if (response->StatusCode == HttpStatusCode::Ok)
{
// write successful
}
else
{
// write not successful
}
});
}
else
{
// Can't write the network location.
}
APIs:
Protect files that you copy to another location
C++ snippet
int WIP::ProtectFromOneFileToAnother(IStorageFile * sourceFile, IStorageFile * targetFile)
{
ComPtr<IFileProtectionManagerStatics> fpmStatics;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_FileProtectionManager).Get(), &fpmStatics);
__FIAsyncOperation_1_boolean_t * protectOperation;
ComPtr<IStorageFile> comPtrSourceFile(sourceFile);
ComPtr<IStorageItem> sourceStorageItem;
comPtrSourceFile.As(&sourceStorageItem);
ComPtr<IStorageFile> comPtrTargetFile(targetFile);
ComPtr<IStorageItem> targetStorageItem;
comPtrTargetFile.As(&targetStorageItem);
fpmStatics->CopyProtectionAsync(sourceStorageItem.Get(), targetStorageItem.Get(), &protectOperation);
Event protectOperationCompleted(CreateEvent(nullptr, true, false, nullptr));
protectOperation->put_Completed(Callback<IAsyncOperationCompletedHandler<bool>>
([&](IAsyncOperation<bool> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(protectOperationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(protectOperationCompleted.Get(), INFINITE);
boolean results;
protectOperation->GetResults(&results);
if (results)
return 0;
else
return -1;
}
C++/ CX snippet
void CopyProtectionFromOneFileToAnother
(StorageFile ^ ^sourceStorageFile, StorageFile ^ ^targetStorageFile)
{
auto ^copyProtectionTask =
create_task(FileProtectionManager::CopyProtectionAsync(sourceStorageFile, targetStorageFile));
copyProtectionTask->then([](bool copyResult) {
if (!copyResult)
{
// Copying failed. To diagnose, you could check the file's status.
// (call FileProtectionManager.GetProtectionInfoAsync and
// check FileProtectionInfo.Status).
}});
}
APIs:
Protect enterprise data when the screen is locked
Remove all sensitive data in memory when the device is locked. When the user unlocks the device, your app can safely add that data back.
Handle the ProtectionPolicyManager.ProtectedAccessSuspending event so that your app knows when the screen is locked. This event is raised only if the administrator configures a secure data protection under lock policy. Windows temporarily removes the data protection keys that are provisioned on the device. Windows removes these keys to ensure that there is no unauthorized access to encrypted data while the device is locked and possibly not in possession of its owner.
Handle the ProtectionPolicyManager.ProtectedAccessResumed event so that your app knows when the screen is unlocked. This event is raised regardless of whether the administrator configures a secure data protection under lock policy.
Remove sensitive data in memory when the screen is locked
Protect sensitive data, and close any file streams that your app has opened on protected files to help ensure that the system doesn't cache any sensitive data in memory.
This example saves content from a text block to an encrypted buffer and removes the content from that text block.
C++ snippet
int WIP::HandleProtectedAccessSuspending(HWND hWnd, HSTRING ** textBoxString, IProtectedAccessSuspendingEventArgs * e)
{
RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
ComPtr<IDeferral> deferral;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Foundation_Deferral).Get(), &deferral);
e->GetDeferral(&deferral);
ComPtr<IProtectionPolicyManagerStatics> ppmStatics;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_ProtectionPolicyManager).Get(), &ppmStatics);
ComPtr<IProtectionPolicyManagerInterop> ppmInterop;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_ProtectionPolicyManager).Get(), &ppmInterop);
ComPtr<IProtectionPolicyManager> ppmInstance;
HSTRING identity;
if (SUCCEEDED(ppmInterop->GetForWindow(hWnd, IID_PPV_ARGS(&ppmInstance))))
{
HRESULT hRet = ppmInstance->get_Identity(&identity);
if (identity != NULL)
{
ComPtr<ICryptographicBufferStatics> cryptographicBuffer;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_Cryptography_CryptographicBuffer).Get(), &cryptographicBuffer);
// "protectedDocumentBuffer is a global IBuffer.
cryptographicBuffer->ConvertStringToBinary(**textBoxString, BinaryStringEncoding::BinaryStringEncoding_Utf8, &protectedDocumentBuffer);
ComPtr<IDataProtectionManagerStatics> pdpStatics;
HRESULT hr = ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_DataProtectionManager).Get(), &pdpStatics);
__FIAsyncOperation_1_Windows__CSecurity__CEnterpriseData__CBufferProtectUnprotectResult_t *operation;
pdpStatics->ProtectAsync(protectedDocumentBuffer.Get(), identity, &operation);
Event operationCompleted(CreateEvent(nullptr, true, false, nullptr));
operation->put_Completed(Callback<IAsyncOperationCompletedHandler<BufferProtectUnprotectResult*>>
([&](IAsyncOperation<BufferProtectUnprotectResult*> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(operationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(operationCompleted.Get(), INFINITE);
IBufferProtectUnprotectResult * pIBProtectUnprotectResult;
operation->GetResults(&pIBProtectUnprotectResult);
IDataProtectionInfo * protectionValue;
pIBProtectUnprotectResult->get_ProtectionInfo(&protectionValue);
DataProtectionStatus status;
protectionValue->get_Status(&status);
if (status == DataProtectionStatus_Protected)
{
IBuffer * protectedBuffer;
pIBProtectUnprotectResult->get_Buffer(&protectedBuffer);
protectedDocumentBuffer = protectedBuffer;
}
}
}
// Close any open streams that you are actively working with
// to make sure that we have no unprotected content in memory.
// Optionally, code goes here to use e.Deadline to determine whether we have more
// than 15 seconds left before the suspension deadline. If we do then process any
// messages queued up for sending while we are still able to access them.
deferral.Get()->Complete();
return 0;
}
C++/ CX snippet
void OnProtectedAccessSuspending
(Platform::Object ^sender, Windows::Security::EnterpriseData::ProtectedAccessSuspendingEventArgs ^args)
{
Deferral ^deferral = args->GetDeferral();
IBuffer ^documentBodyBuffer =
CryptographicBuffer::ConvertStringToBinary(string_in_UI, BinaryStringEncoding::Utf8);
auto protectTask =
create_task(DataProtectionManager::ProtectAsync
(documentBodyBuffer, "contoso.com"));
protectTask.then([](BufferProtectUnprotectResult ^result)
{
if (result->ProtectionInfo->Status == DataProtectionStatus::Protected)
{
protectedDocumentBuffer = result->Buffer;
string_in_UI = nullptr;
}
});
deferral->Complete();
}
APIs:
ProtectionPolicyManager.ProtectedAccessSuspending
ProtectionPolicyManager.Identity
DataProtectionManager.ProtectAsync
BufferProtectUnprotectResult.buffer
ProtectedAccessSuspendingEventArgs.GetDeferral
Deferral.Complete
Add back sensitive data when the device is unlocked
ProtectionPolicyManager.ProtectedAccessResumed is raised when the device is unlocked and the keys are available on the device again.
ProtectedAccessResumedEventArgs.Identities is an empty collection if the administrator hasn't configured a secure data protection under lock policy.
This example does the reverse of the previous example. It decrypts the buffer, adds information from that buffer back to the text box and then disposes of the buffer.
C++ snippet
int WIP::HandleProtectedAccessResumed(HWND hWnd, HSTRING * textBoxSting, IProtectedAccessSuspendingEventArgs * e)
{
RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
ComPtr<IProtectionPolicyManagerStatics> ppmStatics;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_ProtectionPolicyManager).Get(), &ppmStatics);
ComPtr<IProtectionPolicyManagerInterop> ppmInterop;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_ProtectionPolicyManager).Get(), &ppmInterop);
ComPtr<IProtectionPolicyManager> ppmInstance;
HSTRING identity;
if (SUCCEEDED(ppmInterop->GetForWindow(hWnd, IID_PPV_ARGS(&ppmInstance))))
{
HRESULT hRet = ppmInstance->get_Identity(&identity);
if (identity != NULL)
{
ComPtr<IDataProtectionManagerStatics> pdpStatics;
HRESULT hr = ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_EnterpriseData_DataProtectionManager).Get(), &pdpStatics);
__FIAsyncOperation_1_Windows__CSecurity__CEnterpriseData__CBufferProtectUnprotectResult_t *operation;
pdpStatics->ProtectAsync(protectedDocumentBuffer.Get(), identity, &operation);
Event operationCompleted(CreateEvent(nullptr, true, false, nullptr));
operation->put_Completed(Callback<IAsyncOperationCompletedHandler<BufferProtectUnprotectResult*>>
([&](IAsyncOperation<BufferProtectUnprotectResult*> *pHandler, AsyncStatus status) -> HRESULT
{
SetEvent(operationCompleted.Get());
return S_OK;
}).Get());
WaitForSingleObject(operationCompleted.Get(), INFINITE);
IBufferProtectUnprotectResult * pIBProtectUnprotectResult;
operation->GetResults(&pIBProtectUnprotectResult);
IDataProtectionInfo * protectionValue;
pIBProtectUnprotectResult->get_ProtectionInfo(&protectionValue);
DataProtectionStatus status;
protectionValue->get_Status(&status);
if (status == DataProtectionStatus_Protected)
{
ComPtr<ICryptographicBufferStatics> cryptographicBuffer;
ABI::Windows::Foundation::GetActivationFactory
(HStringReference(RuntimeClass_Windows_Security_Cryptography_CryptographicBuffer).Get(), &cryptographicBuffer);
// "protectedDocumentBuffer is a global IBuffer.
cryptographicBuffer->ConvertBinaryToString(BinaryStringEncoding::BinaryStringEncoding_Utf8, protectedDocumentBuffer.Get(), textBoxSting);
protectedDocumentBuffer = nullptr;
}
}
}
return 0;
}
C++/ CX snippet
void OnProtectedAccessResumed(Platform::Object ^sender, Windows::Security::EnterpriseData::ProtectedAccessResumedEventArgs ^args)
{
auto unProtectTask = create_task(DataProtectionManager::UnprotectAsync(protectedDocumentBuffer));
unProtectTask.then([](BufferProtectUnprotectResult ^result)
{
if (result->ProtectionInfo->Status == DataProtectionStatus::Unprotected)
{
string_in_UI = CryptographicBuffer::ConvertBinaryToString(BinaryStringEncoding::Utf8, result->Buffer);
protectedDocumentBuffer = nullptr;
}
});
}
APIs:
ProtectionPolicyManager.ProtectedAccessResumed
ProtectionPolicyManager.Identity
DataProtectionManager.UnprotectAsync
BufferProtectUnprotectResult.Status
Handle enterprise data when protected content is revoked
If you want your app to be notified when the device is un-enrolled from MDM or when the policy administrator explicitly revokes access to enterprise data, handle the ProtectionPolicyManager_ProtectedContentRevoked event.
Indicate that your app is enlightened
To indicate that your app is enlightened, open the resource file of your project, add the following flag, and then recompile your app.
MICROSOFTEDPENLIGHTENEDAPPINFO EDPENLIGHTENEDAPPINFOID
BEGIN
0x0001
END