创建适用于银行的 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 应用商店应用通过使用 createRequestAsync 和 importPfxDataAsync 函数演示两种方式。
目标:续订证书
可以使用客户端生成的密钥或使用服务器生成的密钥进行证书续订,方法与新证书申请一样简单。 适用于银行的 Windows 应用商店应用通过使用 createRequestAsync 和 importPfxDataAsync 函数演示两种方式。
包设置和应用清单
通过编辑名为 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 会将其筛选掉。