Digital Certificate是计算机安全里很常见的内容。在网络上,最常见的情况大概就是SSL安全连接中的使用了。关于Digital Certificate,RSA算法,DSA算法,Message Digest,Mutual SSL等内容我就不介绍了,需要用到的朋友应该都很熟悉。如果不是很熟悉但有兴趣了解一些,这里有一个很浅显的教程:
http://www.youdzone.com/signature.html
这里简单介绍一点我自己遇到过的一些小例子。
我们的项目里需要用到一些Certificate(抱歉,我想还是不翻译成“证书”了,下同),有各种不同的用处,比如有用来做mutual SSL的,有用来做数字签名(digital signature)的。Windows提供了一个管理你系统里所有Certificate的工具,你可以这样调用:
1. Start -> Run -> "mmc"
2. 然后在出来的工具界面中-> File -> Add/Remove Snap-in -> Add -> Certificates -> 选Computer Account
这样就可以看到所有安装在LocalMachine这个Certificate Store里的Certificates了。如果在最后一步选My user account,则是看到CurrentUser这个Certificate Store。
开始的时候我们的Certificate全是通过这个工具来手工管理的,但后来Certificate越来越多,且每个Certificate的权限设置都有不同的时候,我们发现手工管理很容易出错,而且在最后布置(deploy)Web Application的时候很不方便(需要手动地往服务器上一个个加Certificate),所以我就写了一个工具来自动化整个过程。
.NET 2.0中对Ceritificate以及Certificate Store的使用和管理提供了很多的类,请参看System.Security.Cryptography.X509Certificates这个命名空间。两个最常用的类是:
X509Store (代表Certificate Store)和X509Certificate2 (代表X509标准的Certificates)
每个Certificate Store都有一串sub store,比如Personal是存放一些你个人的Certificate的地方(你如果给你的IIS服务器用selfssl加了一个服务器Certificate,那个Certificate就存放在Personal sub store里),还有比如Trusted Root Certification Authorities是存放所有你信任的Root Certificate的地方。
现在假设我们有一个很简单的任务,就是要在LocalMachine Certificate Store里建立一个MyProjectCerts的sub store,然后把我们项目用到的Certificate全放在那里,这个在.NET很容易做(如果是用传统的Win32 CAPI的话,光看看那些有5,6个参数的API就够你头疼的了):
X509Store store = new X509Store(args[ 1 ], StoreLocation.LocationMachine); store.Open(OpenFlags.ReadWrite); X509Certificate2 certificate = new X509Certificate2(PATH_TO_CERTIFICATE); store.Add(certificate); store.Close();
如果你的certificate带有一个Private Key(关于Certificate及其携带的Public Key/Private Key的内容我也略过了)的pfx文件,那么它一定是有密码保护的,不过这也很容易,只要在构造函数里多加一个参数就可以了:
X509Certificate2 certificate = new X509Certificate2(PATH_TO_CERTIFICATE, PASSWORD);
最后一部分是给Certificate分配合理的权限,其中最重要的,就是那些帐号(user account)有权限可以拿到一个Certificate的Private Key。这个很关键,因为Private Key是证明你是这个Certificate拥有者的最终证据,有了Private Key,你就可以生成签名,给消息加密,等等。
可是在这个过程中,原来很简单的问题却有了点波折。
首先我们发现我们的一些功能函数在Visual Studio的unit test环境中工作正常,但当这些函数被Web App的功能模块调用的时候,测试就通不过了。后来我们意识到是权限的问题,在unit test中直接调用那些函数的帐号是system admin,而安装Certificate也是system admin,所以测试程序可以顺利拿到Private Key来完成工作。但当这些函数被Web App调用的时候,执行的帐号变成了ASPNET,而ASPNET没有取得Private Key的权限!
于是我们用的是Windows Server 2003 Resource Kit里的winhttpcertcfg这个工具给ASPNET分配必要的权限,可是这样还有问题。
原本我们是在mmc这个工具里手工加入了Certificate,然后再用winhttpcertcfg分配权限,一切正常。但当我们用我的工具来加入Certificate后,winhttpcertcfg就无法正常设置权限了,硬是说我不是这个certificate的installer。见鬼,我不是刚刚才用我的工具往Certificate Store加了Certifiicate么,然后再马上接着用winhttpcertcfg的,怎么5秒钟的功夫我就变成不是原来的installer了。找了半天也不知什么原因,我甚至把winhttpcertcfg反编译了看它调用了那些CAPI (Cryptography API的简称),还是不懂。
后来才在这里找到了一片文章才恍然:http://blogs.msdn.com/ploeh/archive/2006/12/20/IntegrationTestingWithCertificates.aspx
原来缺省情况下创建一个X509Certificate2对象:X509Certificate2 certificate = new X509Certificate2(PATH_TO_CERTIFICATE); 的时候,Certificate的Private Key是不会被Persist(抱歉,不知该怎么翻译,不过大家对Persistance这个概念总是知道的吧?差不多的意思)到Certificate Store里,一定要调用第三个构造函数:
X509Certificate2 certificate = new X509Certificate2(PATH_TO_CERTIFICATE, PASSWORD, X509KeyStorageFlags.PersistKeySet);
这样做了以后,guess what? 还是不行,呵呵。继续研究一下,才发现,缺省情况下,Private Key是Persist到CurrentUser这个Store里的,而我们的Certificate安装在LocalMachine这个Store里(其实如果你在mmc中同时显示CurrentUser和LocalMachine两个Store,你会发现它们之间有一种奇妙的镜像关系,就是你加到LocalMachine里的Certificate会自动出现在CurrentUser那边,这个现象其实也给我们造成了麻烦,这里我就不展开了)。
最后的正确答案:
X509Certificate2 certificate = new X509Certificate2(PATH_TO_CERTIFICATE, PASSWORD, X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet);