创建适用于银行的 Windows 应用商店应用:代码演练(使用 JavaScript 和 HTML 的 Windows 应用商店应用)

此示例演示如何创建简单的适用于银行的 Windows 应用商店应用,该应用将通过 Internet 执行安全身份验证和通信。你可将此示例中的代码用于任何需要与服务器进行安全交互的 Windows 应用商店应用。

以下部分介绍适用于银行的 Windows 应用商店应用中支持安全身份验证的部分。

先决条件

Microsoft Visual Studio Express 2012 for Windows 8

Microsoft Visual Studio Professional 2012

Microsoft Visual Studio Ultimate 2012

开发人员许可证

注意  若要运行银行应用,则需要提供用来与服务器通信的终结点的 Web 服务。以下主题介绍用于身份验证、证书注册和证书续订的 System.Web.Mvc 控制器实现。

下载位置

可从以下位置下载完整的代码示例:采用强身份验证的 Windows 应用商店应用

运行该应用

现有的适用于银行的 Web 应用普遍采用基于证书的身份验证,而适用于银行的 Windows 应用商店应用都是在此模型中创建的。首次运行该应用并且使用用户名和密码登录,这将提供对你的帐户的有限访问权限。然后关闭该应用并进行重新启动,此次选择升级至证书。最后关闭应用并重新启动,在单击“Sign in to your account”(登录帐户)后,无需输入凭据即可拥有对帐户的完全访问权限。

基于密码的身份验证

首先执行以下步骤。

  • 启动银行应用程序。

  • 单击“Sign in to your account”(登录账户)****。

  • 使用用户名和密码(可以是任何非空字符串)登录,然后单击“提交”。

    帐户页面提示:根据基于密码的身份验证,你具有有限的访问权限。

升级到基于证书的身份验证

从用户名/密码凭据升级至基于证书的身份验证后,帐户即可拥有完全访问权限。

  • 关闭该应用并重新启动。

  • 单击“Upgrade to stronger authentication”(升级至更强的身份验证)****。

  • 该应用要求升级并解释升级的好处。

  • 选中“服务器生成的密钥(PFX 安装)”,然后单击“确定”****。

    该应用将注册证书并对用户进行身份验证。

使用基于证书的身份验证访问帐户

关闭应用。在重新启动该启用时,无需使用用户名和密码登录。

  • 再次启动该应用,单击“登录到你的帐户”。

    由于提供了基于应用的证书,因此使用该证书进行身份验证。系统显示一个对话框,用来确认证书。对你进行身份验证后,帐户页面提示你有完全访问权限。

目标:安全地存储用户登录凭据

通过使用 PasswordVault,可以在 Windows 应用商店应用中安全地存储用户名和密码凭据。

目标:设置安全应用清单

通过编辑 Windows 应用商店应用的应用包清单,可以指定该应用的功能和证书。

目标:登录服务器

适用于银行的 Windows 应用商店应用将展示如何处理用户登录以进行在线银行会话。服务器将检查用户的证书,而该应用根据结果显示适当的页面。

目标:申请证书

可以选择使用客户端生成的密钥或使用服务器生成的密钥申请证书。 适用于银行的 Windows 应用商店应用通过使用 createRequestAsyncimportPfxDataAsync 函数演示两种方式。

目标:续订证书

可以使用客户端生成的密钥或使用服务器生成的密钥进行证书续订,方法与新证书申请一样简单。 适用于银行的 Windows 应用商店应用通过使用 createRequestAsyncimportPfxDataAsync 函数演示两种方式。

包设置和应用清单

通过编辑名为 dpackage.appxmanifest 或 AppxManifest.xml 的应用清单文件,可以控制 Windows 应用商店应用的部署。可以使用 Visual Studio Express 2012 for Windows 8 将设置应用于清单文件,也可以直接编辑该文件。

在解决方案资源管理器中,双击 package.appxmanifest 以打开清单设计器。

包设置:应用 UI 属性

在“应用程序 UI”页面中,设置以下属性,如下所示。

