根证书含义 - 孤舟蓑笠翁,独钓寒江雪
- 博客频道 - CSDN.NET
本文想简单谈谈那个所谓的“根证书”。在访问铁道部网上售票官网 www.12306.cn 后,有一个醒目的提示,为保证顺畅购票,需要下载安装根证书。那么什么是根证书?为什么买火车票的时候需要下载和安装,在淘宝等在线交易网站购物时候为什么就不需要这样做?
今年开始,人民群众们终于可以通过互联网购买火车票了。虽然说在线买的难度不比以往排队购买低多少,但这总算是一次值得鼓励的尝试。不过在线购票系统一经退出,在技术上就已经被人批得体无完肤。
为了确保安全,很多涉及在线交易的网站,例如网上银行、购物网站等,都会使用SSL技术对页面内容进行加密。SSL技术在这里的主要用途有两个:
确保网站服务器和用户浏览器之间的通讯不被窃听:这一点很好理解。SSL属于一种公钥加密体系,简单来说,一个SSL证书分为两部分:公钥和私钥。其中私钥会被网站所有者妥善保管,并在服务器端用私钥将网络通讯全部加密;而公钥会在网上广为传播,一个公钥加密后的数据只有用所对应的私钥才能解密。因此只要SSL证书本身可以保证安全,那么在访问网站的时候就可以保证网络通讯不被他人所窃取,并且如果有人进行中间人攻击,因为没有相应的密钥,导致篡改后的数据无法通过校验,因此可以及时察觉。
确保网站所宣称的身份真实可靠:这一点也不难理解。网上有个网站叫做支付宝,可如何保证这个网站就是那个真正的支付宝,而不是其他人伪造的钓鱼网站?因此真正的支付宝可以使用SSL证书,这种针对企业用的证书的申请手续比较繁琐,有一大堆审查流程,需要提交大量相关的证明文件,因此可以保证只有真正的某公司才能以这个公司的名义申请证书,只要申请到证书,就可以确信身份的可靠。此外近些年还有一种更可靠的EVSSL证书。
试试看访问“https://www.alipay.com”这个域名,随后可以看到,在浏览器的地址栏,“Http”协议后面出现了“s”这个字母,并且IE地址栏的右侧显示了一个锁头图标,因此证明该网站是SSL加密的。点击这个锁头图标后,还可以看到网站所用证书的相关信息。
加密功能基本上没什么需要解释的,因此下文的重点将放在身份可靠性这块。在上图所示界面上点击“查看证书”链接,随后可以看到该网站SSL证书的详情:
上图显示的“VeriSign Class 3 International Server CA – G3”是证书颁发机构,而“www.alipay.com”是证书持有人。那么到底应该怎样通过这些信息判断网站是否可靠?
在这里一个很通用的规则是:如果浏览器检测到加密网站所用的证书是正常的,那么地址栏就会显示为绿色(使用EVSSL证书)或者白色(使用普通SSL证书),这种情况下可以放心地浏览该网站,并提交自己的数据;如果浏览器检测到网站的证书有问题,那么地址栏就会显示为红色,提醒我们注意,同时取决于具体情况,地址栏右侧会显示有“证书错误”按钮,而且网站内容不会显示,取而代之的是浏览器的警告信息。基本上主流浏览器的最新版本都已经具备这样的功能。
这个过程的基本原理是:假设我们信任A公司,而A公司信任B公司,那么我们就可以信任B公司。很明显,“www.alipay.com”的证书是“VeriSign
Class 3 International Server CA –
G3”颁发的,这表示后者信任前者,可以证明前者的真实身份,但是我们又凭什么信任后者这个证书颁发机构?
上述文字里不止一次提到“信任”一词,那么“信任”在这里是什么意思?是否像我们平时讲话时说的“我信任他”那样,代表我们相信他是个好人,不会干坏事?其实完全不是这样。这里所说的“信任”,只是说明证书持有人的身份是真实可靠的,至于持有人用这个证书干什么事情,不在“信任”的范畴内。例如,网上很多臭名昭着的恶意软件,现在都带有数字证书(倒是很多正规用途的软件因为开发商缺乏安全观念不带证书),同时因为这些数字证书的“根”都是我们信任的根证书颁发机构,因此,操作系统和浏览器是信任这些公司的身份的,但并不代表这些公司的软件不会干坏事。只要肯花钱,任何人都可以在商业性质的证书颁发机构买到直接被我们的系统所信任的证书。
其实Windows和任何操作系统本身就包含一些受信任证书颁发机构的根证书,要查看这些根证书,可以运行“certmgr.msc”打开证书控制台,然后从控制台窗口左侧的控制台树中依次进入“证书-当前用户”→“受信任的根证书颁发机构”→“证书”,随后右侧的窗口中会显示本机预置的所有根证书颁发机构,其中就有“CA
– G3”,这表示我们信任“CA – G3”,而“CA –
G3”信任“www.alipay.com”,因此我们可以信任“www.alipay.com”。如果从证书控制台中删除“CA –
G3”的根证书,表示我们不再信任它,那么它所信任的公司也将不再被我们信任。
按照上文的方法打开证书控制台,并进入到“不信任的证书”→“证书”节点后,还会发现里面列出了多个颁发给大量知名企业的证书,例如Microsoft、Google、Skype、Yahoo等。为什么会不信任这些大牌互联网企业?就是因为某家根证书颁发机构被黑客攻破,导致这些大企业所用的证书私钥被窃取。因此为了保证安全,这些被盗证书已经被吊销。因而将原本被盗的证书都添加到“不信任的证书”节点下,这样盗取证书的黑客就算使用这些证书给病毒签名,伪装成这些大企业,也会因为证书吊销的缘故不会让人轻易上当。这家倒霉的公司就是荷兰的DigiNotar,该公司因为这次事件现在已经破产,而从中也足以证明证书吊销机制的重要性。这一点会在下文详细介绍。微软已经通过KB
2607712补丁将受影响的证书全部设置为不信任。
这里要重点提出“根证书”这个概念,全世界具有提供数字证书业务的公司有很多,而Windows自带的“根证书”很少,默认情况下,我们是如何信任这么多不同公司颁发的不同证书的?其实这就是“根”这个字的含义,因为可以颁发证书的公司虽然很多,但最基本的根证书颁发机构只有有限的几个,默认情况下都是被操作系统所信任的。那么既然操作系统能信任根证书颁发机构,自然也就可以信任被根证书颁发机构信任的公司,进而可以信任被这些公司所信任的下一级公司。
如何证明这一点?可以单击上图中的“查看证书”链接,随后打开“证书”对话框,切换到“证书路径”选项卡后,可以看到下图所示界面。从该图中可以看出,整个证书信任链的路径分为三个层次,最顶层的是我们信任的根证书颁发机构,该机构给“International
Server CA –
G3”颁发了证书,因此,我们信任“CA-G3”;随后“CA-G3”又给“www.alipay.com”颁发了证书,因此也可以信任“www.alipay.com”。如果“www.alipay.com”再给别人颁发证书,那么这个人的身份依然可以被我们信任。
而铁道部的在线订票网站并没有走这个一般意义上,涉及金融交易的商业化网站都严格遵守的方法。也就是说,铁道部并没有花钱在商业性质的CA(证书颁发机构)购买受信证书,他们直接自己给自己颁发证书。这样的做法一般主要是用于测试或学习等非正式场合,但如果一个商业化网站想要正式上线运营,通常并不会这样做,因为对用户来说风险太大。
当我们查看12306.cn 这个网站的证书信息时就会发现,这个证书根本没有一个有效的受信任CA,完全是自己给自己发着玩的。
上图信息显示,该证书的颁发者以及根证书是“SRCA”,不知道这个缩写代表什么意思,可能是铁道部内部的某个系统。因为SRCA的身份不被系统自带的任何一个根证书所信任,因此12306.cn网站所用的证书默认也不会被任何一个系统所信任。因而铁道部要求安装根证书的原因,就是让访客将这个证书手工添加到“受信任的根证书颁发机构”节点下。
很遗憾的是,这种做法虽然很不安全,可国内的大佬们很喜欢使用。例如在我的系统中,这里就有建行和工行网银自行添加的根证书。
那么这种做法除了能省几个钱之外,对用户来说有什么危害?
在12306.cn订票的很多人可能会看到过“该站点安全证书的吊销信息不可用,是否继续”这样的信息。这是什么意思?
还是以上文那个DigiNotar的例子来说,假设某个大型CA被攻击,私钥被窃取,这时候有两种方法尽量避免损失:
类似Windows补丁这样,由软件厂商通过发布更新的方式,将被盗证书强制设置为不信任。
使用证书吊销列表(CRL)。
第一种方式比较好理解,但并不是所有公司都能获此殊荣。毕竟主流操作系统的用户数量庞大,根本不可能针对一个地区性的,或者规模很小的公司的被盗证书发布更新,强制不信任。因此第二种情况就至关重要了。每个证书中都包含CRL,其实这个可以理解为一个网址,通过这个网址可以获得证书吊销的相关信息。
因此如果一个小公司通过商业性CA购买的证书被盗了,只要将相关信息告知CA,这家CA就会将这个证书的内容添加到CRL中。随后任何一个用户在执行涉及到证书的操作,例如安装带有数字签名的软件,或者访问SSL网站的时候,系统都会通过这个CRL地址检索吊销清单,并查看当前软件或网站使用的证书是否位于清单中。如果不在,就证明这个证书依然是可信任的;如果在,就证明该证书已经被盗,因此软件或网站存在仿冒的可能。
而因为12306.cn使用了自己给自己颁发的证书,因此也就根本无法在自己的证书中包含CRL信息,所以会看到“吊销信息不可用”的提示,这意味着浏览器在告诉你,你所访问的网站,不一定能完全证明其可靠,这可能是真网站,但也有是钓鱼网站的可能。
而如果12306.cn的证书私钥丢失或被盗(看看去年底的大规模泄密事件,谁敢保证没有这种可能性),持有该证书的人想要伪造一个钓鱼网站,或者以铁道部的身份发布恶意软件,那真是轻而易举,并且铁道部对此会束手无策。
毕竟在线购买火车票的人全都安装了这个根证书,而该证书根本无法通过CRL吊销。此外还有一个更重要的问题,如果盗取该证书的人继续使用“SRCA”的身份给其他人以其他身份颁发证书,例如以银行或支付中介的名义,结果会怎样?因为所有在线购买过火车票的人,由于根证书的关系,系统已经信任SRCA的根证书,因此这些伪造的证书也会直接被信任。最坏的情况下,所有热门的SSL加密网站(购物、银行、股票….)要想被伪造都是轻而易举的。
什么意思呢?如果某天你访问的“支付宝”网站的证书信任链是下面这样,你觉得会是什么后果?
真心希望这种情况永远不会发生。同时更加希望国内这些大佬们有更多安全意识,尽快认识到目前这种做法的不足。商业受信证书虽然需要花钱买,但不是太贵,真的!
当然,上述希望可能永远不会成真,因此作为一般用户,如果你已经成功在线购买到火车票(恭喜你啊,你要不要去买个彩票试试手气),那么建议你在“受信任的根证书颁发机构”节点下将SRCA的根证书彻底删除。
Java编程语言的一个杰出之处就在于开源社区可以以较低的成本或者甚至是免费地提供优秀的应用程序。其中一个例子就是Apache Tomcat,它为使用servlet或JSP技术的开发提供了一个健壮的Web服务器。现在Web服务技术正日趋成熟,所以有些应用程序就有可能利用 Swing特性丰富的前端瘦客户端结合Web或ejb层已经开发出来的数据验证和业务逻辑。此类应用程序只有在受到保护的情况下才能正常运行,不过,安全性不一定意味着昂贵的成本。本文的目的就是要演示Web服务客户端如何通过安全的HTTPS协议使用自签名的安全证书。
HTTPS
通常可以无缝地与不安全的HTTP协议一起使用,而不中断用户的体验。这是由于SSL被设计为由可信的第三方进行验证和签名。Verisign是一家流行的认证机构。如果您的Web应用程序要求安全的通信,那么您就可以付钱给Verisign来签名您的SSL证书。经过Verisign签名之后,您的
Web站点上的用户就可以不中断地在HTTP与HTTPS之间进行切换,因为所有主流Web浏览器都信任由Verisign签名的证书。但是
Verisign并不是获得签名证书的惟一选择。为了节省运作成本,或者为了个人使用方便,您也可以自签名自己的证书。但是,自签名证书会中断Web站点用户的体验。通常Web浏览器会显示一个对话框,询问您是否希望信任一个自签名证书。
Web浏览器的这一特性很好,因为当其获得一个由未知认证机构签名的证书时,还有机会进行处理。在开发用于通过HTTPS进行通信的Web服务客户端时,这就没那么容易了。在运行Java代码时,不会出现询问是否信任一个不可信的认证机构的对话框。JRE会抛出一个异常,说明试图通过HTTPS连接到一个具有不可信证书的Web站点:
Caused by:sun.security.validator.ValidatorException:No trusted certificate found
无法捕获此异常并继续。要让Web服务使用自签名证书,JRE必须以某种方式将您当作认证机构信任。
为演示此问题的解决方案,我将执行以下步骤:
生成并自签名我自己的证书;
为Tomcat配置SSL,使其使用该证书;
创建一个示例Web服务,以便通过HTTPS调用;
从WSDL生成Web服务客户端代码;
使用定制的密钥库解决方案演示客户端;
JDK附带了一个工具,keytool.exe,用于管理SSL公钥/私钥。密钥在文件系统的一个二进制文件中进行添加和删除。默认的密钥库文件是
JAVA_HOME\jre\lib\security\cacerts。该文件包含了JRE所信任的认证机构的列表。一个知名可信公司(比如
Verisign)的列表已经存在于密钥库中了。要查看该列表,可使用口令changeit执行以下代码:
D:\>keytool -list -rfc -keystore JAVA_HOME\jre\lib\security\cacerts
Keytool应用程序可用于编辑此文件。但是,为了防止出错,最好还是创建一个新文件。如果不告知keytool使用哪个文件,它就会默认地创建HOME/.keystore。
要生成自己的自签名证书,可执行:
D:\>keytool.exe -genkey -alias Tomcat -keyalg RSA -storepass bigsecret -keypass bigsecret -dname "cn=localhost"
执行完该命令后,就会在HOME目录下生成一个.keystore文件。下面是各种切换命令的含义:
genkey:告诉keytool应用程序生成新的公钥/私钥对。
alias:用于引用密钥的名称。记住,.keystore文件可包含多个密钥。
Keyalg:使用RSA算法生成公钥/私钥对。
Storepass:访问.keystore文件所需的口令。
Keypass:管理密钥所需的口令。
dname:该值非常重要。.我使用了localhost,因为该示例被设计为本地运行。如果一个Web应用程序被注册为http://www.myserver.com,那么该值就必须是www.myserver.com。如果名称不匹配,证书就会自动被拒绝。
一旦keytool应用程序创建了一个新的公钥/私钥对,它就自动自签名该密钥。我们刚刚生成了自己的自签名证书,它可用于HTTPS通信。只需提取出自签名公钥。后面我将展示如何做。
现在必须配置Tomcat,使其使用自签名证书。我使用的是Tomcat
5.0.30。编辑TOMCAT/conf/server.xml文件。在文件中搜索“8443”,并取消绑定到该端口的注释。然后必须向添加下属属性:
keystorePass="bigsecret"
当JRE启动时,它将自动找到HOME/.keystore文件,并且Tomcat会试着使用口令“bigsecret”访问它。在Tomcat启动时,控制台应该有如下输出:
Feb 4, 2006 3:11:23 PM org.apache.coyote.http11.Http11Protocol start
INFO:Starting Coyote HTTP/1.1 on http-8443
这意味着成功地读取了.keystore文件,现在可以通过8443端口进行安全的HTTPS连接了。打开Web浏览器,并在地址栏输入https://localhost:8443/。因为该证书是自签名的,所以Web浏览器将显示一个对话框,询问是否信任该连接。如果接受,则所有的通信都将通过HTTPS进行,从而成为安全的。
我将使用Apache Axis项目创建一个非常简单的Web服务。该Web服务将模拟检查新的电子邮件消息。Web服务客户端传递一个惟一地识别一个用户的令牌。Web服务返回一个新电子邮件消息的列表(参见清单1)。
import java.util.*;
public class Email {
public List
getNewMessages(String id)
{
List l = new ArrayList(3);
l.add("1");
l.add("2");
l.add("3");
return l;
}
}
要获取已部署的Web服务,执行以下步骤:
从清单1剪切并粘贴代码到Webapp的根目录下的Email.jws文件。
编辑Web.xml文件,添加Axis servlet以及一个*.jws映射(清单2)。
将Axis jar文件放入WEB-INF/lib。请参见文章末尾的“参考资料”部分,获取Axis项目URL。
AxisServlet
Apache-Axis Servlet
org.apache.axis.transport.http.AxisServlet
AxisServlet
*.jws
在部署了本文所附带的WAR文件(并为Tomcat配置SSL)之后,Web就可以安全地通过HTTPS使用下面的URL来访问了:
https://localhost:8443/JDJArticleWebService/Email.jws
Axis项目提供了一个名为WSDL2Java的工具,它获取一个Web服务WSDL并自动创建使用该Web服务所需的Java源代码。参见清单3中用于生成Email.jws Web服务代码的命令行。
java
-classpath
.;axis.jar;log4j-1.2.8.jar
;commons-logging-1.0.4.jar
;commons-discovery-0.2.jar
;jaxrpc.jar;saaj.jar
;wsdl4j-1.5.1.jar
org.apache.axis.wsdl.WSDL2Java
-p jdj.wsclient.shared
http://localhost:8080/JDJArticle/Email.jws?wsdl
注意清单3
中用于访问WSDL的URL。它在8080端口使用了不安全的HTTP协议。为什么不在8443端口使用HTTPS呢?这是因为自签名证书,WSDL2Java工具将遇到与本文所试图解决的证书问题完全相同的问题。所以现在必须使用使用不安全的协议。这意味着生成的代码必须有一点改变,使用“https”和“8443”替换“http”和“8080”引用。本文所附带的客户端zip文件包含了更改后的代码。
JRE的默认密钥库是JAVA_HOME\jre\lib\security\cacerts。只要出现自签名证书,Java应用程序就会抛出异常,因为该证书不在密钥库中。因此,在开发客户端时有两种选择。第一种选择是将自签名证书放入该JRE的默认密钥库中。虽然这种方法有效,但是它并不是一个好的解决方案,因为需要在每个客户端机器上进行定制化。第二种选择是生成一个定制的密钥库,将自签名证书放入其中,并将定制密钥库作为应用程序的一部分分发(通常在一个
jar文件中)。
要为客户端创建定制密钥库,需要执行以下步骤:
从HOME/.keystore导出自签名公钥。
将自签名公钥导入到为客户端创建的新密钥库中。
要从HOME/.keystore导出自签名公钥,可执行以下代码:
D:\>keytool.exe -genkey -alias Tomcat -keyalg RSA -storepass bigsecret -keypass bigsecret -dname "cn=localhost"
现在通过导入Tomcat.cer,为客户端创建定制密钥库:
D:>keytool.exe -import -noprompt
-trustcacerts -alias Tomcat -file Tomcat.cer -keystore CustomKeystore
-storepass littlesecret
使用 “-keystore
CustomKeystore”,将会在当前工作目录中创建一个名为CustomKeystore的新密钥库文件。可以在本文的客户端zip文件的
/classpath/resources/keystore目录下找到CustomKeystore文件。使用刚刚生成的文件替换该文件。
现在只剩下创建一个使用该定制密钥库的客户端了。.我将演示两种实现方法。
第一种方法是使用Java系统属性javax.net.ssl.trustStore和javax.net.ssl.trustStorePassword来指向
CustomKeystore文件,并提供访问该文件的口令。jdj.wsclient.truststore包中的示例Web服务客户端使用的就是这种方法(参见清单4)。
public static
void main(String[] args)
throws Exception
{
System.setProperty(
"javax.net.ssl.trustStore",
"classpath/resources/keystore/CustomKeystore");
System.setProperty(
"javax.net.ssl.trustStorePassword",
"littlesecret");
EmailServiceLocator wsl =
new EmailServiceLocator();
Email_PortType ews =
wsl.getEmail();
Object [] objects =
ews.getNewMessages("12345");
out("Msg Count: " + objects.length);
}
main()方法设置系统属性,然后创建使用该Web服务的对象。当JRE需要访问密钥库时,它就在文件系统中寻找
classpath/resources/keystore/CustomKeystore文件。虽然这只是一个简单的解决方案,但它还是存在问题,因为密钥库文件必须放在文件系统中,而客户端代码也必须知道在哪里找到它。
第二种解决方案具有更好的可移植性,它将资源放在jar文件中,从而避免了文件系统问题。客户端代码负责读取CustomKeystore文件,并以某种方式使用它创建到服务器的安全连接。jdj.wsclient.socketfactory包中的示例Web服务客户端使用的就是这种方法(参见清单5)。
public MySocketFactory(Hashtable table)
throws Exception
{
out("Created!");
KeyStore ks =
KeyStore.getInstance(
KeyStore.getDefaultType()
);
char [] password =
"littlesecret".toCharArray();
String keystore =
"/resources/keystore/CustomKeystore";
Class tclass =
this.getClass();
InputStream is =
tclass.getResourceAsStream(
keystore
);
ks.load(is, password);
KeyManagerFactory kmf =
KeyManagerFactory.getInstance("SunX509");
kmf.init(ks,password);
TrustManagerFactory tmf =
TrustManagerFactory.getInstance("SunX509");
tmf.init(ks);
SSLContext context =
SSLContext.getInstance("SSL");
context.init(
kmf.getKeyManagers(),
tmf.getTrustManagers(),
new SecureRandom()
);
factory =
context.getSocketFactory();
}
清单5显示了如何将CustomKeystore文件作为资源读取,并使用它来创建javax.net.ssl.SSLSocketFactory。配置Axis可插入架构,然后可使用MySocketFactory类从该工厂创建安全的Socket对象。
本文以一个简单的问题开始:我希望使用自签名的证书保护通过HTTPS的Web服务通信。默认情况下,JRE会拒绝应用程序的自签名证书,因为它不是来自于可信的认证机构。要让安全的通信可运行,必须让Web服务客户端JRE信任自签名证书。为此,我使用keytool应用程序生成一个新的公钥/私钥对,提取出自签名公钥,然后创建一个新的密钥库,并导入该自签名证书。然后我创建一个不需要任何客户端配置的、完全自包含的Web服务客户端