为通过ClickOnce部署的应用程序进行数字签名
ClickOnce是.NET用于Windows应用程序的一种便捷部署方式。不过由于便捷,导致缺少自定义操作的空间。比如需要对通过ClickOnce部署的应用程序的主程序(exe文件)进行数字签名就比较麻烦。下面简单分享下,在既能获得ClickOnce的便捷功能(包括VS自动生成ClickOnce部署包)的同时,又能方便的对应用程序进行签名。
1,为什么要进行数字签名
为了保证系统的安全,现在Windows(比如Windows 8)对于通过网络上下载的应用程序的执行文件都会首先阻止,并提示用户是否要运行。这个时候,同时会提示这个应用程序的发行者。如果一个没有进行数字签名的应用程序,这个时候发行者就为“未知”,签名后,就可以告知用户这个应用程序是由你提供的。在这里,数字签名就是保证这个应用程序是由你发行,并未被第三方更改的。
上面这种情况还好,很多时候,如果你安装了360之类的软件,对于未签名的应用程序,大部分情况是阻止其运行的。如果没有进行数字签名,那么对用户会造成极大困扰。
对执行文件进行数字签名还有诸多好处,我就不一一例举。现在购买一个代码签名的证书也不算贵,对于类似我们这种发行客户端应用程序的厂商而言,是很有必要购买一个证书的。
2,几个误解:程序集强签名、ClickOnce清单签名、安装程序签名
程序集强签名
.NET里面有个概念——程序集强签名。这个只是.NET可以使用这个强签名文件(snk)来对程序集进行签名,保证程序集本身不被篡改。以便保证其他人引用的这个程序集是你开发的。MSDN这样描述:
程序集签名(也称为强名称签名)赋予应用程序或组件一个唯一标识,其他软件可用该标识来显式标识和引用该应用程序或组件。强名称由程序集的简单文本名、版本号、区域性信息(如果提供)以及公钥/私钥对组成。
例如,强命名使应用程序作者和管理员可以指定用于共享组件将使用的一种准确的服务的版本。这使不同的应用程序指定不同的版本,而不会影响其他应用程序。 此外,还可以使用组件的强名称作为安全证据生成两个组件之间的信任关系。
要对程序集进行强签名,无需使用购买的代码签名数字证书,用.NET提供的sn工具就可以生成snk文件,你只要保存好这个文件,就可以保证你的程序集的签名。
但是,程序集强签名并不等于执行文件的数字签名(就算这个程序集是一个exe文件)。并且MSDN中明确说不应该对exe文件进行强签名(虽然我是这样做的)。文件的数字签名实际上是在文件系统级别对任何文件附加一个签名,告知操作系统此文件的发行者是谁。即可以在文件的属性对话框中,看到“数字签名”这项选项卡。
ClickOnce清单签名
另外,对于ClickOnce的应用程序清单(即app.exe.manifest文件)和部署清单(即app.application文件)进行签名。这样的签名实际上保证客户端正确识别ClickOnce部署包的发行者以及完整性,保证不被第三方篡改。如果不对ClickOnce清单进行签名的话,那么在安装(即运行app.application文件)的时候,安装程序就无法显示发行者,在某些企业环境下或某些安全软件下,就可能阻止application文件的运行(尤其你的应用程序需要获取Full Trust权限的时候更是如此)。但是,对于ClickOnce清单进行签名并不会对程序的执行文件进行签名(虽然同样需要代码签名数字证书)。
安装程序签名
使用过ClickOnce部署的同学,应该知道VS在生成ClickOnce部署包的时候,会同时生成一个setup.exe文件。同样,ClickOnce不会自动对这个按照执行文件进行签名。为了保证这个setup.exe能顺利的在客户端机器上运行,最好还是对其进行数字签名。MSDN文档中就有一节内容专门讲述了这个问题——“How to: Sign Setup Files with SignTool.exe (ClickOnce)”。同样,就算setup.exe被签名了,应用程序真正的执行文件还是需要签名的。
当然,正因为有了SignTool这个工具。
3,失败尝试:Post-Build Events
由于我生成ClickOnce部署包,是完全依赖于VS来自动生成的。所以为了最大限度的保证这种自动化,我首先尝试在Post-Build Events中对编译出来的exe程序集(bin文件夹内的)进行签名。不过后来发现,ClickOnce是从obj文件夹里面复制文件;随后对obj中的程序集进行签名,结果ClickOnce包含的程序集还是未签名的。我估计,ClickOnce在打包前会对程序集进行hash检查,如果hash不对,就会自动全新编译(跳过Build Events的执行)。经过几次尝试后,最后还采用ClickOnce打包完成后,再来签名的方法。
4,最终做法:用CMD脚本对ClickOnce部署包进行处理
在重新复习了ClickOnce的一些重打包的命令之后,编写了如下命令行脚本,可以对ClickOnce打包好的部署包中的exe文件进行签名:
1: SET _dir=%CD%
2:
3: ECHO %_dir%
4:
5: rename eBalance.exe.deploy eBalance.exe
6:
7: signtool.exe sign /sha1 F0B5D453FE821F3DA370B87933BXXX /t http://timestamp.comodoca.com/authenticode eBalance.exe
8:
9: mage -update eBalance.exe.manifest
10:
11: mage -sign eBalance.exe.manifest -ch F0B5D453FE821F3DA370B87933BXXX -ti http://timestamp.comodoca.com/authenticode
12:
13: rename eBalance.exe eBalance.exe.deploy
14:
15: cd ....
16:
17: signtool.exe sign /sha1 F0B5D453FE821F3DA370B87933BXXX /t http://timestamp.comodoca.com/authenticode setup.exe
18:
19: mage -update eBalance.application -appm "%_dir%eBalance.exe.manifest"
20:
21: mage -sign eBalance.application -ch F0B5D453FE821F3DA370B87933BXXX -ti http://timestamp.comodoca.com/authenticode
22:
23: xcopy eBalance.application "%_dir%eBalance.application"
对上面的脚本简单解释一下:
5行,由于我打包部署的时候,包含了deploy后缀,所以先进行了一个重命名操作
7行,对exe执行文件进行签名,使用的是我安装在本机中的签名证书
9行,更新应用程序清单(即这个应用程序会包含哪些程序集、资源和其他文件)
11行,对应用程序清单重新签名
13行,把exe文件改回deploy后缀,以便接下来更新部署清单的时候能正常执行
17行,对setup.exe进行签名(可选)
19行,更新部署清单,且指明对应的应用程序清单是那个
21行,对部署清单重新签名
23行,用更新且重新签名的部署清单覆盖具体版本目录里面的部署清单
这段脚本需要进入到具体的部署版本目录里(比如:publishApplication FileseBalance_4_1_13054_7),才能执行。
5,可选做法:手动生成ClickOnce部署包
当然,通过利用signtool、mage工具,你可以编写一段更加复杂的脚本(甚至使用PowerShell),来完全用脚本执行签名、生成相关清单,打包的工作。具体可以参考MSDN文档:http://msdn.microsoft.com/zh-cn/library/xc3tc5xx.aspx