ClickOnce作为VS自带的软件发布方案,可根据不同需求,使用相应的策略,自动实现依赖环境检测和安装,软件版本检测、安装、升级、运行控制。
我们在使用ClickOnce发布的时候,一般会使用VS生成的自签测试证书做签名,也可以单独购买代码签名证书。前者证书管理不成体系,后者需要付费。
本文将介绍一种应用场景较为有限,但系统化、更灵活的免费代码签名证书管理方案。
背景提要:公司在防火墙(pfSense)上已经配置了一套自建CA,并广泛应用于内网的多项业务,包括WEB站点、IPSEC点到点、OpenVPN等。现在需要为一些内部软件进行代码签名。



代码签名靠的是证书EKU的codeSigning用途。由于pfSense的证书管理控制台不支持为证书添加EKU扩展,所以只能用openssl命令手动创建和签发(可以直接借用pfSense里的openssl,也可以在任意一台电脑安装openssl后进行处理)。
对比VS测试证书和自建CA签发的代码签名证书在应用程序和VSTO应用程序的部署后,结论如下:
1. 使用代码签名证书可以在CA里集中式管理,VS测试证书比签发代码签名证书更为便捷。
2. 安全策略对内网发布位置更为宽松,默认情况下外网发布的安装会被直接拒绝,内网发布则会在弹窗中由用户自行选择是否安装。虽然可以通过修改注册表,让外网发布达到同样的效果,但这样做更繁琐,对客户电脑的侵入更多,极大降低用户电脑的安全性。
具体流程:
1. 导出ca备用:
位置System->Certificates->Authorities,导出证书ca.crt和私钥ca.key。

2. 打开pfSense的ssh登录功能,然后用scp上传ca证书和key:
System -> Advanced,在Admin Access页面,找到Secure Shell Server选项并启用,并保存生效。
由于pfsense的ca并没有单独以文件的形式保存,而是作为配置文件的一部分保存在/conf/config.xml,所以下载以后需要传回pfSense。

* pfSense自带openssl,所以我的操作都在pfSense上进行。也可以找一台电脑安装openssl进行证书签发。
3. ssh登录pfSense,并进入Shell(选项8)

4. 创建一个目录,aaa,所有东西都放在这里面,然后生成代码签名证书的私钥。
openssl genrsa -out vstoxls.key 2048
5. 生成CSR证书请求文件
openssl req -new -key vstoxls.key -subj "/C=CN/ST=JS/L=NJ/O=AWNJ/OU=Dev/CN=VSTOXLS" -out vstoxls.csr6. 创建ca签名配置文件sign.cnf,并用ca进行签名
[ v3_codesign ]
basicConstraints = critical, CA:FALSE
keyUsage = critical, digitalSignature
extendedKeyUsage = codeSigning
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid, issuer:alwaysopenssl x509 -req -in vstoxls.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out vstoxls.crt -days 1800 -sha256 -extfile sign.cnf -extensions v3_codesign

顺手验证一下证书内容
openssl x509 -in vstoxls.crt -noout -text

7. 生成pfx
openssl pkcs12 -export -out vstoxls.pfx -inkey vstoxls.key -in vstoxls.crt -passout pass:abcd1234 -certfile ca.crt
8. 导回pfSense证书管理,或者自己拷出来备份。
具体操作:
a. System->Certificate页面,Certificates标签下,点击Add/Sign按钮打开导入界面。
b. 选择操作类型Import an existing Certificate,填写名称,证书类型选择X509(默认就是这个),并将生成的.crt和.key分别粘贴到Certificate data和Private key data中。

c. 下载证书验证一下。


换一台没有添加CA的电脑,发现证书无法验证:

9. 搭建ClickOnce发布站点
创建ClickOnce时,我选择了http发布源,这样就不需要把安装程序拷来拷去。并且由于是内部应用,我可以将它发到内网服务器,此时系统会使用Intranet策略,而非更严格的Internet策略。当然也可以把发布位置的url添加到【受信任的站点】,只是在没有AD环境,无法下发安全策略的情况下,需要为每台电脑做设置。

发布站点搭建非常简单,部署一个https静态站点即可,我用的是nginx,SSL证书来自同一个CA。

server {
listen 443 ssl;
server_name 192.168.5.93;
ssl_certificate cert.crt;
ssl_certificate_key cert.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
root html;
index index.html index.htm;
}
}10. 使用ClickOnce发布应用程序
a. 创建一个测试用的.net程序,并创建ClickOnce发布,VS可以生成一个测试证书。


b. 在项目生成界面直接发布,并把程序发布目录里所有内容都拷到http发布路径下。

c. 在用户电脑进行安装

d. 报错属于正常情况,因为还缺少一步,将ca添加到用户电脑的受信任的根证书机构(选择【本地计算机】,不要选择【当前用户】),这样发布站点才可信。

e. 将VS生成的测试证书替换为自建CA签发的证书(必须为pfx)。

11. 使用ClickOnce发布VSTO项目
按以下几个步骤进行测试(在进行测试前,已将自建CA添加到受信任的根证书机构):
a. 外网发布 + VS测试证书(直接提示失败)
b. 外网发布+代码签名证书(直接提示失败)
c. 内网发布 + VS测试证书(弹窗提示,由用户选择是否安装)
d. 内网发布 + 代码签名证书(弹窗提示,由用户选择是否安装)最终会发现,VSTO项目发布时,对发布位置的限制更严格。
a. 以下是外网发布 + VS测试证书(直接提示失败)

b. 以下是外网发布+代码签名证书(直接提示失败)

c. 以下是内网发布 + VS测试证书(弹窗提示,由用户选择是否安装)

d. 以下是内网发布 + 代码签名证书(弹窗提示,由用户选择是否安装)

最后还有一个未能解决的问题,在安装的时候,弹窗里会提示【未知的发行者】,即使将证书添加到【受信任的发布者】,在VS的ClickOnce选项里将发布者设置为证书的CN也不行。等有空再研究如何解决,暂时先挖一个坑。

