前一篇http://zhuyuehua.iteye.com/blog/1101041 说到创建TOMCAT下HTTPS项目,现在接着说,如何签发证书。
打开cmd
1.输入D:
2.输入cd keys
3.输入命令查看创建的密钥库的证书列表
keytool -list -v -keystore mykey.keystore -storepass 123456
如下图:
4. 之前导出过server.cer文件,把这个文件安装到操作系统,过程默认。
5.创建一个密钥对,和之前一样,输入如下命令:
--创建密钥对
keytool -genkey -dname "CN=tmp, OU=NC, O=Shanghai University, L=ZB, ST=Shanghai,C=CN" -alias client -keyalg RSA -keystore myclientkey.store -keypass 654321 -storepass 654321 -validity 1000
--导出公钥
keytool -export -trustcacerts -alias client -file client.cer -keystore myclientkey.store -storepass 654321
双击client.cer可以看到:
该证书不被系统信任,且颁发者和颁发给都是tmp
5.签发证书,这里采用的是JAVA程序来签发。可以用OPENSSL签发或者到正式CA机构签发。
由于已经把server.cer安装到客户机电脑上,所以客户机操作系统会信任server.cer中的机构签发的证书。
这里就用server.cer中的机构来签发client.cer
签发代码:
package com.syspro.test;
import java.io.*;
import java.security.*;
import java.security.cert.*;
import java.util.*;
import java.math.*;
import sun.security.x509.*;
public class SignCert {
private String mKeystore = "D:/keys/mykey.keystore"; // 密锁库路径
private char[] mKeystorePass = "123456".toCharArray();// 密锁库密码
private char[] mSignPrivateKeyPass = "123456".toCharArray();// 取得签发者私锁所需的密码
private String mSignCertAlias = "keytest";// 签发者别名
private String mSignedCert = "D:/keys/client.cer"; // 被签证书
private String mNewCert = "D:/keys/clientSignKey.cer"; // 签发后的新证书全名
private int mValidityDay = 3000; // 签发后的新证书有效期(天)
private PrivateKey mSignPrivateKey = null;// 签发者的私锁
private X509CertInfo mSignCertInfo = null;// 签发证书信息
private X509CertInfo mSignedCertInfo = null;// 被签证书信息
public void Sign() throws Exception{
try {
/**
* 证书签名
*/
getSignCertInfo(); // 获取签名证书信息
signCertificate(); // 用签名证书信息签发待签名证书
createNewCertificate(); // 创建并保存签名后的新证书
} catch (Exception e) {
System.out.println("Error:" + e.getMessage());
}
}
/**
* 取得签名证书信息
* @throws Exception
*/
private void getSignCertInfo() throws Exception
{
FileInputStream vFin=null;
KeyStore vKeyStore=null;
java.security.cert.Certificate vCert=null;
X509CertImpl vCertImpl=null;
byte[] vCertData=null;
//获取签名证书密锁库
vFin=new FileInputStream(mKeystore);
vKeyStore=KeyStore.getInstance("JKS");
vKeyStore.load(vFin,mKeystorePass);
//获取签名证书
vCert= vKeyStore.getCertificate(mSignCertAlias);
vCertData=vCert.getEncoded();
vCertImpl=new X509CertImpl(vCertData);
//获取签名证书信息
mSignCertInfo=(X509CertInfo)vCertImpl.get(X509CertImpl.NAME+"."+X509CertImpl.INFO);
mSignPrivateKey=(PrivateKey)vKeyStore.getKey(mSignCertAlias,mSignPrivateKeyPass);
vFin.close();
}
/**
* 取得待签证书信息,并签名待签证书
*
* @throws Exception
*/
private void signCertificate() throws Exception {
FileInputStream vFin = null;
java.security.cert.Certificate vCert = null;
CertificateFactory vCertFactory = null;
byte[] vCertData = null;
X509CertImpl vCertImpl = null;
// 获取待签名证书
vFin = new FileInputStream(mSignedCert);
vCertFactory = CertificateFactory.getInstance("X.509");
vCert = vCertFactory.generateCertificate(vFin);
vFin.close();
vCertData = vCert.getEncoded();
// 设置签名证书信息:有效日期、序列号、签名者、数字签名算发
vCertImpl = new X509CertImpl(vCertData);
mSignedCertInfo = (X509CertInfo) vCertImpl.get(X509CertImpl.NAME + "."
+ X509CertImpl.INFO);
mSignedCertInfo.set(X509CertInfo.VALIDITY, getCertValidity());
mSignedCertInfo.set(X509CertInfo.SERIAL_NUMBER, getCertSerualNumber());
mSignedCertInfo.set(X509CertInfo.ISSUER + "."
+ CertificateIssuerName.DN_NAME,
mSignCertInfo.get(X509CertInfo.SUBJECT + "."
+ CertificateIssuerName.DN_NAME));
mSignedCertInfo.set(CertificateAlgorithmId.NAME + "."
+ CertificateAlgorithmId.ALGORITHM, getAlgorithm());
}
/**
* 待签签证书被签名后,保存新证书
*
* @throws Exception
*/
private void createNewCertificate() throws Exception {
FileOutputStream vOut = null;
X509CertImpl vCertImpl = null;
// 用新证书信息封成为新X.509证书
vCertImpl = new X509CertImpl(mSignedCertInfo);
// 生成新正书验证码
vCertImpl.sign(mSignPrivateKey, "MD5WithRSA");
vOut = new FileOutputStream(mNewCert );
// 保存为der编码二进制X.509格式证书
vCertImpl.derEncode(vOut);
vOut.close();
}
// 辅助方法===========================================================================
/**
* 得到新证书有效日期
*
* @throws Exception
* @return CertificateValidity
*/
private CertificateValidity getCertValidity() throws Exception {
long vValidity = (60 * 60 * 24 * 1000L) * mValidityDay;
Calendar vCal = null;
Date vBeginDate = null, vEndDate = null;
vCal = Calendar.getInstance();
vBeginDate = vCal.getTime();
vEndDate = vCal.getTime();
vEndDate.setTime(vBeginDate.getTime() + vValidity);
return new CertificateValidity(vBeginDate, vEndDate);
}
/**
* 得到新证书的序列号
*
* @return CertificateSerialNumber
*/
private CertificateSerialNumber getCertSerualNumber() {
Calendar vCal = null;
vCal = Calendar.getInstance();
int vSerialNum = 0;
vSerialNum = (int) (vCal.getTimeInMillis() / 1000);
return new CertificateSerialNumber(vSerialNum);
}
/**
* 得到新证书的签名算法
*
* @return AlgorithmId
*/
private AlgorithmId getAlgorithm() {
AlgorithmId vAlgorithm = new AlgorithmId(
AlgorithmId.md5WithRSAEncryption_oid);
return vAlgorithm;
}
public static void main(String args[]) throws UnsupportedEncodingException
{
SignCert s = new SignCert();
try {
s.Sign();
} catch (Exception e) {
e.printStackTrace();
}
}
}
打开签名完成后的新证书clientSignKey.cer,如下图:
会看到此时,操作系统会信任该证书,而且颁发者变成了server.cer中的localhost
再将签名后的数字证书clientSignKey.cer和CA证书server.cer导入myclientkey.store库中,命令如下:注意先后顺序
先倒入CA证书
keytool -import -alias ca -keystore D:\keys\myclientkey.store -trustcacerts -file D:\keys\server.cer -storepass 654321
注意-alias 和之前的不一样。
再导入签名后的证书:
keytool -import -alias client -keystore D:\keys\myclientkey.store -trustcacerts -file D:\keys\
clientSignKey.cer -storepass 654321
注意-alias 和之前的一样。
操作完后修改tomcat的server.xml文件,如下:
<Connector protocol="org.apache.coyote.http11.Http11NioProtocol" port="8443" enableLookups="true" disableUploadTimeout="true" acceptCount="100" maxThreads="200" scheme="https" secure="true" SSLEnabled="true" sslProtocol="TLS" clientAuth="false" keystoreFile="D:\keys\myclientkey.store" keystorePass="654321" />
然后启动tomcat,就会发现不会再提示了。
注:由于myclientkey.store的cn不是localhost,所以之前那样的设置打开网站时还是会提示不信任的证书。解决办法是将
myclientkey.store的cn写成localhost(你需要访问的网站地址),当然之前那个CA的密钥库的CN就不要写成localhost了,
随便写个别的就好。
注意,这样之后httpclient还是不会信任该证书,可以这样操作:
切换到<java-home>\jar\lib\security\目录下:
keytool -import -noprompt -keystore cacerts -storepass changeit -alias yourEntry1 -file
clientSignKey.cer