摘要:本文高度概括了 Windows CE 5.0 中为设备和应用程序提供安全性的不同选项。本文假定读者熟悉基本的 Microsoft 安全技术和 Win32 应用程序编程接口 (API)。
安全服务是现代操作系统的核心部分。网络基础结构、系统管理策略和最终用户的感受都取决于安全服务的管理功能、灵活性和实施。Microsoft® Windows® CE 5.0 通过提供安全服务的集成功能集,使企业在日益增长的网络世界中不断扩展,且无须牺牲安全性。安全服务包括以下功能:
Windows CE 设备发送、接收和处理需要保护(以避免潜在的不安全应用程序)的信息。那么,如何保护您的设备呢?您可以创建一个安全操作系统 (OS),防止加载未知模块、限制对系统 API 的访问、并禁止对系统注册表的某些部分执行写入操作。
要创建信任环境,必须实现下面两个函数:
在内核加载应用程序之前,OEMCertifyModule 函数验证应用程序签名,以避免非法应用程序进入您的系统。这可以确保仅当应用程序包含有效的数字签名时,才能够被基于 Windows CE 的平台加载。OEMCertifyModule 函数返回以下三个选项之一:
下表对这两个函数加以说明。
函数 | 说明 | 返回值 |
OEMCertifyModuleInit | 启用 OS 加载程序来通知 OEM 有一个新的模块正在加载。允许 OEM 决定是否验证该模块以确保安全。 | TRUE 或 FALSE |
OEMCertifyModule | 允许 OS 加载程序将模块代码(例如,DLL、EXE 和 OCX)传递给 OEM,以验证该模块是否可以在系统中安全地运行。 | OEM_CERTIFY_TRUST OEM_CERTIFY_RUN OEM_CERTIFY_FALSE |
下表说明 OEMCertifyModule 函数的返回值。
返回值 | 说明 |
OEM_CERTIFY_TRUST | 信任应用程序,可以执行任何操作。 |
OEM_CERTIFY_RUN | 信任应用程序,可以运行,但不能调用任何特权函数。 |
OEM_CERTIFY_FALSE | 不信任,因此不允许运行。 |
OEMCertifyModule 函数可以对正在加载的模块进行任何类型的检查,例如,循环冗余检查或证书检查。当动态链接库 (DLL) 加载到某 EXE 程序的地址空间时,EXE 的信任级别将决定最终的访问级别。例如,如果 OEM_CERTIFY_RUN 信任级别的 EXE 试图加载具有较低信任级别 (OEM_CERTIFY_TRUST) 的 DLL 时,DLL 的最终信任级别是 OEM_CERTIFY_RUN。另一方面,如果 EXE 试图加载具有较高信任级别的 DLL 时,加载操作将失败。
下表显示 EXE 和 DLL 信任级别的不同组合方式。
EXE 信任 | DLL 信任 | 最终 DLL 信任 |
OEM_CERTIFY_RUN | OEM_CERTIFY_RUN | OEM_CERTIFY_RUN |
OEM_CERTIFY_RUN | OEM_CERTIFY_TRUST | OEM_CERTIFY_RUN |
OEM_CERTIFY_TRUST | OEM_CERTIFY_RUN | 加载 DLL 失败 |
OEM_CERTIFY_TRUST | OEM_CERTIFY_TRUST | OEM_CERTIFY_TRUST |
注意: OEM 必须对所有第三方驱动程序进行数字签名,否则加载驱动程序时将失败。实现此安全模型时,所有驱动程序都必须受信任。
要实现信任模型,最简单的方法是使用 OEMCertifyModule 函数,为所有应用程序返回 OEM_CERTIFY_RUN。从而使映像的非 ROM MODULES 部分的应用程序可以运行,但是对特权函数的调用会受到限制。通过这种方法,您不必指定运行时哪个应用程序是否受信任。如果返回的是 OEM_CERTIFY_FALSE,那么 RAM 中的应用程序将不能运行。在任何情况下,位于映像的 ROM MODULES 部分中的操作系统文件总是以最高权限运行。
要创建数字签名,您可以使用 Signfile.exe,该程序包含在 Platform Builder 3.0 中。Signfile.exe 是用私人密钥为可执行文件签名的工具,使用 Secure Hash Algorithm (SHA) 计算加密散列。关于 Signfile.exe 代码的示例,请参见 Platform Builder 3.0 产品 CD 中的 Public/Common/Oak/Tool/Signfile。
要在加载时验证签名,您可以使用 Loadauth.lib 函数,该函数在 /Public/Common/Oak/Lib 中与处理器对应的 Platform Builder 目录中。关于使用 Loadauth.lib 函数的详细信息,请参见 Platform Builder 3.0 文档。
您也可以不使用 Platform Builder 工具,而编写自己的签名验证方案。
下面的代码是使用 Loadauth.lib 函数实现 OEMCertifyModuleInit 和 OEMCertifyModule 函数的示例:
/* 签名公共密钥 BLOB */
const unsigned char g_bSignPublicKeyBlob[] = {
0x06,0x02,0x00,0x00,0x00,0x24,0x00,0x00,0x52,0x53,0x41,0x31,0x00,0x02,
0x00,0x00,0x01,0x00,0x01,0x00,0xb1,0x00,0x93,0x7c,0x18,0x63,0xce,0xf3,
0x23,0xe3,0x57,0x74,0x13,0x54,0x17,0x2c,0xdb,0xf6,0x56,0x77,0xb3,0x8d,
0x34,0x6c,0x41,0x3d,0x4e,0xbb,0xc1,0xaf,0x3d,0x17,0xb6,0x0e,0x70,0x72,
0x43,0x12,0x1d,0xb1,0x2a,0x57,0x05,0x27,0x58,0x63,0xef,0xb7,0x3b,0x71,
0xee,0xe4,0xcd,0x14,0xbe,0xf7,0x32,0xec,0xa2,0xae,0xbf,0x9a,0x6b,0x75
};
// RAM 可执行程序加载时签名检查
// 的声明
typedef BOOL (* OEMLoadInit_t)(LPWSTR lpszName);
typedef DWORD (* OEMLoadModule_t)(LPBYTE lpData, DWORD cbData);
extern OEMLoadInit_t pOEMLoadInit;
extern OEMLoadModule_t pOEMLoadModule;
extern BOOL InitPubKey(const BYTE *KeyBlob, DWORD cbKeyBlob);
// Loadauth 库例程
extern BOOL CertifyModuleInit(void);
extern BOOL CertifyModule(PBYTE pbBlock, DWORD cbBlock);
extern BOOL CertifyModuleFinal(PBYTE *ppbSignData,
PDWORD pcbSignData);
// 为每个 RAM 可执行模块调用一次
// 以初始化签名检查
static BOOL OEMCertifyInit(LPWSTR lpszName)
{
return CertifyModuleInit();
}
// 在 OemLoadInit 之后调用一次或多次
static DWORD OEMCertifyModule(LPBYTE lpData, DWORD cbData)
{
if (cbData)
{
// 处理模块字节
return CertifyModule(lpData, cbData);
}
else
{
// 最终调用
DWORD dwTrustLevel = OEM_CERTIFY_FALSE;
LPBYTE pSignedData;
DWORD cbSignedData;
BOOL fRet = CertifyModuleFinal(&pSignedData,
&cbSignedData);
if (fRet)
{
// 该文件有有效签名
// 我们希望以签名数
// 据返回信任级别
if (cbSignedData < sizeof(CHAR))
dwTrustLevel = OEM_CERTIFY_RUN;
else
switch (*pSignedData)
{
case 'T' :
dwTrustLevel = OEM_CERTIFY_TRUST;
break;
case 'R' :
dwTrustLevel = OEM_CERTIFY_RUN;
break;
default:
dwTrustLevel = OEM_CERTIFY_FALSE;
break;
}
}
#ifdef DEBUG
if (!fRet)
lpWriteDebugStringFunc(TEXT("OEMCertifyModule:signature
check failed./r/n"));
#endif
// 返回一个 OEM_CERTIFY 级别
return dwTrustLevel;
}
}
void OEMInit()
{
...
...
//
// 设置模型签名验证挂钩。
//
pOEMLoadInit = OEMCertifyInit;
pOEMLoadModule = OEMCertifyModule;
//
// 初始化签名验证公共密钥。
//
InitPubKey(g_bSignPublicKeyBlob,sizeof(g_bSignPublicKeyBlob));
//
// 其他 OEM 初始化步骤
//
...
}
注意: OEMCertifyModuleInit 和 OEMCertifyModule 函数的名称是任意的;您可以使用任何名称。但重要的是,应初始化 OEMInit 函数中的两个内核指针:pOEMLoadInit 和 pOEMLoadModule,让它们分别指向这两个命名函数。
除了 OEM 函数,还可以使用 CeGetCurrentTrust 和 CeGetCallerTrust API 来查询调用应用程序的信任级别。您可以使用这些函数验证应用程序的信任级别。
不信任模块不能调用以下 API:
CreateProcess API 中的调试标志:DEBUG_ONLY_THIS_PROCESS 和 DEBUG_PROCESS 也是受限制的。
Windows CE 5.0 中的安全注册表体系结构只允许已经识别的“信任应用程序”(OEM_CERTIFY_TRUST) 修改受保护注册表中的键和值。
在 Windows CE 5.0 中,以下注册表主键及其子键是受保护的,以避免不信任应用程序的非法操作:
此外,如果不信任应用程序试图使用以下注册表函数,将得到 ERROR_ACCESS_DENIED 返回值:
因为注册表的其余部分是不受保护的,所以 OEM 必须将所有重要的注册表信息存放在某个受保护的键中。
注意: 所有应用程序对所有注册表键和值都具有只读访问权限。
在 Secur32.dll 模块中提供的安全性支持提供者接口 (SSPI) 是一个严格定义的通用 API,用于获取进行身份验证、消息的完整性检查和消息加密的集成安全服务。它在应用程序层协议和安全性协议之间提供了一个抽象层。因为不同的应用程序在网络上传输数据时所采用的识别或验证用户身份的方法,以及加密数据的方法各不相同,因此 Windows CE SSPI 提供了访问包含各种身份验证和加密数据方案的 DLL 的途径。这些 DLL 被称作安全性支持提供者 (SSP)。
下图说明了 SSP DLL 和 SSPI Secur32.dll、Winsock、WinInet 之间的关系。
图 1:SSP DLL 和 SSPI Secur32.dll、Winsock、WinInet 之间的关系
SSPI 生成应用程序可用的一个或多个 SSP(也叫做安全包)。安全包将不同的 SSPI 函数映射到该安全包专用的某个安全性协议的实现上。OEM 也可以编写他们自己的安全包,然后将其添加到注册表中。
以下是 Windows CE 5.0 中可用的 SSP:
SSPI 允许应用程序开发人员使用多个 SSP 中的一个,而不必了解安全性协议的细节。
下表列出了 Windows CE 5.0 支持的 SSPI 函数。
“凭据管理函数”提供对主控者的凭据的访问。主控者是一个操作系统能识别的实体,可以是用户或进程。主控者使用凭据建立用户或应用程序的标识。
函数 | 说明 |
AcquireCredentialsHandle | 使应用程序能够获取凭据的句柄。 |
FreeCredentialsHandle | 释放凭据句柄和所有相关资源。 |
QueryCredentialsAttributes | 检索凭据属性。 |
“上下文管理函数”允许应用程序创建和使用安全上下文。安全上下文是与连接相关的安全数据,并且包含诸如会话密钥和会话持续时间等数据。客户端和服务器必须合作,来共同创建安全上下文。
函数 | 说明 |
InitializeSecurityContext | 通过生成可以传递给服务器或远程对等终端的令牌来初始化安全上下文。 |
AcceptSecurityContext | 使用重定向器验证凭据来建立安全上下文。 |
DeleteSecurityContext | 释放安全上下文,并删除与之相关联的本地数据结构。 |
QueryContextAttributes | 检索安全上下文的属性。 |
ApplyControlToken | 在现有的安全上下文中应用附加的安全性消息。 |
FreeContextBuffer | 释放由安全性提供者分配的内存缓冲区。 |
“消息支持函数”在安全连接上交换消息时,使用安全上下文以确保消息的完整性和保密性。通过消息签名和签名验证来保证消息的完整性。通过加密和解密保证消息的保密性。
函数 | 说明 |
MakeSignature | 生成消息的加密校验和,并且包含有序的数据,以防止消息丢失或误插。此函数还允许应用程序选择不同的加密算法。 |
VerifySignature | 验证消息签名。 |
“包管理函数”为所支持的各种安全包提供服务。
函数 | 说明 |
EnumerateSecurityPackages | 列举可用的安全包及其功能。 |
QuerySecurityPackageInfo | 检索指定的包的信息。 |
在客户端/服务器应用程序中,Windows CE 5.0 使用 Windows NT LAN Manager 安全性支持提供者 (NTLMSSP.dll) 进行用户身份验证。客户端应用程序向 NTLMSSP 提供用户名、域名和密码,服务器和客户端应用程序交换令牌以完成身份验证。
在 Windows CE 上运行的服务器应用程序也可以充分利用 Windows NT LAN Manager 安全包。例如,如果选择了 RPC_C_AUTHN_WINNT 标志,分布式组件对象模型 (DCOM) 将使用 Windows NT LAN Manager 协议来建立用户凭据。
虽然 Windows CE 与 Windows NT 的身份验证类似,但两者之间还是有显著的差别。在 Windows NT 中,每当客户端与服务器建立连接、客户端呼叫或者客户端与服务器交换数据时,都要进行身份验证;也可以禁用身份验证功能。Windows NT 支持模拟,也就是允许对象获取已通过身份验证的用户或客户端的安全凭据,而 Windows CE 不支持此功能。在 Windows CE 中,仅在连接层 (RPC_C_AUTH_LEVEL_CONNECT) 进行身份验证。在连接层,当客户端首次呼叫服务器时,DCOM 进行身份检查,但在随后的呼叫中不再进行身份验证。Windows CE 上的 DCOM 对象可以在任何身份验证级别发起呼叫,但是不会出现身份验证级别高于 CONNECT 的呼入请求。此外,可以禁用 Windows CE 的身份验证 (RPC_C_AUTHN_LEVEL_NONE)。
身份验证成功之后,要根据访问列表执行访问检查,访问列表标识可以在系统中启动类的主控者。在服务器初始化期间,根据 DefaultAccessPermissions 注册表设置创建该列表,或者编写程序用 DCOMAccessControl 对象来提供列表。
当 Windows CE 客户端通过网络连接到 Windows NT 域控制器(管理安全凭据)时,可以进行正常的身份验证。但在移动环境中,Windows NT 域控制器并非总是可用的,或者网络不是基于 Windows NT 的,在这样的情况下,您可以创建用户名和密码的本地数据库,以便 Windows NT LAN Manager 安全包使用这些信息验证凭据。Windows CE 提供下列 API 来创建和管理这些本地安全数据库:
为了进行安全网络通信,Windows CE 还支持 SSL 版本 2.0 和 3.0 的安全性协议,可以通过 WinInet 或直接通过 WinSock 来使用这些协议。这些应用程序使用安全套接字来发送和接收通信线路上的编码数据。
安全套接字依靠身份验证来确定是否可以信任远程主机。远程主机通过从证书颁发机构 (CA) 获取证书,来建立自己的信任度。同样,CA 可能会从高一级的机构获取证书,依此类推,建立一条信任链。要确定证书是否可靠,应用程序必须确定根 CA 的身份,然后再确定其是否可靠。
Windows CE 5.0 SSL 维护信任 CA 的数据库,该数据库独立于 CryptoAPI 2.0 证书存储区。当应用程序试图建立安全连接时,Windows CE 5.0 从证书链中提取根证书,并根据 CA 数据库进行检查。它通过证书验证回调函数将服务器证书和根据 CA 数据库得到的比较结果传递给应用程序。
最终,应用程序负责验证是否可以接受证书。应用程序可以接受或拒绝任何证书。如果某证书被拒绝,则连接建立失败。证书至少要满足两个条件:证书是当前使用的;证书代表的身份与正在建立连接的目标实体的身份相匹配。通道 CA 数据库中包括根证书颁发机构的列表。注意,这些根证书颁发机构是有一定期限的,可能需要定期更新。也可以通过编辑注册表来更新数据库,以添加更多的 CA。
在 Windows CE 5.0 Schannel CA 数据库中包含下列根证书颁发机构:
通过 CryptoAPI 提供的服务,应用程序开发人员可以添加数据加密/解密方案、使用数字证书进行身份验证、为基于 Win32 的应用程序进行 ASN.1 的编码或解码操作。应用程序开发人员无需了解内部的实现细节,即可使用 CryptoAPI 中的函数。CryptoAPI 与许多执行实际加密功能(例如,加密、解密、密钥存储和安全性)的加密服务提供者 (CSP) 协同工作。
Microsoft 加密系统包含三个要素:操作系统、应用程序和 CSP。应用程序通过 CryptoAPI 层与操作系统通信,操作系统则通过加密服务提供者接口 (CSPI) 与 CSP 通信。下图说明了这一点。
图 2:应用程序通过 CryptoAPI 层与操作系统通信,操作系统通过加密服务提供者接口 (CSPI) 与 CSP 通信。
CSP 是实现所有加密操作的独立模块,通常是一个 DLL。理想情况下,CSP 是一个与应用程序无关的模块,因此任何应用程序运行的都是同一种 CSP。但是,实际上某些有特定要求的应用程序需要定制的 CSP。EOM 可以编写自己的 CSP 包并将其添加到注册表中。
下表介绍 Windows CE 5.0 中包含的两种预定义的 CSP。
CSP | 说明 |
RSA 基本提供者 | 支持数字签名和数据加密,是多用途的加密工具。 |
RSA 增强提供者 | 支持 128 位的密钥加密。通过扩展密钥长度和附加算法提供更强大的安全性。 |
应用程序可以使用 CryptoAPI 函数执行下列操作:
CryptoAPI 1.0 在 Windows CE 5.0 中提供的功能与在 Windows 2000 和 Windows NT 中提供的功能很相似;但是,仅支持 CryptoAPI 2.0 的子集。Windows CE 5.0 支持 CryptoAPI 2.0 的下列功能:基于 X.509 标准对数字证书进行编码和解码,以及管理证书。不支持下列功能:管理验证字撤销列表 (CRL) 和验证字信任列表 (CTL) 的工具、低级消息传递函数和简化的消息传递函数。
Coredll.lib 导出 CryptoAPI 1.0 函数;Crypto32.lib 导出 CryptoAPI 2.0 函数;所有这些函数都在 Wincrypt.h 头文件中定义。
通过使用智能卡存储身份验证信息或数字签名机制,您可以向 Windows CE 设备添加安全层。可以编写定制的 CryptoAPI 提供者,使用智能卡的功能实现安全信息存储。
Windows CE 智能卡子系统通过智能卡服务提供者 (SCSP) 支持 CrytoAPI,SSCP 是允许访问特定服务的 DLL。该子系统在智能卡读卡器硬件和应用程序之间提供链接。通常,Windows CE 不提供 SCSP,而由智能卡供应商提供相应的 SCSP。但是,Windows CE 提供下表中描述的接口。
子系统组件 | 文件 | 说明 |
资源管理器 | Scard.dll | 使用 Win32 API 管理对多个读卡器和智能卡的访问。 |
资源管理器帮助器库 | Winscard.dll | 为使用智能卡和智能卡读卡器提供 PC/SC 服务。 |
智能卡读卡器帮助器库 | Smclib.lib | 提供通用智能卡驱动支持例程,以及所需特定驱动程序的附加 T=0 和 T=1 协议支持。 |
智能卡读卡器驱动程序示例 | Pscr.dll bulltlp3.dll stcusb.dll |
SwapSmart PC 读卡器驱动程序。 串行读卡器驱动程序。 通用串行总线 (USB) 读卡器驱动程序。 |
典型的智能卡系统包括应用程序、处理智能卡读卡器和应用程序之间的通信的子系统、读卡器以及智能卡。下图显示了基于 Windows CE 的智能卡系统的体系结构。
图 3:基于 Windows CE 的智能卡系统的体系结构
在一个独立的硬件中实现智能卡 CryptoAPI 服务提供者的部分功能将保证密钥和操作的安全性,因为:
在使用智能卡的组织中,用户实际不必记住任何密码(只有一个个人识别号或 PIN),并且出于其他安全性目的(例如电子签名的电子邮件),他们可以使用相同的证书。
OEM 可以为 Windows CE 设备提供唯一的识别标记,并使应用程序可以访问它。例如,您可能需要标记运行您的 OS 映像的每个蜂窝电话,以便达到记帐和安全性的目的。
Windows CE 使用 DEVICE_ID 结构来保存唯一的设备识别号。OEM 适配层中的输入/输出控制 IOCTL_HAL_GET_DEVICEID 返回指定给该 Windows CE 设备的当前 DEVICE_ID。有关详细信息,请参阅 Platform Builder 文档的“操作系统开发”一节中的“为应用程序提供平台信息”。
在运行线程时使用全内核模式将使整个系统易受攻击,因为 Windows CE 绕过了安全性特性。在全内核模式中,应用程序可以访问系统中的任何物理内存。这将使系统对恶意应用程序保持开放,不能防止它们获取特权信息和加密代码,或者删除文件。
尽管在全内核模式下运行时性能将得到提高,但这在一个开放的未受保护的环境中是不可接受的。您可以禁用全内核模式;在 Config.bib 文件中设置 ROMFLAGS 的第二个字节。根据其他标志的设置,ROMFLAGS 的值可能会有所不同。
拨号启动加载程序是一个存储在 ROM 中的程序,该 ROM 用于通过快闪内存或远程服务器升级 OS 映像文件 (Nk.bin)。要确保下载的 OS 映像的完整性,您可以使用数字密钥来签名和验证 OS 映像文件。拨号启动加载程序通过 Microsoft CryptoAPI 使用非对称散列算法 (CALG_SHA) 来验证数据。非对称散列算法将生成 160 位散列值。
Platform Builder 3.0 为数字身份验证提供下列工具:
在 OS 映像下载进程中,拨号启动加载程序从清单文件中提取签名,并验证每个 OS 映像文件的可靠性。如果验证失败,将暂停下载进程并通知用户。
有关 SSPI、安全套接字层、CryptoAPI 和智能卡的实现细节,请参阅 Windows CE 软件开发人员手册(英文)。
有关内核级别安全性(信任模型和内核模式)、设备标签和拨号启动加载程序的实现细节,请参阅 Windows CE Platform Builder 3.0(英文)。
还请参阅 MSDN Online Windows 嵌入式开发人员中心,网址为 http://msdn.microsoft.com/embedded(英文)。
有关智能卡的详细信息,请参阅 PC/SC 工作组主页,网址为 http://www.pcscworkrgroup.com(英文)。