属性
起始页 default.html
显示名称 所有徽标
短名称 TBA
前景文本 黑色
背景颜色 #000000
锁屏通知 (未设置)
初始旋转 纵向

 

包设置:应用功能

在“功能”页面中,选中“Internet (客户端)”和“专用网络(客户端和服务器)”。若要使用共享证书,请确保选中“共享用户证书”。 有关更多信息,请参阅设置证书存储功能

注意  要将智能卡用于银行应用,必须指定应用清单中的“sharedUserCertificates”功能。

以下示例说明如何在应用清单中声明应用的功能。该代码位于 package.appxmanifest 中。

<Capabilities>
   <Capability Name="privateNetworkClientServer" />
   <Capability Name="internetClient" />
<!--   <Capability Name="sharedUserCertificates"/>-->
 </Capabilities>

包设置:证书声明

可以使用证书声明分配受信任的根证书和其他数字证书。 以下过程描述如何使用声明设计器添加新的根证书。有关详细信息,请参阅使用清单安装证书

如果“证书”****未在“支持的声明”下列出,则使用以下步骤添加根证书。

  • 打开“声明”****页面。

  • 在“可用声明”下拉列表中,选择“证书”****,然后单击“添加”。

    随即将打开证书设计器。

  • TrustFlags 下,确保选中 ExclusiveTrust

  • 在“选择条件”下,确保选中“自动选择”****。

  • 在“证书”下,单击“添加新”****。

  • StoreName 文本框中,键入 Root

  • 在“内容”文本框中,键入 myroot.cer

以下示例说明如何分配根和开发人员证书。该代码位于 package.appxmanifest 中。

<Extensions>
   <Extension Category="windows.certificates">
     <Certificates>
       <Certificate StoreName="Root" Content="myroot.cer" />
       <Certificate StoreName="CA" Content="mystandca.cer" />
       <TrustFlags ExclusiveTrust="true" />
       <SelectionCriteria AutoSelect="true" />
     </Certificates>
   </Extension>
 </Extensions>

包设置:包装属性

在“包装”页面中,将“包名称”****设置为 Microsoft.SDKSamples.TailoredBankAppShare 并且将“包显示名称”设置为 TailoredBankAppShare SDK Sample

使用用户名和密码登录服务器

使用 PasswordVault 存储和检索安全应用程序的密码凭据。为了保证最佳安全性能,银行应用建议用户升级到基于证书的身份验证。

以下示例说明如何使用用户名和密码凭据登录到安全应用。该代码位于 scenario0.js 中。

//account logon
// onClick() handler for the 'submitButton'
function logon() {
    try {
        var username = document.getElementById("username").value;
        var password = document.getElementById("password").value;

        //store user credential in the vault component
        if (username && password) {
            var cred = new Windows.Security.Credentials.PasswordCredential("WoodGrove-Bank-usercred", username, password);
            var vault = new Windows.Security.Credentials.PasswordVault();
            vault.add(cred);
        }else {
            return false;
        }
    }
    catch (err) {
        var message = '';
        for (var f in err) {
            message += f;
            message += ':';
            message += err[f];
            message += ' ';
        }
        goError(message);
        return false;
    }

    var params = "UserName=" + username;
    params += "&Password=" + password;

    WinJS.xhr({
        type: "POST",
        url: "Your URL",    //Please provide the server url here. For example:
                            //url: "https://WoodGrove-Bank/bankserver2/account/simplelogon",
        headers: { "Content-type": "application/x-www-form-urlencoded" },
        data: params
    }).then(
        function (request) {
            var obtainedData = window.JSON.parse(request.responseText);
            var /*@override*/ item = {
                title: "Account Page",
                content: "<strong>(Please upgrade to strong authentication if you need full access to your account.)",
                backgroundColor: 'rgba(25,50,200,1)',
                navigate: "groupeditemsPage"
            };
            WinJS.Navigation.navigate('/html/account.html', { item: item });
        },
        function (request) {
            goError("(The error was: <strong>" + request.message + "</strong>) <br>" 
                    + "The server URL you are using may not be valid. <br>"
                    + "Please contact your bank server service, "
                    + "or refer to the bank server walk through document for instructions to setup your own server.");
            return false;
        }
    );
};

