Windows 信息保护 (WIP) 开发人员指南Windows Information Protection (WIP) developer guide

启发式应用可区分公司和个人数据,并且知道应基于管理员定义的 Windows 信息保护 (WIP) 策略保护哪些数据。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. 员工也愿意他们的个人数据在设备上保持不变,即使取消注册组织的移动设备管理 (MDM) 或完全退出组织也是如此。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.

注意 本指南有助于启发你创建 UWP 应用。Note This guide helps you enlighten a UWP app. 如果你想要启发 C++ Windows 桌面应用,请参阅 Windows 信息保护 (WIP) 开发人员指南 (C++)If you want to enlighten a C++ Windows desktop app, see Windows Information Protection (WIP) developer guide (C++).

可以在此处阅读有关 WIP 和启发式应用的详细信息:Windows 信息保护 (WIP)You can read more about WIP and enlightened apps here: Windows Information Protection (WIP).

可以在此处找到完整示例。You can find a complete sample here.

如果你已准备好完成每个任务,那我们开始吧。If you're ready to go through each task, let's start.

首先,收集所需内容First, gather what you need

你将需要以下内容:You'll need these:

  • 运行 Windows 10 版本 1607 或更高版本的测试虚拟机 (VM)。A test Virtual Machine (VM) that runs Windows 10, version 1607 or higher. 你将针对此测试 VM 调试你的应用。You'll debug your app against this test VM.

  • 运行 Windows 10 版本 1607 或更高版本的开发计算机。A development computer that runs Windows 10, version 1607 or higher. 这可能是你的测试 VM,前提是你已在其上安装了 Visual Studio。This could be your test VM if you have Visual Studio installed on it.

设置你的开发环境Setup your development environment

你将执行以下操作:You'll do these things:

在测试 VM 上安装 WIP 设置开发人员助手Install the WIP Setup Developer Assistant onto your test VM

使用此工具在测试 VM 上设置 Windows 信息保护策略。Use this tool to setup a Windows Information Protection policy on your test VM.

在此处下载工具:WIP 设置开发人员助手Download the tool here: WIP Setup Developer Assistant.

创建保护策略Create a protection policy

通过将信息添加到 WIP 设置开发人员助手中的每个部分定义你的策略。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.

设置 Visual Studio 项目Setup a Visual Studio project

  1. 在开发计算机上,打开你的项目。On your development computer, open your project.

  2. 添加对适用于通用 Windows 平台 (UWP) 的桌面和移动扩展的引用。Add a reference to the desktop and mobile extensions for Universal Windows Platform (UWP).

    添加 UWP 扩展

  3. 将此功能添加到程序包清单文件:Add this capability to your package manifest file:

       <rescap:Capability Name="enterpriseDataPolicy"/>
    

    可选阅读:“rescap”前缀表示受限功能Optional Reading: The "rescap" prefix means Restricted Capability. 请参阅特殊和受限功能See Special and restricted capabilities.

  4. 将此命名空间添加到程序包清单文件:Add this namespace to your package manifest file:

      xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
    
  5. 将该命名空间前缀添加到程序包清单文件的 <ignorableNamespaces> 元素。Add the namespace prefix to the <ignorableNamespaces> element of your package manifest file.

        <IgnorableNamespaces="uap mp rescap">
    

    这样,如果你的应用在不支持受限功能的 Windows 操作系统版本上运行,Windows 将忽略 enterpriseDataPolicy 功能。This way, if your app runs on a version of the Windows operating system that doesn't support restricted capabilities, Windows will ignore the enterpriseDataPolicy capability.

设置远程调试Setup remote debugging

如果你要在 VM 之外的计算机上开发你的应用,将仅在测试 VM 上安装 Visual Studio 远程工具。Install Visual Studio Remote Tools on your test VM only if you are developing your app on a computer other than your VM. 然后,在你的开发计算机上,启动远程调试程序,并查看你的应用是否在测试 VM 上运行。Then, on your development computer start the remote debugger and see if your app runs on the test VM.

请参阅远程电脑说明See Remote PC instructions.

将这些命名空间添加到代码文件Add these namespaces to your code files

将这些 using 语句添加到代码文件的顶部(本指南中的代码段,使用它们):Add these using statements to the top of your code files(The snippets in this guide use them):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Security.EnterpriseData;
using Windows.Web.Http;
using Windows.Storage.Streams;
using Windows.ApplicationModel.DataTransfer;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml;
using Windows.ApplicationModel.Activation;
using Windows.Web.Http.Filters;
using Windows.Storage;
using Windows.Data.Xml.Dom;
using Windows.Foundation.Metadata;
using Windows.Web.Http.Headers;

确定是否要在你的应用中使用 WIP APIDetermine whether to use WIP APIs in your app

确保运行应用的操作系统支持 WIP,并且在设备上启用了 WIP。Ensure that the operating system that runs your app supports WIP and that WIP is enabled on the device.

bool use_WIP_APIs = false;

if ((ApiInformation.IsApiContractPresent
    ("Windows.Security.EnterpriseData.EnterpriseDataContract", 3)
    && ProtectionPolicyManager.IsProtectionEnabled))
{
    use_WIP_APIs = true;
}
else
{
    use_WIP_APIs = false;
}

