用JAVA签发数字证书
前一篇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
推荐阅读