用户名和密码将添加到 PasswordVault 中,并使用 xhr 函数将其发送到 Web 服务的 simplelogon 终结点。如果申请成功,则显示 account.html。

申请证书并进行注册

为了保证最佳安全性能,银行应用建议用户升级到基于证书的身份验证。你可以在客户端应用中创建证书,也可以从服务器中导入个人信息交换 (PFX) 证书。在这两种情况下,都可以通过使用 CertificateEnrollmentManager 对象进行注册。

向服务器提交证书申请

Windows 应用商店应用可以向运行 Microsoft 证书服务的证书颁发机构提交证书申请,你可以使用 CertificateEnrollmentManager 安装证书颁发机构颁发的证书。有关详细信息,请参阅提交证书申请和安装证书响应

以下示例演示如何申请带有客户端生成的密钥的证书。该代码位于 enrollment.js 中。

//pkcs10 certification enrollment
function createRequestBlobAndEnroll(user)
{
    var encoded;
    try {
        //WinRT APIs for creating a certficate request
        var request = new Windows.Security.Cryptography.Certificates.CertificateRequestProperties;
        request.subject = user;
        request.friendlyName = user + "'s WoodGrove Bank Certificate";
        request.keyProtectionLevel = Windows.Security.Cryptography.Certificates.KeyProtectionLevel.noConsent;
        Windows.Security.Cryptography.Certificates.CertificateEnrollmentManager.createRequestAsync(request).then(
            function (requestResult) {
                encoded = requestResult;
                   var installOption = 0; //Windows.Security.Cryptography.Certificates.InstallOptions.none;
                var params = "username=" + user;
                params += "&request=" + encodeURIComponent(encoded);
            
                WinJS.xhr({
                    type: "POST",
                    url: "Your URL",    //Please provide the server url here. For example:
                                        //url: "https://WoodGrove-Bank/bankserver2/enrollment/submit",
                    headers: { "Content-type": "application/x-www-form-urlencoded" },
                    data: params
                }).then(
                    function (/*@override*/request) {
                        var obtainedData = window.JSON.parse(request.responseText);
                        try {
                            // WinRT API for certficate enrollment
                            Windows.Security.Cryptography.Certificates.CertificateEnrollmentManager
                                .installCertificateAsync(obtainedData.certificate, installOption ).then(
                            function() {
                                // set certificate enrollment mark to aviod redundant certificate enrollments
                                Windows.Storage.ApplicationData.current.localSettings.values["EnrollCertificate"] = true;

                                var /*@override*/ item = {
                                        title: "Account Page",
                                        content: 
"<strong>(Please sign out and re-launch the application so as to use your enrolled certificate.)<strong>",
                                        backgroundColor: 'rgba(191, 84, 46, 1)',
                                        navigate: "sign-out"
                                };
                                WinJS.Navigation.navigate('/html/account.html', { item: item });
                                return false;
                            },
                            function(installError) {
                                gotoError("(The error was: <strong>" + intallError.message + "</strong>)");
                                return false;
                            });
                        } catch (err) {
                            var /*@override*/ message = '';
                            for (var /*@override*/ f in err) {
                            message += f;
                            message += ':';
                            message += err[f];
                            message += ' ';
                        }
                            goError(message);
                            return false;
                        }
                    },
                    function (/*@override*/request) {
                        goError("(The error was: <strong>" + request.message + "</strong>) <br>" 
                        + "The server URL you are using may not be valid. <br>"
                        + "Please contact your bank server service, "
                        + "or refer to the bank server topic for instructions to setup your own server.");
                        return false;
                    });
            },
            function (requestError) {
                gotoError("(The error was: <strong>" + requestError.message + "</strong>)");
                return false;
            });
    }
    catch (err) {
        var message = '';
        for (var f in err) {
            message += f;
            message += ':';
            message += err[f];
            message += ' ';
        }
        goError(message);
        return false;
    };
}

createRequestBlobAndEnroll 函数使用 CertificateEnrollmentManager 对象生成证书请求并将其提交给服务器。如果证书请求成功,installCertificateAsync 函数会安装所提供的证书。

