本文讨论在简体中文Linux系统中的Tomcat Web服务器上制作数字证书的方法。
数字证书采用PKI(Public Key Infrastructure)公开密钥基础架构技术,利用一对互相匹配的密钥进行加密和解密。但在内部系统应用中,不需要专门CA(认证授权中心)的认证证书,可自己建立认证服务器。可以用Windows 2003 Server证书颁发机构作为CA,用Java制作中文申请后向Windows 2003 Server证书颁发机构CA申请证书,这样就可以得到简体中文数字证书了。本文讨论在简体中文Linux系统中的Tomcat Web服务器上制作数字证书的方法。
证书的产生方法
在Linux下做CA,必须安装openssl,该软件的最新版本可以从www.openssl.org下载。不过,即使直到最新版本,openssl输入简体中文信息后产生的证书也会显示为乱码。还好,有两个办法可以解决这个问题: 一是修改openssl源程序; 另一个是用UTF-8编码进行处理。由于前一种方法比较复杂,本文主要讨论后一种方法。在输入中文字符时,openssl可以正确处理UTF-8的字符编码,不能处理简体中文的gb2312编码。于是可将简体中文的用户信息输入到配置文件中,再转换为UTF-8编码,生成证书申请时使用配置文件的缺省信息,即可产生正确显示中文的证书。其方法如下:
1.假设配置文件为openssl.cnf,编辑修改如下内容:
string_mask=utf8only
countryName_default=CN stateOrProvinceName_default=省份
localityName_default=城市 O.organizationName_default=部门
organizationalUnitName_default=单位 commonName_default=名字
2.用iconv命令将文件格式转换为UTF-8编码:
iconv -f gbk -t utf-8 openssl.cnf > openssl_utf8.cnf
3.用openssl生成证书签名请求时,加上”-utf8”和将配置文件指定为openssl_utf8.cnf:
openssl req -utf8 -config openssl_utf8.cnf -new -out req.csr
除了输入密码,其他的一律选择回车,使用显示为乱码的缺省值,就可产生包含正确中文信息的证书申请文件req.csr,这样的申请方式可用于CA和客户证书的申请。Tomcat服务器证书需要生成keystore文件,用Java工具keytool命令生成,安装简体中文版本的Java,可正确生成简体中文的证书申请。
证书生成步骤和使用
1. 生成自签名认证的CA证书ca-cert.p12。其中在生成CA申请时,CA的中文信息要使用上述的UTF-8中文编码格式。
openssl req -config ca/openssl_ca_utf8.cnf -new -out ca/ca-req.csr -key ca/ca-key.pem
2. 生成服务器证书。Tomcat Web服务器使用keystore文件,内含CA根证书和服务器证书,需要使用Java的keytool工具生成keystore文件。不过如果使用dname选项直接在命令行中写入,可减少中文输入法的麻烦。
keytool -genkey -alias tomcat -dname “CN=域名,OU=部门,O=组织,L=城市,S=省份 ,C=CN” -validity 365 -keyalg RSA -keysize 1024 -keystore server/server-keystore
最后将CA证书和生成的服务器证书用keytool命令导入server-keystore文件。
3. 生成客户证书client-cert.p12。在生成客户证书申请时,要为每个客户的中文信息生成UTF-8中文编码文件:
openssl req -config client/openssl_cl_utf8.cnf -new -out client/client-req.csr -key client/client-key.pem
Tomcat使用keystore文件存放证书,并在server.xml设置安全连接以使用证书,server.xml配置例子如下,假设使用8443端口支持https:
将ca-cert.p12和client-cert.p12导入客户浏览器或USBKey中,即可使用证书,用“https://域名: 8443 URL”即可访问服务器。
JSP中文编码转换
从以上过程得到的服务器证书是简体中文编码的,而CA和客户证书使用的是UTF-8编码。在显示和使用中都没有问题。若应用中服务器需要处理客户证书的信息,JSP可使用X509Certificate对象获得证书信息。如果要显示该客户证书信息,则需要使用UTF-8编码转换,而且页面设置为UTF-8编码:
String subdn_utf8 = new String(subdn.getBytes(“ISO8859_1”), “UTF-8”);
在国内环境中,一般需要简体中文gb2312(gbk)编码,不但需要显示中文,而且还需要处理简体中文编码信息。这就要求在jsp中将获取的UTF-8编码转换为gb2312编码。但问题是Java JDK本身没有提供编码转换的类,所有和编码有关的类都在sun.io.*和Java.nio.charset.*中,而自己编程转换代码又比较麻烦。
但是,JDK隐藏了一个私有类java.lang.StringCoding可实现编码转换。需要做的是复制java.lang.StringCoding源程序,在自己的项目中建立一个StringCoding类,将需要部分改为public,即可利用StringCoding的函数实现代码转换。步骤如下:
首先,在自己项目文件创建一个类StringCoding.java,拷贝源程序,并将类定义、构造函数、编码转换要用到的编码、解码函数改为“public”属性。
public static char[] decode(String charsetName, byte[] ba, int off, int len)
public static byte[] encode(String charsetName, char[] ca, int off, int len)
然后,在jsp应用程序中import StringCoding类,先获取UTF-8编码字节流,通过一系列编解码操作得到简体中文gb2312编码字符串。要点是在UTF-8字符数组下解码为简体中文字节数组才能正确转换代码。
String subdn=cert.getSubjectDN().toString();
byte[] byteutf8 = subdn.getBytes(“iso8859_1”);
char[] charutf8 = StringCoding.decode(“utf-8″,byteutf8,0,byteutf8.length);
byte[] bytegb = StringCoding.encode(“gb2312″,charutf8,0,charutf8.length);
String subdn_gb=new String(bytegb,”gb2312″);