如果操作系统不支持 WIP,或者在设备上未启用 WIP,请不要调用 WIP API。Don't call WIP APIs if the operating system doesn't support WIP or 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 信息保护会为你的应用提供权限。Windows Information Protection gives your app permission if your app is on the protection policy's allowed list.

本节内容:In this section:

从文件中读取数据Read data from a file

步骤 1:获取文件句柄Step 1: Get the file handle

    Windows.Storage.StorageFolder storageFolder =
        Windows.Storage.ApplicationData.Current.LocalFolder;

    Windows.Storage.StorageFile file =
        await storageFolder.GetFileAsync(fileName);

步骤 2:确定你的应用是否可以打开该文件Step 2: Determine whether your app can open the file

调用 FileProtectionManager.GetProtectionInfoAsync 以确定你的应用是否可以打开该文件。Call FileProtectionManager.GetProtectionInfoAsync to determine whether your app can open the file.

FileProtectionInfo protectionInfo = await FileProtectionManager.GetProtectionInfoAsync(file);

if ((protectionInfo.Status != FileProtectionStatus.Protected &&
    protectionInfo.Status != FileProtectionStatus.Unprotected))
{
    return false;
}
else if (protectionInfo.Status == FileProtectionStatus.Revoked)
{
    // Code goes here to handle this situation. Perhaps, show UI
    // saying that the user's data has been revoked.
}

FileProtectionStatusProtected 表示文件受保护,并且你的应用可以打开它,因为你的应用在策略的允许列表中。A FileProtectionStatus value of Protected means that the file is protected and your app can open it because your app is on the policy's allowed list.

FileProtectionStatusUnProtected 表示文件不受保护,即使你的应用不在策略的允许列表中,你的应用仍然可以打开该文件。A FileProtectionStatus value of UnProtected means that the file is not protected and your app can open the file even your app is not on the policy's allowed list.

APIAPIs
FileProtectionManager.GetProtectionInfoAsyncFileProtectionManager.GetProtectionInfoAsync
FileProtectionInfoFileProtectionInfo
FileProtectionStatusFileProtectionStatus
ProtectionPolicyManager.IsIdentityManagedProtectionPolicyManager.IsIdentityManaged

步骤 3:将文件读取到流或缓冲区中Step 3: Read the file into a stream or buffer

将文件读取到流中Read the file into a stream

var stream = await file.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite);

将文件读取到缓冲区中Read the file into a buffer

var buffer = await Windows.Storage.FileIO.ReadBufferAsync(file);

从网络终结点读取数据Read data from a network endpoint

创建受保护的线程上下文,以从企业终结点读取。Create a protected thread context to read from an enterprise endpoint.

步骤 1:获取网络终结点的标识Step 1: Get the identity of the network endpoint

Uri resourceURI = new Uri("http://contoso.com/stockData.xml");

Windows.Networking.HostName hostName =
    new Windows.Networking.HostName(resourceURI.Host);

string identity = await ProtectionPolicyManager.
    GetPrimaryManagedIdentityForNetworkEndpointAsync(hostName);

如果终结点不由策略管理,你将得到一个空字符串。If the endpoint isn't managed by policy, you'll get back an empty string.

APIAPIs
ProtectionPolicyManager.GetPrimaryManagedIdentityForNetworkEndpointAsyncProtectionPolicyManager.GetPrimaryManagedIdentityForNetworkEndpointAsync

步骤 2:创建受保护的线程上下文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.

if (!string.IsNullOrEmpty(identity))
{
    using (ThreadNetworkContext threadNetworkContext =
            ProtectionPolicyManager.CreateCurrentThreadNetworkContext(identity))
    {
        return await GetDataFromNetworkRedirectHelperMethod(resourceURI);
    }
}
else
{
    return await GetDataFromNetworkRedirectHelperMethod(resourceURI);
}

此示例将套接字调用包含在 using 块中。This example encloses socket calls in a using block. 如果你不执行此操作,请确保你在检索资源后关闭线程上下文。If you don't do this, make sure that you close the thread context after you've retrieved your resource. 请参阅 ThreadNetworkContext.CloseSee ThreadNetworkContext.Close.

不要在受保护的线程上创建任何个人文件,因为这些文件将自动加密。Don't create any personal files on that protected thread because those files will be automatically encrypted.

无论终结点是否由策略管理,ProtectionPolicyManager.CreateCurrentThreadNetworkContext 方法都会返回一个 ThreadNetworkContext 对象。The ProtectionPolicyManager.CreateCurrentThreadNetworkContext method returns a ThreadNetworkContext object whether or not the endpoint is being managed by policy. 如果应用处理个人和企业资源,请为所有标识调用 ProtectionPolicyManager.CreateCurrentThreadNetworkContextIf your app handles both personal and enterprise resources, call ProtectionPolicyManager.CreateCurrentThreadNetworkContext for all identities. 获取资源后,处置 ThreadNetworkContext 以从当前线程中清除所有标识标记。After you get the resource, dispose the ThreadNetworkContext to clear any identity tag from the current thread.

