某项目的业务系统要求在用户使用的过程使用数字证书。在刚接手这个任务时,我对个中的知识几乎不了解,于是到处搜刮相关资料,网上此类文章甚多,但都比较零散,而且个人认为其中绝大部分未能尽释所疑。在经历了数天的郁闷及实践以后,终有所悟,作此文以记。
1 对称加密与非对称加密对称加密方法的加密与解密采用相同的密钥,因此任何人只要获得其中的密钥,就可以对密文进行解密。而非对称加密方法有两个不同密钥,任意一个都可以成为加密密钥,另外一个就是解密密钥,用一个密钥加密的密文必须用另一个密钥才可以解密。
2 公钥与私钥在非对称加密方法的两个密钥中,保管其中一个密钥,禁止泄漏他人,除了密钥拥有者以外,任何人都不知道,此即私钥。另一个密钥则公之于众,任何人都可以获得,此即公钥。可知,用私钥加密的密文必须用公钥解密,反之亦然。
3 数字签名数字签名是基于非对称加密方法来实现的。拥有私钥的一方使用私钥对某一份内容加密后发送给其他人(个体或群休),因为任何人都可以拥有与该私钥对应的公钥,拥有公钥者使用公钥对接收到的密文进行解密,如果能正确解密,则说明此份内容必然是拥有私钥的一方发出。另一方面,如果拥有公钥的实体希望把一份内容发送给拥有私钥的一方而不希望其它任何人看到,则可以利用该公钥加密此份内容。因为只有私钥拥有者才可以将密文解密,所以能保证任何其他人无法查看其内容。
4 数字证书数字证书是以数字签名的方式通过第三方权威认证机构有效地进行网上身份认证,以帮助各个实体识别对方身份及表明自身身份的证书,具有防抵赖功能及真实、安全、保密和防篡改的特性。在此,我们最主要的是需要知道数字证书是一个文件,该文件保存了某个实体(个人、机构或设备等)的信息及该实体所拥有私钥对应的公钥。
5 SSL协议这里主要介绍SSL握手协议以建立安全通信通道的过程。
1) 客户端的浏览器向服务器传送客户端 SSL 协议的版本号,加密算法的种类,产生的随机数,以及其他服务器和客户端之间通讯所需要的各种信息。
2) 服务器向客户端传送 SSL 协议的版本号,加密算法的种类,随机数以及其他相关信息,同时服务器还将向客户端传送自己的证书。
3) 客户利用服务器传过来的信息验证服务器的合法性。服务器的合法性包括:证书是否过期,发行服务器证书的 CA 是否可靠,发行者证书的公钥能否正确解开服务器证书的“发行者的数字签名”,服务器证书上的域名是否和服务器的实际域名相匹配。如果合法性验证没有通过,通讯将断开;如果合法性验证通过,将继续进行第4)步。
4) 客户端随机产生一个用于后面通讯的“对称密码”,然后用服务器的公钥(服务器的公钥从步骤2)中的服务器的证书中获得)对其加密,然后将加密后的“预主密码”传给服务器。
5) 如果服务器要求客户的身份认证(在握手过程中为可选),用户可以建立一个随机数然后对其进行数据签名,将这个含有签名的随机数和客户自己的证书以及加密过的“预主密码”一起传给服务器。
6) 如果服务器要求客户的身份认证,服务器必须检验客户证书和签名随机数的合法性。具体的合法性验证过程包括:客户的证书使用日期是否有效,为客户提供证书的 CA 是否可靠,发行 CA 的公钥能否正确解开客户证书的发行 CA 的数字签名,检查客户的证书是否在证书废止列表(CRL)中。检验如果没有通过,通讯立刻中断;如果验证通过,服务器将用自己的私钥解开加密的“预主密码”,然后执行一系列步骤来产生主通讯密码(客户端也将通过同样的方法产生相同的主通讯密码)。
7) 服务器和客户端用相同的主密码即“通话密码”,一个对称密钥用于 SSL 协议的安全数据通讯的加解密通讯。同时在 SSL 通讯过程中还要完成数据通讯的完整性,防止数据通讯中的任何变化。
8) 客户端向服务器端发出信息,指明后面的数据通讯将使用的步骤7)中的主密码为对称密钥,同时通知服务器客户端的握手过程结束。
9) 服务器向客户端发出信息,指明后面的数据通讯将使用的步骤7)中的主密码为对称密钥,同时通知客户端服务器端的握手过程结束。
10) SSL 的握手部分结束,SSL 安全通道的数据通讯开始,客户和服务器开始使用相同的对称密钥进行数据通讯,同时进行通讯完整性的检验。
6 从何处获得证书基本的原理差不多就这些,下一个问题是从哪里获得数字证书?从前面的SSL握手过程可以知道,客户端需要查看服务器的证书,以证明服务器的身份,而服务器端也要(当然这需要服务器有相关的配置)查看客户端证书以证明客户的身份。因此我们至少需要两个证书:服务器证书和客户证书。事实上,每一个客户都可以拥有自己的证书以证明自己的身份。当一个实体需要一个证书的时候,需要向相关的权威认证机构申请,比如GDCA,由该权威机构向该实体颁发证书。所谓权威机构就是已经被另一个权威机构证明是真实有效的机构,因此这是一个递归的过程,在最顶层有一个虚拟的根机构,被称为RootCA。申请证书需要一定的费用,根据证书类型的不同而不同,从GDCA的网站上得知,个人证书是50元/年,而网站证书则需要10000元/年。如果申请一个有效其为10年的证书,这是一笔不小的费用。那么能不能不申请而获得证书呢?
7 自己做CA首先要说明,不管你的证书是由哪个机构颁发的,对于通信过程来说都是安全的。因此对于某个特定范围的圈子来说,完全可以由该圈子中公认的机构做CA。对于一个企业来说,企业及其客户所组成的圈子中,企业可以认为是具有权威性的,于是就可以作为一个权威机构向其客户颁发证书。据了解,广东发展银行及中国建设银行客户所用的数字证书就是该银行颁发的。在这里需要使用一个软件openssl。Openssl的功能相当强大,偶对其中的功能也不甚了解,只了解其中与CA认证相关的部分功能,基本上可以满足自己做CA的要求。在使用openssl前,先要设置一个环境变量OPENSSL_CONF,使其指向openssl安装目录下的openssl.cnf文件。产生证书的过程大致为:生成密钥对->生成签证请求->签证。签证后的证书就为数字证书,其中保存有对应实体的公钥信息。
下面是使用opensll实现CA并签发证书的过程:
1) 生成CA密钥CA作为一个权威机构,他本身也是使用证书来标识自己,CA本身也拥有私钥。在签发数字证书的过程中,CA的私钥主要用于加密用户证书请求中的用户信息及公钥。genrsa -out cakey.pem 1024该命令会产生长度为1024字节的私钥文件cakey.pem。
2) 生成CA证书请求req -new -out careq.csr -key cakey.pem此命令会根据cakey.pem产生该证书的请求文件careq.csr。
3) 用CA私钥自签名x509 -req -in careq.csr -out cacert.pem -signkey cakey.pem -days 3650该命令会根据输入的证书请求及CA私钥,生成CA证书。至此,作为CA所要求的证书已经准备好,所得到的cakey.pem可用于将来颁发证书,而cacert.pem证书作为用户的可信任证书,需要分发给每一个由该CA机构颁发证书的实体。
8 服务器证书与客户端证书的制作服务器证书与客户端证书的制作稍有不同,这是由于服务器对证书格式与客户端证书格式要求不同导致的。对于Tomcat或WebSphere这样的服务器(偶只知道这两种),通常都要求一种JKS格式的文件,在该文件中,保存有服务器私钥、服务器证书以及服务器根证书链(从RootCA一直到服务器证书颁发机构的一系列CA证书列表)。
8.1 服务器证书要制作这一类证书,需要使用JDK提供的一个工具keytool。
下面是使用keytool制作证书库的过程:
1) 生成初始密钥库keytool -genkey -alias serveralias -keystore server.jks -keyalg RSA输入keystore密码:changit您的名字与姓氏是什么? [Unknown]: CN您的组织单位名称是什么? [Unknown]: OU您的组织名称是什么? [Unknown]: O您所在的城市或区域名称是什么? [Unknown]: L您所在的州或省份名称是什么? [Unknown]: ST该单位的两字母国家代码是什么 [Unknown]: CNCN=CN, OU=OU, O=O, L=L, ST=ST, C=CN 正确吗? [否]: y 输入证书的主密码 (如果和 keystore 密码相同,按回车):在输入上述的“名字与姓氏”时,要注意所输入的值应该与服务器的IP或域名一致,否则在客户端验证服务器证书时会认为该证书与指定的服务器实体不匹配,如果是IE就会弹出一个窗口提示用户是否信任该证书。
2) 生成证书请求keytool –certreq -alias serveralias -sigalg MD5withRSA -file server.csr -keypass changeit -keystore server.jks -storepass changeit最后生成的证书请求文件为server.csr。把该请求发送给证书颁发机构,该机构将会验证证书请求中的实体信息,然后实现签证。因为我们是自己做CA,所以签证这一步也是我们自己来做。
3) 实现签证签证仍然使用openssl,而不是keytool。在openssl中,输入以下命令:x509 -req -in server.csr -out servercert.pem -CA cacert.pem -CAkey cakey.pem -days 3650输出的servercert.pem文件即是签证后的证书,即证书请求响应。命令中用到的cakey.pem和cacert.pem是上面生成的CA密钥文件和CA根证书。完成CA签证后,把证书请求响应连同CA根证书一起返回给申请者。申请者需要把CA根证书(链)及证书请求响应导入到JKS库中。此时使用的工具是keytool。keytool –import –trustcacerts –keystore server.jks –file cacert.jks –alias RootCAkeytool –import –trustcacerts –keystore server.jks –file servercert.jks –alias serveralias需要注意的是,首先导入CA根证书(链),最后才导入证书请求响应,而且导入证书请求响应时的别名必须与生成JKS库时使用的别名一致。至此,服务器端的JKS文件已经完成,可以配置到相应的服务器中。
8.2 客户端证书至于客户端证书,既可以用openssl制作,也可以用keytool来制作,不过用keytool来生成的话,要麻烦一些,而且签证的步骤必须由opensll来实现,因此这里讲述用openssl生成客户端证书的过程。
1) 生成客户端密钥文件genrsa -out clientkey.pem 1024该命令会产生clientkey.pem密钥文件。
2) 产生证书请求req -new -out clientreq.csr -key client-key.pem把证书请求发送给CA以实现签证,这里同样由自己做。
3) 实现签证x509 -req -in clientreq.csr -out clientcert.pem -signkey clientkey.pem -CA cacert.pem -CAkey cakey.pem -CAcreateserial -days 3650签证后的客户端证书只包含了客户端的公钥及客户实体的信息,不包含私钥。
4) 生成客户端可导入的客户端证书pkcs12 -export -clcerts -in clientcert.pem -inkey clientkey.pem -out client.pfx这一步把客户端的证书(公钥)与私钥捆绑在一起,生成client.pfx文件,该文件连同CA根证书一起发送给客户。
9 配置IE客户端对于使用IE的客户,需要把CA根证书及个人证书导入到IE的证书列表中。在导入CA根证书的时候,如果不是权威的CA根证书(自己当CA的那种),IE会提示是否信任该证书,确认后CA根证书会保存在证书信任列表中。此后再导入客户自己的证书,IE将会认为这是可信的,因为CA根证书保存在证书信任列表中了。在导入含有私钥的客户证书时,IE会提示是否给私钥加密以及设置私钥是否可导出等属性,建议把私钥加密并设置为不可导出,并要求每次使用证书时输入密码。
10 配置WebSphere服务器使用数字证书(凭记忆描述)打开WebSphere的管理控制台,进入到安全性->SSL页面中,该页面列出了目前所有的SSL配置库,一般来说只有一个默认配置库,我们修改该配置库的参数即可。点击默认的配置库链接,将含有服务器证书的密钥库路径及密码,修改信任CA列表的密钥库路径(一般来说可与服务器证书的密钥库一致)及密码,选中 “客户机认证”选择框即完成配置。
11 配置Apache使用数字证书(从别处参考)
11.1 编辑Apache的ssl.conf1) 指定服务器证书位置SSLCertificateFile
/servercert.pem2) 指定服务器证书key位置 SSLCertificateKeyFile /serverkey.pem3) 证书目录SSLCACertificatePath 4) 根证书位置SSLCACertificateFile /cacert.pem 5) 开启客户端SSL请求 SSLVerifyClient require SSLVerifyDepth 1
11.2 启动ssl apachectl startssl 会要求输入server.key的密码
12 在服务器端读取客户端发送的数字证书在某些情况下希望用户使用数字证书时能够自动登录系统,这就需要以用户的数字证书序列号作为登录标识。下列代码演示在服务器端读取客户端数字证书的方法(Tomcat或WebSphere)。javax.servlet.request.X509Certificate[] certs = (javax.servlet.request.X509Certificate[]) request.getAttribute(“javax.servlet.request.X509Certificate”);if(certs!=null){ //第一个证书为用户个人证书javax.servlet.request.X509Certificate cert=( javax.servlet.request.X509Certificate)certs[0];//以16进制形式打印其序列号System.out.println(cert.toString(16)); }elseSystem.out.println(“could not get certificate from client!”);上传的数字证书有数个,包括用户个人证书及其根证书链,第一个为用户个人证书,接着为用户个人证书的根证书,再接着为用户个人证书根证书的根证书,依次类推。上述代码是网上可以找到的代码,而且可以正常通过编译,但在实践的过程中,偶发现javax.servlet.request.X509Certificate应该为java.servlet.request.X509Certificate,否则会引发类型转换异常。不知道这是不是服务器版本不同引起的问题,测试时使用的服务器版本为WebShpere5.0.1。对于使用其它服务器或语言的情况,在网上可以找到很多例子,偶还没有使用或测试过,不敢罔言。
13 后记CA认证的配置过程事实上并不复杂,重要的是一些基本概念的理解。开始的时候,由于缺乏相关的知识及经验,走了不少的弯路,浪费不少时间,特撰此文,以启后来者。撰写过程中,引用了不少他人的文章,由于记录零散,作者无从考究,在此对这些作者一并表示感谢。我已经尽力把事情说得清楚明白,但由于表达能力问题,文中不妥之处肯定不少,给你的阅读及理解造成障碍。如果这样,请你告诉我,我定当尽力改进。如果因此而提高我的表达能力,那将是我最大的荣幸。