使用客户端生成的密钥续订

续订证书的过程与第一次安装证书的过程基本相同,因为 Windows 8 会筛选掉旧证书。

以下示例演示如何申请带有客户端生成的密钥的证书。该代码位于 groupedItemsPage.js 中。

//pkcs10 certificate renewal
function createRequestBlobAndEnroll(user) {
    var encoded;
    try {
        //WinRT APIs for creating a certficate request
        var request = new Windows.Security.Cryptography.Certificates.CertificateRequestProperties;
        request.subject = user;
        request.friendlyName = user + "'s WoodGrove Bank Certificate";
        request.keyProtectionLevel = Windows.Security.Cryptography.Certificates.KeyProtectionLevel.noConsent;
        Windows.Security.Cryptography.Certificates.CertificateEnrollmentManager.createRequestAsync(request).then(
            function (requestResult) {
                encoded = requestResult;
                // No username or password required in this case - we already have a
                // client-authenticated SSL connection
                var installOption = 0; //Windows.Security.Cryptography.Certificates.InstallOptions.none;
                var params = "request=" + encodeURIComponent(encoded);
                WinJS.xhr({
                    type: "POST",
                    url: "Your URL",    //Please provide the server url here. For example:
                                        //url: "https://WoodGrove-Bank/bankserver2/renewal/renewP10",
                    headers: { "Content-type": "application/x-www-form-urlencoded" },
                    data: params
                }).then(
                    function (/*@override*/request) {
                        var obtainedData = window.JSON.parse(request.responseText);
                        // We do not need to remove the previous certificate first -
                        // Win8 filtering will take care of it.
                        try {
                            Windows.Security.Cryptography.Certificates.CertificateEnrollmentManager
                                .installCertificateAsync(obtainedData.certificate, installOption).then(
                            function () {
                                var /*@override*/ item = {
                                    title: "Account Page",
                                    content: "",
                                    backgroundColor: 'rgba(25,50,200,1)',
                                    navigate: "sign-out"
                                };
                                WinJS.Navigation.navigate('/html/account.html', { item: item });
                            },
                            function (installError) {
                                gotoError("(The error was: <strong>" + intallError.message + "</strong>)");
                                return false;
                            });
                        } catch (err) {
                            var /*@override*/ message = '';
                            for (var /*@override*/ f in err) {
                                message += f;
                                message += ':';
                                message += err[f];
                                message += ' ';
                            };
                            goError(message);
                            return false;
                        };
                    },
                    function (/*@override*/request) {
                        goError("(The error was: <strong>" + request.message + "</strong>) <br>" 
                        + "The server URL you are using may not be valid. <br>"
                        + "Please contact your bank server service, "
                        + "or refer to the bank server walk through document for instructions to setup your own server.");
                        return false;
                    });
            },
            function (requestError) {
                gotoError("(The error was: <strong>" + requestError.message + "</strong>)");
                return false;
            });
    }
    catch (err) {
        var message = '';
        for (var f in err) {
            message += f;
            message += ':';
            message += err[f];
            message += ' ';
        }
        goError(message);
        return false;
    };
}

createRequestBlobAndEnroll 函数使用 CertificateEnrollmentManager 对象生成证书请求并将其提交给服务器。如果证书请求成功,installCertificateAsync 函数会安装所提供的证书。无需删除旧证书,因为 Windows 8 会将其筛选掉。

检查证书

可以查询证书的状态并根据结果显示不同的页面。

以下示例说明如何检查证书并根据结果显示不同的页面。该代码位于 groupedItemsPage.js 中。