APIAPIs
ProtectionPolicyManager.GetForCurrentViewProtectionPolicyManager.GetForCurrentView
ProtectionPolicyManager.IdentityProtectionPolicyManager.Identity
ProtectionPolicyManager.CreateCurrentThreadNetworkContextProtectionPolicyManager.CreateCurrentThreadNetworkContext

步骤 3:将资源读取到缓冲区中Step 3: Read the resource into a buffer

private static async Task<IBuffer> GetDataFromNetworkHelperMethod(Uri resourceURI)
{
    HttpClient client;

    client = new HttpClient();

    try { return await client.GetBufferAsync(resourceURI); }

    catch (Exception) { return null; }
}

(可选)使用标头令牌,而不是创建受保护的线程上下文(Optional) Use a header token instead of creating a protected thread context

public static async Task<IBuffer> GetDataFromNetworkbyUsingHeader(Uri resourceURI)
{
    HttpClient client;

    Windows.Networking.HostName hostName =
        new Windows.Networking.HostName(resourceURI.Host);

    string identity = await ProtectionPolicyManager.
        GetPrimaryManagedIdentityForNetworkEndpointAsync(hostName);

    if (!string.IsNullOrEmpty(identity))
    {
        client = new HttpClient();

        HttpRequestHeaderCollection headerCollection = client.DefaultRequestHeaders;

        headerCollection.Add("X-MS-Windows-HttpClient-EnterpriseId", identity);

        return await GetDataFromNetworkbyUsingHeaderHelperMethod(client, resourceURI);
    }
    else
    {
        client = new HttpClient();
        return await GetDataFromNetworkbyUsingHeaderHelperMethod(client, resourceURI);
    }

}

private static async Task<IBuffer> GetDataFromNetworkbyUsingHeaderHelperMethod(HttpClient client, Uri resourceURI)
{

    try { return await client.GetBufferAsync(resourceURI); }

    catch (Exception) { return null; }
}

处理页面重定向Handle page redirects

有时,Web 服务器将通信重定向到资源的更新版本。Sometimes a web server will redirect traffic to a more current version of a resource.

若要处理此情况,请发出请求,直到请求的响应状态的值为 OKTo handle this, make requests until the response status of your request has a value of OK.

然后使用该响应的 URI 获取终结点的标识。Then use the URI of that response to get the identity of the endpoint. 以下是执行此操作的一种方法:Here's one way to do this:

private static async Task<IBuffer> GetDataFromNetworkRedirectHelperMethod(Uri resourceURI)
{
    HttpClient client = null;

    HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter();
    filter.AllowAutoRedirect = false;

    client = new HttpClient(filter);

    HttpResponseMessage response = null;

        HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Get, resourceURI);
        response = await client.SendRequestAsync(message);

    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)
    {
        message = new HttpRequestMessage(HttpMethod.Get, message.RequestUri);
        response = await client.SendRequestAsync(message);

        try { return await response.Content.ReadAsBufferAsync(); }

        catch (Exception) { return null; }
    }
    else
    {
        try { return await response.Content.ReadAsBufferAsync(); }

        catch (Exception) { return null; }
    }
}

APIAPIs
ProtectionPolicyManager.GetPrimaryManagedIdentityForNetworkEndpointAsyncProtectionPolicyManager.GetPrimaryManagedIdentityForNetworkEndpointAsync
ProtectionPolicyManager.CreateCurrentThreadNetworkContextProtectionPolicyManager.CreateCurrentThreadNetworkContext
ProtectionPolicyManager.GetForCurrentViewProtectionPolicyManager.GetForCurrentView
ProtectionPolicyManager.IdentityProtectionPolicyManager.Identity

从剪贴板中读取数据Read data from the clipboard

获取从剪贴板中使用数据的权限Get permission to use data from the clipboard

若要从剪贴板中获取数据,请要求 Windows 提供权限。To get data from the clipboard, ask Windows for permission. 使用 DataPackageView.RequestAccessAsync 执行该操作。Use DataPackageView.RequestAccessAsync to do that.

public static async Task PasteText(TextBox textBox)
{
    DataPackageView dataPackageView = Clipboard.GetContent();

    if (dataPackageView.Contains(StandardDataFormats.Text))
    {
        ProtectionPolicyEvaluationResult result = await dataPackageView.RequestAccessAsync();

        if (result == ProtectionPolicyEvaluationResult..Allowed)
        {
            string contentsOfClipboard = await dataPackageView.GetTextAsync();
            textBox.Text = contentsOfClipboard;
        }
    }
}

APIAPIs
DataPackageView.RequestAccessAsyncDataPackageView.RequestAccessAsync

隐藏或禁用使用剪贴板数据的功能Hide or disable features that use clipboard data

确定当前视图是否有权获取剪贴板上的数据。Determine whether 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.

private bool IsClipboardAllowedAsync()
{
    ProtectionPolicyEvaluationResult protectionPolicyEvaluationResult = ProtectionPolicyEvaluationResult.Blocked;

    DataPackageView dataPackageView = Clipboard.GetContent();

    if (dataPackageView.Contains(StandardDataFormats.Text))

        protectionPolicyEvaluationResult =
            ProtectionPolicyManager.CheckAccess(dataPackageView.Properties.EnterpriseId,
                ProtectionPolicyManager.GetForCurrentView().Identity);

    return (protectionPolicyEvaluationResult == ProtectionPolicyEvaluationResult.Allowed |
        protectionPolicyEvaluationResult == ProtectionPolicyEvaluationResult.ConsentRequired);
}

APIAPIs
ProtectionPolicyEvaluationResultProtectionPolicyEvaluationResult
ProtectionPolicyManager.GetForCurrentViewProtectionPolicyManager.GetForCurrentView
ProtectionPolicyManager.IdentityProtectionPolicyManager.Identity

防止用户收到同意对话框提示Prevent users from being prompted with a consent dialog box

新文档不属于个人,也不属于企业A new document isn't personal or enterprise. 它只是新文档。It's just new. 如果用户将企业数据复制到该文档中,Windows 会强制执行策略,并且用户会收到同意对话框提示。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.

private async void PasteText(bool isNewEmptyDocument)
{
    DataPackageView dataPackageView = Clipboard.GetContent();

    if (dataPackageView.Contains(StandardDataFormats.Text))
    {
        if (!string.IsNullOrEmpty(dataPackageView.Properties.EnterpriseId))
        {
            if (isNewEmptyDocument)
            {
                ProtectionPolicyManager.TryApplyProcessUIPolicy(dataPackageView.Properties.EnterpriseId);
                string contentsOfClipboard = contentsOfClipboard = await dataPackageView.GetTextAsync();
                // add this string to the new item or document here.          

            }
            else
            {
                ProtectionPolicyEvaluationResult result = await dataPackageView.RequestAccessAsync();

                if (result == ProtectionPolicyEvaluationResult.Allowed)
                {
                    string contentsOfClipboard = contentsOfClipboard = await dataPackageView.GetTextAsync();
                    // add this string to the new item or document here.
                }
            }
        }
    }
}

APIAPIs
DataPackageView.RequestAccessAsyncDataPackageView.RequestAccessAsync
ProtectionPolicyEvaluationResultProtectionPolicyEvaluationResult
ProtectionPolicyManager.TryApplyProcessUIPolicyProtectionPolicyManager.TryApplyProcessUIPolicy

从“共享”合约读取数据Read data from a Share contract

当员工选择你的应用来共享他们的信息时,你的应用将打开包含该内容的新项目。When employees choose your app to share their information, your app will open a new item that contains that content.

正如我们之前提到的,新项目不属于个人,也不属于企业As we mentioned earlier, a new item isn't personal or enterprise. 它只是新文档。It's just new. 如果你的代码将企业内容添加到该项目,Windows 会强制执行策略,并且用户会收到同意对话框提示。If your code adds enterprise content to the item, Windows enforces policy and the user is prompted with a consent dialog. 此代码可防止此情况发生。This code prevents that from happening.

protected override async void OnShareTargetActivated(ShareTargetActivatedEventArgs args)
{
    bool isNewEmptyDocument = true;
    string identity = "corp.microsoft.com";

    ShareOperation shareOperation = args.ShareOperation;
    if (shareOperation.Data.Contains(StandardDataFormats.Text))
    {
        if (!string.IsNullOrEmpty(shareOperation.Data.Properties.EnterpriseId))
        {
            if (isNewEmptyDocument)
                // If this is a new and empty document, and we're allowed to access
                // the data, then we can avoid popping the consent dialog
                ProtectionPolicyManager.TryApplyProcessUIPolicy(shareOperation.Data.Properties.EnterpriseId);
            else
            {
                // In this case, we can't optimize the workflow, so we just
                // request consent from the user in this case.

                ProtectionPolicyEvaluationResult protectionPolicyEvaluationResult = await shareOperation.Data.RequestAccessAsync();

                if (protectionPolicyEvaluationResult == ProtectionPolicyEvaluationResult.Allowed)
                {
                    string text = await shareOperation.Data.GetTextAsync();

                    // Do something with that text.
                }
            }
        }
        else
        {
            // If the data has no enterprise identity, then we already have access.
            string text = await shareOperation.Data.GetTextAsync();

            // Do something with that text.
        }

    }

}

APIAPIs
ProtectionPolicyManager.RequestAccessAsyncProtectionPolicyManager.RequestAccessAsync
ProtectionPolicyEvaluationResultProtectionPolicyEvaluationResult
ProtectionPolicyManager.TryApplyProcessUIPolicyProtectionPolicyManager.TryApplyProcessUIPolicy

保护企业数据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, or through a share contract.

本节内容:In this section:

保护显示在页面中的数据Protect data that appears in pages

当你在页面中显示数据时,请通知 Windows 它所属的数据类型(个人还是企业)。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.

当你标记视图或进程时,Windows 将对它强制执行策略。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. 例如,在计算机上,用户可能使用 CTRL-V 从视图复制企业信息,然后将该信息粘贴到另一个应用中。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 可以防范此风险。Windows protects against that. Windows 还有助于强制执行“共享”合约。Windows also helps to enforce share contracts.

标记当前应用视图Tag the current app view

