第十一章 客户端证书认证(Client Certificate Authentication)
尽管用户名和密码认证特别常见,就像我们在第一章:一个不安全应用的剖析和第二章:Spring Security起步所讨论的那样,form认证的存在允许用户提供各种类型的凭证。Spring Security也为这种需求提供了支持,在本章中我们将不再讨论基于form的认证并探索使用可信任的客户端证书。
在本章的内容中将会包括:
l 学习在客户端证书认证中,用户浏览器和服务器之间是如何交互的;
l 配置Spring Security使用客户端证书认证用户;
l 理解Spring Security中客户端证书认证的架构;
l 探索客户端证书认证的高级配置选项;
l 了解客户端证书的优劣以及在处理过程中的常见问题。
客户端证书认证是怎样工作的
客户端证书认证需要服务器向浏览器发送请求信息而浏览器进行响应,这样来建立用户(和他们的浏览器)与服务端应用的可信任认证关系。这个可信任的关系是通过交换可信的以及可认证的凭证建立起来的,而这也就是所谓的证书(certificates)。
与前面见到的不同,在客户端证书认证中,servlet容器或应用服务器本身要负责在浏览器和服务器之间建立可信的关系,这是通过请求证书、评估并接受其合法性。
客户端证书认证也被成为共同认证(mutual authentication),是安全socket层(Secure Sockets Layer,SSL)协议及其后续协议传输层安全协议(Transport Layer Security,TLS)的一部分。因为共同认证是SSL和TLS协议的一部分,它需要HTTPS连接(通过SSL或TLS进行安全防护)进行客户端证书认证。关于Spring Security中SSL/TLS的更多细节,请参考我们在第四章:凭证安全存储中对SSL/TLS的讨论和实现。为了实现客户端证书认证需要在Tomcat(或者你正在使用要运行例子的应用服务器)中建立SSL/TLS。就像第四章那样,在本章的后续内容中我们将会用SSL代替SSL/TLS。
下面的序列图展现了客户端浏览器与web服务器之间的交互,在这个交互中进行了SSL连接的建立并检验用来做共同认证的客户端证书的可信性。
我们可以看到两个证书的交换(服务端证书和客户端证书)提供了两端参与者的认证并确信他们之间的继续会话是安全的。清晰起见,我们省略了一些SSL握手和证书信任检查的细节,但是,我们建议你去阅读SSL和TLS的协议和证书,关于这些课题有很多好的参考手册。RFC 5246, The Transport Layer Security (TLS) Protocol V1.2 (http://tools.ietf.org/html/rfc5246),是学习客户端证书的很好入门资料。如果你想了解更细节的东西,那Eric Rescorla的SSL and TLS: Designing and Building Secure Systems是关于协议和实现的极佳细节材料。
基于客户端证书认证的另一个名称是X.509认证。术语X.509来源于X.509标准,最初由ITU-T组织发布,用于在X.500标准下的目录(LDAP的起源,你可能会想起第九章:LDAP目录服务)下使用。后来,这个标准修改后用在了安全的互联网通信上。
我们提及这些是因为在Spring Security中与这个主题有关的类都与X.509相关。要记住的是X.509并没有定义共同认证协议本身,但是定义了证书的格式和结构并包含了可信任证书的权限。
建立客户端证书认证的设施
对于个体的开发人员来说不好的消息是,为了体验客户端证书认证需要一些并不简单的配置并事先建立起与Spring Security的简单集成。鉴于这些建立步骤对于初学者来说可能会遇到很多问题,所以我们觉得给读者进行介绍是很重要的。
我们假设你使用本地的、自认证的服务器证书以及自认证的客户端证书,Apache Tomcat也是如此。这在大多数的开发环境中很常见,但是,你可能会访问合法的服务器证书、一个证书中心(CA)或者其它的应用服务器。如果是这样的话,你需要使用它们的安装指令并以类似的方式配置你的环境。请参考第四章的SSL建立指令来帮助配置Tomcat和Spring Security在单独的环境中使用SSL。
理解公钥设施的目的
本章会关注为了教学而建立完整的开发环境,但是在大多数的情况下,你需要将Spring Security集成到已有的客户端校验安全环境中,那里会有很多的设施(通常会是硬件和软件的组合)来提供诸如证书授权和管理、用户服务以及撤销等功能。这种类型的环境会有公钥设施——联合使用硬件、软件和安全策略以实现高度安全、以认证为驱动力的网络系统。
为了进行web应用的认证,环境中的证书或硬件设备能用来进行安全、不可抵赖的电子邮件(使用S/MIME),网络认证,甚至基于物理元件的访问(使用基于PKCS 11的硬件设施)。(In addition to being used for web application authentication, certificates or hardware devices in these environments can be used for secure, non-repudiated email (using S/MIME), network authentication, and even physical building access (using PKCS 11-based hardware devices))
因为管理这样的环境会比较困难(需要IT和工序都能够很好完成),所以这无疑需要专业的安全人员来操作这样的环境。
创建客户端证书密钥对
创建自签名的客户端证书与创建自签名的服务端证书是一样的,要通过keytool命令来创建密钥对。客户端密钥对的不同在于它需要web浏览器能够访问key store并且需要将客户端的公钥加到服务器的trust store中(我们稍后将会介绍它是什么)。
创建客户端的密钥对如下:
keytool -genkeypair -alias jbcpclient -keyalg RSA -validity 365 -keystore jbcppets_clientauth.p12 -storetype PKCS12 |
当提示为客户端证书输入名字(common name或DN即拥有者DN的一部分)时,确保第一个提示的输入要与Spring Security JDBC存储中已有的用户相匹配,如admin:
What is your first and last name? [Unknown]: admin ... etc Is CN=admin, OU=JBCP Pets, O=JBCP Pets, L=Anywhere, ST=NH, C=US correct? [no]: yes |
当我们配置Spring Security访问证书授权用户信息时,将会看到为什么这(个设置)很重要。在Tomcat中进行证书授权前,我们还有最后的一步。
配置Tomcat的trust store
回忆一下密钥对的定义包含一个私钥和公钥。与SSL证书校验和保护服务器通信类似,校验客户端证书需要检查创建它的可信任者。
因为我们使用keytool命令自己创建的自签名客户端证书,所以java虚拟机不会像信任认证中心的那样信任它。
这样我们就需要强制要求Tomcat将这个证书视为可信任的证书。我们要达到这个目的需要将密钥对的公钥导出并将其添加到Tomcat的trust store中。
首先,我们要将公钥导出到一个标准的证书文件中,并将其命名为jbcppets_clientauth.cer,如下:
keytool -exportcert -alias jbcpclient -keystore jbcppets_clientauth.p12 -storetype PKCS12 -storepass password -file jbcppets_clientauth.cer |
接下来,我们要将证书导入到trust store中(这将会创建一个trust store,但是在典型的部署环境中,你可能会在trust store中拥有其它的证书):
keytool -importcert -alias jbcpclient -keystore tomcat.truststore -file jbcppets_clientauth.cer |
这将创建名为tomcat.truststore的trust store并提示你输入密码。你会看到一些证书相关的信息并最后被询问是否信任这个证书:
Owner: CN=admin, OU=JBCP Pets, O=JBCP Pets, L=Anywhere, ST=NH, C=US Issuer: CN=admin, OU=JBCP Pets, O=JBCP Pets, L=Anywhere, ST=NH, C=US Serial number: 4b3fb3d9 Valid from: Sat Jan 02 16:00:09 EST 2010 until: Sun Jan 02 16:00:09 EST 2011 Certificate fingerprints: MD5: 02:69:16:3B:D7:C2:74:9E:F7:FD:18:C9:C5:E4:C8:94 SHA1: 65:57:94:6D:D2:83:7E:51:19:CF:58:94:ED:43:11:F6:AC:D0:FB:EC Signature algorithm name: SHA1withRSA Version: 3 Trust this certificate? [no]: yes |
复制这个新的tomcat.truststore文件到你使用Tomcat服务器的conf目录下。在最后的成功前还要一个最后的配置!
【Key Store和Trust Store的区别是什么?Java Secure Socket Extension (JSSE)对key store进行了如下的定义:私钥和对应公钥的存储机制。key store(包含密钥对)被用来加密或解密安全信息等功能。trust store原本用来只存储公钥以实现校验身份时的可信任通信(如我们在证书校验时看到trust store是怎样被使用的)。但是,在很多常见的管理场景中,key store和trust store被结合到一个文件中(在Tomcat中,这可以通过使用Connector的keystoreFile和truststoreFile属性来完成配置)。这些文件本身的格式完全相同(实际上,每个文件可以是任意的JSSE支持的keystore格式,包括Java Key Store / JKS, PKCS 12等)。】
最后,我们需要为Tomcat指明trust store并启用客户端证书认证。这需要在Tomcat 的server.xml文件中为SSL Connector添加三个额外的属性:
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true" maxThreads="150" scheme="https" secure="true" sslProtocol="TLS" keystoreFile="conf/tomcat.keystore" keystorePass="password" truststoreFile="conf/tomcat.truststore" truststorePass="password" clientAuth="true" />
这是建立SSL时,触发Tomcat请求客户端证书的所需要的配置。到这里,我们可以重新启动Tomcat了。
【还有一个其它的方式来配置Tomcat使用客户端证书认证——我们将会在稍后启用。现在,我们需要在第一次连接Tomcat服务器的时候就要使用客户端证书。这会更容易的判断出设置是否正确。】
最后一步是将客户端证书导入到客户端的浏览器中。
导入证书到浏览器中
根据你使用浏览器的不同,导入证书的过程会有所区别。我们将会提供Firefox和IE的指南,但是如果你使用其它的浏览器,请查询其帮助文档或你喜欢的搜索引擎寻找帮助。
使用Firefox
按照以下的步骤来导入包含客户端证书密钥对的key 存储:
1. 打开“工具”菜单;
2. 点击“选项”菜单项;
3. 点击“高级”按钮/图标;
4. 点击“加密”tab标签;
5. 点击“查看证书”按钮。将会打开“证书管理器”;
6. 点击“您的证书”tab标签;
7. 点击“导入”按钮;
8. 找到你存储jbcppets_clientauth.p12文件的位置并将其选中;
9. 你需要输入创建这个文件时的密码。
客户端证书就导入了,你可以在列表中看见它。
使用IE
IE与Windows操作系统紧密集成,所以它导入key存储会比较容易:
1. 在Windows Explorer中双击jbcppets_clientauth.p12文件。将会启动“证书导入向导”;
2. 点击“下一步”接受默认选项直到提示你输入证书密码;
3. 输入证书密码,并点击“下一步”;
4. 接受“自动选择证书存储选项”并点击“下一步”;
5. 点击“完成”。
为了校验证书正确安装,你需要进行一下的步骤:
1. 在IE中打开“工具”菜单;
2. 点击“internet选项”菜单项;
3. 点击“内容”tab标签;
4. 点击“证书”按钮;
5. 点击“个人”tab标签(如果它没有被选中的话)。你会看到证书列在此处。
测试
现在你可以使用客户端证书连接到JBCP Pets。如果所有的都被成功设置,在访问站点时会提示要求证书——在Firefox下,证书展现如下:
但是,当你试图访问站点中受保护的区域,如“My Account”区域,你将会被重定向到登录页面。这是因为我们没有配置Spring Security辨别证书中的信息——到这里,客户端和服务器的所有交互停止在Tomcat服务器本身那里。
客户端证书认证的问题解决
不幸的是,如果我们说第一次就能将客户端认证配置正确且没有任何错误出现是很容易得,那我们是在骗你。事实上,尽管这是一个很伟大和强大的安全设施,但是浏览器和web服务器的文档都很匮乏,出错信息在好的情况下会比较令人费解而在不好情况下根本就是误导性的。
记住到此时我们还有涉及到Spring Security,所以调试器可能帮不上什么帮(除非你手头有Tomcat的源码)。一些常见的错误和要检查的事情如下:
l 当你访问站点时,没有提示你要求证书。有很多情况可能会导致如此,而这也是最令人迷惑的问题。以下是要检查的事情:
u 确保你使用的客户端浏览器正确安装了证书。如果你访问上面的网站被拒绝,有时你需要重启整个浏览器(关闭所以的窗口);
u 确保你访问的是服务器的SSL端口(在开发环境下一般为8443),并在URL中选择了https协议。客户端证书对不安全的浏览器链接不会提供。确保浏览器也信任服务器的SSL证书,即使你强制它信任一个自签名的证书;
u 确保你添加了clientAuth指令到Tomcat配置中(或者对等的任意你使用的应用服务器);
u 如果以上的所有都失败的话,使用网络分析器或包探测器,如Wireshark (http://www.wireshark.org/) 或Fiddler2 (http://www.fiddler2.com/) 来了解线路上的交流和SSL密钥交换。
l 如果你使用的是自签名的客户端证书,确保公钥被导入到服务器端的trust store中。如果你使用的是CA签发的证书,确保JVM信任CA或者CA的证书被导入到了服务器trust store中。
l 特别在IE下,根本不会报告客户端证书失败的细节(只会报告一个“页面不能展现”的错误)。使用Firefox来查看是否会有客户端证书相关的错误。