//check certification
function checkCert(senario, scenarioitem) {
    var params = "";
    WinJS.xhr({
        type: "POST",
        url: "Your URL",    //Please provide the server url here. For example:
                            //url: "https://WoodGrove-Bank/bankserver2/renewal/CheckCert",
        headers: { "Content-type": "application/x-www-form-urlencoded" },
        data: params
    }).then(
        function (request) {
            var obtainedData = window.JSON.parse(request.responseText);
            if (obtainedData.hasCert === true) {
                if (obtainedData.renew === true) {
                    if (obtainedData.pfx === true) {
                        doPFXRenewal(obtainedData.user);
                    }
                    else {
                        doPKCS10Renewal(obtainedData.user);
                    }
                }
                else {
                    var /*@override*/ item = {
                        title: "Account Page",
                        content: "",
                        backgroundColor: 'rgba(25,50,200,1)',
                        navigate: "sign-out"
                    };
                    WinJS.Navigation.navigate('/html/account.html', { item: item });
                }
            }
            else {
                switch (senario) {
                    case 0:
                        if (checkCredential()) {
                            var item2 = {
                                title: "Account Page",
                                content: 
"<strong>(Please upgrade to strong authentication if you need full access to your account. <br>"
+ "If you already upgraded to use strong authentication, please sign out and launch the app again and sign-in.)</strong>",
                                backgroundColor: 'rgba(191, 84, 46, 1)',
                                navigate: "groupeditemsPage"
                            };
                            WinJS.Navigation.navigate('/html/account.html', { item: item2 });
                        } else {
                            WinJS.Navigation.navigate('/html/scenario0.html', { item: scenarioitem });
                        }
                        break;
                    case 1:
                        if (checkCredential() && !checkEnrollment()) {
                            WinJS.Navigation.navigate('/html/enrollment.html', { item: scenarioitem });
                        }
                        else if (!checkEnrollment()) {
                            WinJS.Navigation.navigate('/html/scenario1.html', { item: scenarioitem });
                        }
                        else {
                            var item3 = {
                                title: "Account Page",
                                content: 
"<strong>(You already upgraded to use strong authentication. Please sign out and launch the app again and sign-in.)</strong>",
                                backgroundColor: 'rgba(191, 84, 46, 1)',
                                navigate: "sign-out"
                            };
                            WinJS.Navigation.navigate('/html/account.html', { item: item3 });
                        }
                        break;
                    default:
                        goError("unknown scenario!!!");
                        break;
                }
            }
            return false;
        },
        function (request) {
            goError("(The error was: <strong>" + request.message + "</strong>) <br>" 
                    + "The server URL you are using may not be valid. <br>"
                    + "Please contact your bank server service, "
                    + "or refer to the bank server walk through document for instructions to setup your own server.");
            return false;
        }
    );
}

checkCert 函数查询 Web 服务的 CheckCert 终结点并分析响应。如果响应为证书,则该应用会检查该证书是否需要续订,如果需要,则该应用会调用相应的续订函数。否则,显示强身份验证帐户页面。

升级到基于证书的身份验证

为了保证最佳安全性能,银行应用建议用户升级到基于证书的身份验证。你可以在客户端应用中创建证书,也可以从服务器中导入个人信息交换 (PFX) 证书。在这两种情况下,都可以通过使用 CertificateEnrollmentManager 对象进行注册。

从服务器导入 PFX 证书

以下示例说明如何从服务器导入 PFX 证书。该代码位于 enrollment.js 中。