如果你的应用具有多个视图,其中一些视图使用企业数据,而另一些视图使用个人数据,请执行此操作。Do this if your app has multiple views where some views consume enterprise data and some consume personal data.


// 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.
ProtectionPolicyManager.GetForCurrentView().Identity = identity;

// tag as personal data.
ProtectionPolicyManager.GetForCurrentView().Identity = String.Empty;

APIAPIs
ProtectionPolicyManager.GetForCurrentViewProtectionPolicyManager.GetForCurrentView
ProtectionPolicyManager.IdentityProtectionPolicyManager.Identity

标记进程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.



// 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.
ProtectionPolicyManager.ClearProcessUIPolicy();

APIAPIs
ProtectionPolicyManager.TryApplyProcessUIPolicyProtectionPolicyManager.TryApplyProcessUIPolicy

将数据保护到文件Protect data to a file

创建受保护的文件,然后对该文件进行写入。Create a protected file and then write to it.

步骤 1:确定你的应用是否可以创建企业文件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.

  if (!ProtectionPolicyManager.IsIdentityManaged(identity)) return false;

APIAPIs
ProtectionPolicyManager.IsIdentityManagedProtectionPolicyManager.IsIdentityManaged

步骤 2:创建文件,并将其保护到标识Step 2: Create the file and protect it to the identity

StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
StorageFile storageFile = await storageFolder.CreateFileAsync("sample.txt",
    CreationCollisionOption.ReplaceExisting);

FileProtectionInfo fileProtectionInfo =
    await FileProtectionManager.ProtectAsync(storageFile, identity);

APIAPIs
FileProtectionManager.ProtectAsyncFileProtectionManager.ProtectAsync

步骤 3:将该流或缓冲区写入文件Step 3: Write that stream or buffer to the file

写入流Write a stream

    if (fileProtectionInfo.Status == FileProtectionStatus.Protected)
    {
        var stream = await storageFile.OpenAsync(FileAccessMode.ReadWrite);

        using (var outputStream = stream.GetOutputStreamAt(0))
        {
            using (var dataWriter = new DataWriter(outputStream))
            {
                dataWriter.WriteString(enterpriseData);
            }
        }

    }

写入缓冲区Write a buffer

     if (fileProtectionInfo.Status == FileProtectionStatus.Protected)
     {
         var buffer = Windows.Security.Cryptography.CryptographicBuffer.ConvertStringToBinary(
             enterpriseData, Windows.Security.Cryptography.BinaryStringEncoding.Utf8);

         await FileIO.WriteBufferAsync(storageFile, buffer);

      }

APIAPIs
FileProtectionInfoFileProtectionInfo
FileProtectionStatusFileProtectionStatus

将数据作为后台进程保护到文件Protect data to a file as a background process

当设备屏幕处于锁定状态时,此代码可以运行。This code can run while the screen of the device is locked. 如果管理员配置了安全的“锁屏下的数据保护”(DPL) 策略,则 Windows 会从设备内存中删除访问受保护的资源所需的加密密钥。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.

步骤 1:确定你是否可以创建企业文件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.

if (!ProtectionPolicyManager.IsIdentityManaged(identity)) return false;

APIAPIs
ProtectionPolicyManager.IsIdentityManagedProtectionPolicyManager.IsIdentityManaged

步骤 2:创建文件,并将其保护到标识Step 2: Create a file and protect it to the identity

FileProtectionManager.CreateProtectedAndOpenAsync 创建受保护的文件,并在你对其进行写入时使文件句柄保持打开状态。The FileProtectionManager.CreateProtectedAndOpenAsync creates a protected file and keeps the file handle open while you write to it.

StorageFolder storageFolder = ApplicationData.Current.LocalFolder;

ProtectedFileCreateResult protectedFileCreateResult =
    await FileProtectionManager.CreateProtectedAndOpenAsync(storageFolder,
        "sample.txt", identity, CreationCollisionOption.ReplaceExisting);

APIAPIs
FileProtectionManager.CreateProtectedAndOpenAsyncFileProtectionManager.CreateProtectedAndOpenAsync

步骤 3:将流或缓冲区写入文件Step 3: Write a stream or buffer to the file

此示例将流写入文件。This example writes a stream to a file.

if (protectedFileCreateResult.ProtectionInfo.Status == FileProtectionStatus.Protected)
{
    IOutputStream outputStream =
        protectedFileCreateResult.Stream.GetOutputStreamAt(0);

    using (DataWriter writer = new DataWriter(outputStream))
    {
        writer.WriteString(enterpriseData);
        await writer.StoreAsync();
        await writer.FlushAsync();
    }

    outputStream.Dispose();
}
else if (protectedFileCreateResult.ProtectionInfo.Status == FileProtectionStatus.AccessSuspended)
{
    // Perform any special processing for the access suspended case.
}

APIAPIs
ProtectedFileCreateResult.ProtectionInfoProtectedFileCreateResult.ProtectionInfo
FileProtectionStatusFileProtectionStatus
ProtectedFileCreateResult.StreamProtectedFileCreateResult.Stream

保护文件的一部分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. 例如,Microsoft Outlook 可以将企业邮件和个人邮件一起存储在单个存档文件中。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. 这样,即使用户从 MDM 注销或者他们的企业数据访问权限被吊销,他们仍然可以继续使用该文件。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.

步骤 1:将企业数据添加到加密的流或缓冲区Step 1: Add enterprise data to an encrypted stream or buffer

string enterpriseDataString = "<employees><employee><name>Bill</name><social>xxx-xxx-xxxx</social></employee></employees>";

var enterpriseData= Windows.Security.Cryptography.CryptographicBuffer.ConvertStringToBinary(
        enterpriseDataString, Windows.Security.Cryptography.BinaryStringEncoding.Utf8);

BufferProtectUnprotectResult result =
   await DataProtectionManager.ProtectAsync(enterpriseData, identity);

enterpriseData= result.Buffer;

APIAPIs
DataProtectionManager.ProtectAsyncDataProtectionManager.ProtectAsync
BufferProtectUnprotectResult.bufferBufferProtectUnprotectResult.buffer

步骤 2:将个人数据添加到未加密的流或缓冲区Step 2: Add personal data to an unencrypted stream or buffer

string personalDataString = "<recipies><recipe><name>BillsCupCakes</name><cooktime>30</cooktime></recipe></recipies>";

var personalData = Windows.Security.Cryptography.CryptographicBuffer.ConvertStringToBinary(
    personalDataString, Windows.Security.Cryptography.BinaryStringEncoding.Utf8);

步骤 3:将流或缓冲区写入文件Step 3: Write both streams or buffers to a file

StorageFolder storageFolder = ApplicationData.Current.LocalFolder;

StorageFile storageFile = await storageFolder.CreateFileAsync("data.xml",
    CreationCollisionOption.ReplaceExisting);

 // Write both buffers to the file and save the file.

var stream = await storageFile.OpenAsync(FileAccessMode.ReadWrite);

using (var outputStream = stream.GetOutputStreamAt(0))
{
    using (var dataWriter = new DataWriter(outputStream))
    {
        dataWriter.WriteBuffer(enterpriseData);
        dataWriter.WriteBuffer(personalData);

        await dataWriter.StoreAsync();
        await outputStream.FlushAsync();
    }
}

步骤 4:跟踪文件中的企业数据位置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 the 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.

此示例将该信息保存到单独的 XML 文件中。This example, saves that information to a separate XML file.

StorageFile metaDataFile = await storageFolder.CreateFileAsync("metadata.xml",
   CreationCollisionOption.ReplaceExisting);

await Windows.Storage.FileIO.WriteTextAsync
    (metaDataFile, "<EnterpriseDataMarker start='0' end='" + enterpriseData.Length.ToString() +
    "'></EnterpriseDataMarker>");

读取文件的受保护部分Read the protected part of a file

下面介绍如何从该文件中读取企业数据。Here's how you'd read the enterprise data out of that file.

步骤 1:获取文件中的企业数据位置Step 1: Get the position of your enterprise data in the file

Windows.Storage.StorageFolder storageFolder =
    Windows.Storage.ApplicationData.Current.LocalFolder;

 Windows.Storage.StorageFile metaDataFile =
   await storageFolder.GetFileAsync("metadata.xml");

string metaData = await Windows.Storage.FileIO.ReadTextAsync(metaDataFile);

XmlDocument doc = new XmlDocument();

doc.LoadXml(metaData);

uint startPosition =
    Convert.ToUInt16((doc.FirstChild.Attributes.GetNamedItem("start")).InnerText);

uint endPosition =
    Convert.ToUInt16((doc.FirstChild.Attributes.GetNamedItem("end")).InnerText);

步骤 2:打开数据文件并确保它不受保护Step 2: Open the data file and make sure that it's not protected

Windows.Storage.StorageFile dataFile =
    await storageFolder.GetFileAsync("data.xml");

FileProtectionInfo protectionInfo =
    await FileProtectionManager.GetProtectionInfoAsync(dataFile);

if (protectionInfo.Status == FileProtectionStatus.Protected)
    return false;

APIAPIs
FileProtectionManager.GetProtectionInfoAsyncFileProtectionManager.GetProtectionInfoAsync
FileProtectionInfoFileProtectionInfo
FileProtectionStatusFileProtectionStatus

步骤 3:从文件中读取企业数据Step 3: Read the enterprise data from the file

var stream = await dataFile.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite);

stream.Seek(startPosition);

Windows.Storage.Streams.Buffer tempBuffer = new Windows.Storage.Streams.Buffer(50000);

IBuffer enterpriseData = await stream.ReadAsync(tempBuffer, endPosition, InputStreamOptions.None);

步骤 4:解密包含企业数据的缓冲区Step 4: Decrypt the buffer that contains enterprise data

DataProtectionInfo dataProtectionInfo =
   await DataProtectionManager.GetProtectionInfoAsync(enterpriseData);

if (dataProtectionInfo.Status == DataProtectionStatus.Protected)
{
    BufferProtectUnprotectResult result = await DataProtectionManager.UnprotectAsync(enterpriseData);
    enterpriseData = result.Buffer;
}
else if (dataProtectionInfo.Status == DataProtectionStatus.Revoked)
{
    // Code goes here to handle this situation. Perhaps, show UI
    // saying that the user's data has been revoked.
}

