强命名程序集能防篡改
用私钥对程序集进行签名,并将公钥和签名嵌入程序集,CLR就可以炎症程序集未被修改或破坏。程序集安装到GAC时,系统对包含清单的那个文件的内容进行哈希处理,将Hash值与PE文件中嵌入的RSA数字签名进行比较。如果两个值完全一致,表明内容未被篡改。此外,系统还对程序集的其他文件的内容进行哈希处理,并将Hash值与清单文件的FileDef中存储的哈希值进行比较。任何一个哈希值不匹配,表明程序集至少有一个文件被篡改,程序集将无法安装到GAC。
应用程序需要绑定到程序集时,CLR根据被引用的程序集的属性在GAC中找到该程序集。找到被引用的程序集,就返回包含他的子目录,并加载清单所在的文件。以这种方式查找程序集,可保证运行时加载的程序集和最初编译时生成的程序集来自同一个发布者,因为进行引用的程序集的AssemblyRef表中的公钥标记与被引用程序集的AssemblyRef表中的公钥匹配。如果被引用程序集不在GAC中,CLR会查找应用程序的基目录,然后查找应用程序配置文件中标注的任何private路径。然后,如果应用程序由MSI安装,CLR要求MSI定位程序集。如果在任何位置都找不到程序集,那么绑定失败,抛出异常。
如果强命名程序集文件从GAC之外的位置加载,CLR会在程序集加载后比较哈希值。也就是说,每次应用程序执行并加载程序集时,都会对文件进行hash处理,以牺牲性能为代价,保证程序集文件内容没有被篡改。CLR在运行时检测到不匹配的哈希值会抛出异常。
注:将强命名程序集安装到GAC时,系统会执行检查,确保包含清单的文件没有被篡改。这个检查仅在安装的时候执行一次。除此之外,为了增强性能,如果强命名程序集被完全信任,并加载到完全信任的APPDomain中,CLR将不检查该程序集是否被篡改。相反,从那个飞GAC的沐浴露加载强命名程序集时,CLR会校验程序集的清单文件,确保文件内容未被篡改,造成该文件每次加载都造成额外的性能开销。
延迟签名
SN.exe工具生成公钥/私钥对时,会调用Windows提供的Crypto API。密钥可存储到文件或其他存储设备中。例如,大公司会将自己的私钥保存到一个硬件设备中,再将硬件锁进保险库。公司只有少数人才能访问私钥。这项措施可以防止私钥泄露,并保证了密钥的完整性。当然,公钥是完全公开的,可以自由分发。
准备打包自己的强命名程序集时,必须使用受严密保护的私钥对它进行签名。然而,在开发和测试程序集时,访问这项受严密保护的私钥可能有点碍事。因此,.Net提供了延迟签名,该技术也被称为部分签名。延迟签名允许只用公司的公钥生成程序集,暂时不用私钥。由于使用了公钥,引用了程序集的其他程序集会在他们的AssemblyRef元数据表的记录项中嵌入正确的公钥值。另外,它还使程序集能正确存储到GAC的内部结构中。当然,不用公司的私钥对文件进行签名,便无法实现防篡改保护。这是因为无法对程序集的文件进行哈希处理,无法在文件中嵌入数字签名。然而,失去这种保护不是一个大问题,因为只是在开发阶段才延迟签名。打包和部署程序集肯定会签名。
为了实现延迟签名,需要获取存储在文件中的公钥值,将文件名传给用于生成程序集的实用程序。另外,还必须让工具知道你想延迟对程序集的签名,暂不提供私钥。
编译器或AL.exe一旦检测到要对程序集进行延迟签名,就会生成程序集的AssemblyDef清单记录项,其中将包含程序集的公钥。公钥使程序集能正确存储到GAC。另外,这也不妨碍引用了改程序集的其他程序集的正确生成。在进行引用的程序集的AssemblyRef元数据表记录项中,会包含正确公钥。创建程序集时,会在生成的PE文件中为RSA数字签名预留空间。文件内容不会在这个时候进行哈希处理。
目前生成的程序集没有有效签名。安装到GAC会失败,因为尚未对文件内容执行哈希处理——文件表面上已被篡改了。在需要安装到GAC的每台机器上,都必须禁止系统验证程序集文件的完整性。这要求使用SN.exe实用程序并指定-Vr命令行开关。用这个开关执行SN.exe,程序集的任何文件在运行时加载时,CLR都会跳过对其哈希值的检查。
结束程序集的开发和测试之后,就要正式对其签名,以便打包和部署。为了对程序集进行签名,要再次使用SN.exe实用程序,使用-R开关,并指定了包含私钥文件的名称。-R开关指示SN.exe对文件内容进行哈希处理,用私钥对其进行签名,并将RSA数字签名嵌入文件中之前的预留空间。这样,就可以部署完全签好名的程序集。