// pfx certificate enrollment
function doPFXEnrollment(user) {

    var exportable = Windows.Security.Cryptography.Certificates.ExportOption.exportable;
    var consent = Windows.Security.Cryptography.Certificates.KeyProtectionLevel.noConsent;
    var installOption = 0; //Windows.Security.Cryptography.Certificates.InstallOptions.none;
    var params = "username=" + user;

    WinJS.xhr({
        type: "POST",
        url: "Your URL",    //Please provide the server url here. For example:
                            //url: "https://WoodGrove-Bank/bankserver2/enrollment/getPFX",
        headers: { "Content-type": "application/x-www-form-urlencoded" },
        data: params
    }).then(
        function (request) {
            var obtainedData = window.JSON.parse(request.responseText);
            try {
                // import pfx certificate file into the app container
                Windows.Security.Cryptography.Certificates.CertificateEnrollmentManager
                    .importPfxDataAsync(obtainedData.pfx, obtainedData.password, exportable, consent, 
                        installOption, obtainedData.friendlyName).then(
                    function () {
                // set certificate enrollment mark to aviod redundant certificate enrollments
                Windows.Storage.ApplicationData.current.localSettings.values["EnrollCertificate"] = true;
                var /*@override*/ item = {
                    title: "Account Page",
                    content: 
"<strong>(Please sign out and re-launch the application so as to use your enrolled certificate.)</strong>",
                    backgroundColor: 'rgba(191, 84, 46, 1)',
                    navigate: "sign-out"
                };
                WinJS.Navigation.navigate('/html/account.html', { item: item });
                return;
                    },
                    function(importError) {
                        gotoError("(The error was: <strong>" + requestError.message + "</strong>)");
                        return false;
                    }); 
            } catch (err) {
                var message = '';
                for (var f in err) {
                    message += f;
                    message += ':';
                    message += err[f];
                    message += ' ';
                }
                goError(message);
                return false;
            }
        },
        function (request) {
            goError("(The error was: <strong>" + request.message + "</strong>) <br>" 
                    + "The server URL you are using may not be valid. <br>"
                    + "Please contact your bank server service, "
                    + "or refer to the bank server walk through document for instructions to setup your own server.");
            return false;
        }
    );
}

doPFXEnrollment 函数尝试连接至 Web 服务的 getPFX 终结点。如果连接成功,则 CertificateEnrollmentManager 对象导入所提供的证书并在客户端计算机上安装该证书。

续订 PFX 证书

续订个人信息交换 (PFX) 证书的过程与第一次安装证书的过程基本相同,因为 Windows 8 会筛选掉旧证书。

以下示例说明如何续订 PFX 证书。该代码位于 groupedItemsPage.js 中。

//pfx certificate renewal
function doPFXRenewal(user) {
    var exportable = Windows.Security.Cryptography.Certificates.ExportOption.exportable;
    var consent = Windows.Security.Cryptography.Certificates.KeyProtectionLevel.noConsent;
    var installOption = 0; //Windows.Security.Cryptography.Certificates.InstallOptions.none;
    var params = "username=" + user;
    WinJS.xhr({
        type: "POST",
        url: "Your URL",    //Please provide the server url here. For example:
                            //url: "https://WoodGrove-Bank/bankserver2/enrollment/getPFX",
        headers: { "Content-type": "application/x-www-form-urlencoded" },
        data: params
    }).then(
        function (request) {
            var obtainedData = window.JSON.parse(request.responseText);
            // We do not need to remove the previous certificate first -
            // Win8 filtering will take care of it.
            try {
                // No need to remove old cert, Windows will filter.
                Windows.Security.Cryptography.Certificates.CertificateEnrollmentManager
                    .importPfxDataAsync(obtainedData.pfx, obtainedData.password, exportable, consent, 
                        installOption, obtainedData.friendlyName).then(
                    function () {
                        var /*@override*/ item = {
                            title: "Account Page",
                            content: "",
                            backgroundColor: 'rgba(25,50,200,1)',
                            navigate: "sign-out"
                        };
                        WinJS.Navigation.navigate('/html/account.html', { item: item });
                    },
                    function (importError) {
                        gotoError("(The error was: <strong>" + importError.message + "</strong>)");
                        return false;
                    });
            } catch (err) {
                var message = '';
                for (var f in err) {
                    message += f;
                    message += ':';
                    message += err[f];
                    message += ' ';
                };
                goError(message);
                return false;
            }

        },
        function (request) {
            goError("(The error was: <strong>" + request.message + "</strong>) <br>" 
                    + "The server URL you are using may not be valid. <br>"
                    + "Please contact your bank server service, "
                    + "or refer to the bank server walk through document for instructions to setup your own server.");
            return false;
        }
    );
}

doPFXRenewal 函数向服务器传送 PFX 证书的申请。如果传送成功,则 importPfxDataAsync 函数导入所提供的证书并在客户端计算机上安装该证书。无需删除旧证书,因为 Windows 8 会将其筛选掉。

相关主题

CertificateEnrollmentManager

PasswordVault

使用证书

创建适用于银行的 Windows 应用商店应用

创建适用于银行的 Windows 应用商店应用:资源