APIAPIs
DataProtectionInfoDataProtectionInfo
DataProtectionManager.GetProtectionInfoAsyncDataProtectionManager.GetProtectionInfoAsync

将数据保护到文件夹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.

private async Task<bool> CreateANewFolderAndProtectItAsync(string folderName, string identity)
{
    if (!ProtectionPolicyManager.IsIdentityManaged(identity)) return false;

    StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
    StorageFolder newStorageFolder =
        await storageFolder.CreateFolderAsync(folderName);

    FileProtectionInfo fileProtectionInfo =
        await FileProtectionManager.ProtectAsync(newStorageFolder, identity);

    if (fileProtectionInfo.Status != FileProtectionStatus.Protected)
    {
        // Protection failed.
        return false;
    }
    return true;
}

在保护该文件夹前,确保它是空的。Make sure that the folder is empty before you protect it. 你无法保护已经包含项目的文件夹。You can't protect a folder that already contains items.

APIAPIs
ProtectionPolicyManager.IsIdentityManagedProtectionPolicyManager.IsIdentityManaged
FileProtectionManager.ProtectAsyncFileProtectionManager.ProtectAsync
FileProtectionInfo.IdentityFileProtectionInfo.Identity
FileProtectionInfo.StatusFileProtectionInfo.Status

将数据保护到网络终结点Protect data to a network end point

创建受保护的线程上下文以将该数据发送到企业终结点。Create a protected thread context to send that data to an enterprise endpoint.

步骤 1:获取网络终结点的标识Step 1: Get the identity of the network endpoint

Windows.Networking.HostName hostName =
    new Windows.Networking.HostName(resourceURI.Host);

string identity = await ProtectionPolicyManager.
    GetPrimaryManagedIdentityForNetworkEndpointAsync(hostName);

APIAPIs
ProtectionPolicyManager.GetPrimaryManagedIdentityForNetworkEndpointAsyncProtectionPolicyManager.GetPrimaryManagedIdentityForNetworkEndpointAsync

步骤 2:创建受保护的线程上下文,并将数据发送到网络终结点Step 2: Create a protected thread context and send data to the network endpoint

HttpClient client = null;

if (!string.IsNullOrEmpty(m_EnterpriseId))
{
    ProtectionPolicyManager.GetForCurrentView().Identity = identity;

    using (ThreadNetworkContext threadNetworkContext =
            ProtectionPolicyManager.CreateCurrentThreadNetworkContext(identity))
    {
        client = new HttpClient();
        HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Put, resourceURI);
        message.Content = new HttpStreamContent(dataToWrite);

        HttpResponseMessage response = await client.SendRequestAsync(message);

        if (response.StatusCode == HttpStatusCode.Ok)
            return true;
        else
            return false;
    }
}
else
{
    return false;
}

APIAPIs
ProtectionPolicyManager.GetForCurrentViewProtectionPolicyManager.GetForCurrentView
ProtectionPolicyManager.IdentityProtectionPolicyManager.Identity
ProtectionPolicyManager.CreateCurrentThreadNetworkContextProtectionPolicyManager.CreateCurrentThreadNetworkContext

保护你的应用通过“共享”合约共享的数据Protect data that your app shares through a share contract

如果你希望用户从应用共享内容,你将必须实现一个“共享”合约,并处理 DataTransferManager.DataRequested 事件。If you want users to share content from your app, you'll have to implement a share contract and handle the DataTransferManager.DataRequested event.

在事件处理程序中,在数据包中设置企业标识上下文。In your event handler, set the enterprise identity context in the data package.

private void OnShareSourceOperation(object sender, RoutedEventArgs e)
{
    // Register the current page as a share source (or you could do this earlier in your app).
    DataTransferManager.GetForCurrentView().DataRequested += OnDataRequested;
    DataTransferManager.ShowShareUI();
}

private void OnDataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
    if (!string.IsNullOrEmpty(this.shareSourceContent))
    {
        var protectionPolicyManager = ProtectionPolicyManager.GetForCurrentView();
        DataPackage requestData = args.Request.Data;
        requestData.Properties.Title = this.shareSourceTitle;
        requestData.Properties.EnterpriseId = protectionPolicyManager.Identity;
        requestData.SetText(this.shareSourceContent);
    }
}

APIAPIs
ProtectionPolicyManager.GetForCurrentViewProtectionPolicyManager.GetForCurrentView
ProtectionPolicyManager.IdentityProtectionPolicyManager.Identity

将你复制的文件保护到另一个位置Protect files that you copy to another location

private async void CopyProtectionFromOneFileToAnother
    (StorageFile sourceStorageFile, StorageFile targetStorageFile)
{
    bool copyResult = await
        FileProtectionManager.CopyProtectionAsync(sourceStorageFile, targetStorageFile);

    if (!copyResult)
    {
        // Copying failed. To diagnose, you could check the file's status.
        // (call FileProtectionManager.GetProtectionInfoAsync and
        // check FileProtectionInfo.Status).
    }
}

APIAPIs
FileProtectionManager.CopyProtectionAsyncFileProtectionManager.CopyProtectionAsync

在设备屏幕处于锁定状态时保护企业数据Protect enterprise data when the screen of the device 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.

处理 ProtectionPolicyManager.ProtectedAccessSuspending 事件,以便你的应用知道屏幕何时锁定。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 将临时删除设备上预配的数据保护密钥。Windows temporarily removes the data protection keys that are provisioned on the device. Windows 将删除这些密钥以确保在设备处于锁定状态并且可能不在其所有者的掌控下时,无法对加密数据进行未经授权的访问。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.

处理 ProtectionPolicyManager.ProtectedAccessResumed 事件,以便你的应用知道屏幕何时解锁。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 textblock to an encrypted buffer and removes the content from that textblock.

private async void ProtectionPolicyManager_ProtectedAccessSuspending(object sender, ProtectedAccessSuspendingEventArgs e)
{
    Deferral deferral = e.GetDeferral();

    if (ProtectionPolicyManager.GetForCurrentView().Identity != String.Empty)
    {
        IBuffer documentBodyBuffer = CryptographicBuffer.ConvertStringToBinary
           (documentTextBlock.Text, BinaryStringEncoding.Utf8);

        BufferProtectUnprotectResult result = await DataProtectionManager.ProtectAsync
            (documentBodyBuffer, ProtectionPolicyManager.GetForCurrentView().Identity);

        if (result.ProtectionInfo.Status == DataProtectionStatus.Protected)
        {
            this.protectedDocumentBuffer = result.Buffer;
            documentTextBlock.Text = null;
        }
    }

    // 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.Complete();
}

APIAPIs
ProtectionPolicyManager.ProtectedAccessSuspendingProtectionPolicyManager.ProtectedAccessSuspending
ProtectionPolicyManager.GetForCurrentViewProtectionPolicyManager.GetForCurrentView
ProtectionPolicyManager.IdentityProtectionPolicyManager.Identity
DataProtectionManager.ProtectAsyncDataProtectionManager.ProtectAsync
BufferProtectUnprotectResult.bufferBufferProtectUnprotectResult.buffer
ProtectedAccessSuspendingEventArgs.GetDeferralProtectedAccessSuspendingEventArgs.GetDeferral
Deferral.CompleteDeferral.Complete

当设备解锁时,添加回敏感数据Add back sensitive data when the device is unlocked

当设备解锁并且密钥在设备上再次可用时,将引发 ProtectionPolicyManager.ProtectedAccessResumedProtectionPolicyManager.ProtectedAccessResumed is raised when the device is unlocked and the keys are available on the device again.

如果管理员未配置锁屏下的安全数据保护策略,则 ProtectedAccessResumedEventArgs.Identities 是空的集合。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 textbox and then disposes of the buffer.

private async void ProtectionPolicyManager_ProtectedAccessResumed(object sender, ProtectedAccessResumedEventArgs e)
{
    if (ProtectionPolicyManager.GetForCurrentView().Identity != String.Empty)
    {
        BufferProtectUnprotectResult result = await DataProtectionManager.UnprotectAsync
            (this.protectedDocumentBuffer);

        if (result.ProtectionInfo.Status == DataProtectionStatus.Unprotected)
        {
            // Restore the unprotected version.
            documentTextBlock.Text = CryptographicBuffer.ConvertBinaryToString
                (BinaryStringEncoding.Utf8, result.Buffer);
            this.protectedDocumentBuffer = null;
        }
    }

}

APIAPIs
ProtectionPolicyManager.ProtectedAccessResumedProtectionPolicyManager.ProtectedAccessResumed
ProtectionPolicyManager.GetForCurrentViewProtectionPolicyManager.GetForCurrentView
ProtectionPolicyManager.IdentityProtectionPolicyManager.Identity
DataProtectionManager.UnprotectAsyncDataProtectionManager.UnprotectAsync
BufferProtectUnprotectResult.StatusBufferProtectUnprotectResult.Status

当吊销受保护的内容时处理企业数据Handle enterprise data when protected content is revoked

如果你希望你的应用在设备从 MDM 注销或策略管理员显式吊销对企业数据的访问权限时收到通知,请处理 ProtectionPolicyManager_ProtectedContentRevoked 事件。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.

此示例确定电子邮件应用的企业邮箱中的数据是否已吊销。This example determines if the data in an enterprise mailbox for an email app has been revoked.

private string mailIdentity = "contoso.com";

void MailAppSetup()
{
    ProtectionPolicyManager.ProtectedContentRevoked += ProtectionPolicyManager_ProtectedContentRevoked;
    // Code goes here to set up mailbox for 'mailIdentity'.
}

private void ProtectionPolicyManager_ProtectedContentRevoked(object sender, ProtectedContentRevokedEventArgs e)
{
    if (!new System.Collections.Generic.List<string>(e.Identities).Contains
        (this.mailIdentity))
    {
        // This event is not for our identity.
        return;
    }

    // Code goes here to delete any metadata associated with 'mailIdentity'.
}

APIAPIs
ProtectionPolicyManager_ProtectedContentRevokedProtectionPolicyManager_ProtectedContentRevoked

Windows 信息保护 (WIP) 示例Windows Information Protection (WIP) sample