from http://renhl169.blog.163.com/blog/static/20365322201023194724282/
最近研究了一下数字签名和关于证书相关。
证书必须通过CA权威机构签发,但在开发期间有多种途径实现签发证书用于测试:
1)去相关ca获取测试证书,一般有效期在15-30天
2)用keytool工具可以生成证书,但不能实现签发.
3)用openssl实现,不过对c或c++不熟悉的用起来比较麻烦
4)利用weblogic提供的CertGen实现签发。
综合上述几种途径(这几中方法多少都要配合keytool实现),第一种是最简单的,
但为了深入了解证书签发过程,我自己写了个工具实现了证书签发。
下面是工具的代码,编译后直接运行,供大家学习参考:
package com.app;
imp
imp
imp
imp
imp
/**
* Title:签发证书
* Description: 用于根证书签发
* Copyright: Copyright (c) 2004.8.12
* Company: smartcomm
* @author 大脸猫(billdengyj)
* @version 1.0
*/
public class SignCertificate
{
private String mKeystore=""; //密锁库路径
private char[] mKeystorePass=null;//密锁库密码
private char[] mSignPrivateKeyPass=null;//取得签发者私锁所需的密码
private String mSignCertAlias="";//签发者别名
private String mSignedCert=""; //被签证书
private String mNewCert=""; //签发后的新证书全名
private int mValidityDay=3; //签发后的新证书有效期(天)
private PrivateKey mSignPrivateKey=null;//签发者的私锁
private X509CertInfo mSignCertInfo=null;//签发证书信息
private X509CertInfo mSignedCertInfo=null;//被签证书信息
public static void main(String args[])
{
/**
* 参数检查
*/
String vArgs0=null;
if(args.length==0 || (args.length!=7 && !args[0].equals("/")))
{
System.out.println("参数错误,可以加参数'/'查询用法");
System.exit(1);
}
vArgs0=args[0];
if(vArgs0.equals("/"))
{
System.out.println(
"语法:********************************************************************");
System.out.println("java com.app.SignCertificate keystore keystorepass " +
"signCertAlias signPrivateKeyPass signedCert newCert validity");
System.out.println(
"说明:********************************************************************");
System.out.println("keystore:密锁库完整地址");
System.out.println("keystorepass:打开密锁库的密码");
System.out.println("signCertAlias:用于签名的证书别名");
System.out.println("signPrivateKeyPass:用取得签名待签证书的私锁密码");
System.out.println("signedCert:待签名证书完整路径");
System.out.println("newCert:被签名后的新证书保存路径全名");
System.out.println("validity:被签名后的新证书有效期(天)");
System.out.println(
"=======================================================================");
System.out.println("必须提供签名证书保存的密锁库、待签名证书等信息,最后被签名的证书(.cer)被保存到新文件中");
System.exit(1);
}
SignCertificate vSignCert=new SignCertificate();
vSignCert.mKeystore=vArgs0;
vSignCert.mKeystorePass=args[1].toCharArray();
vSignCert.mSignCertAlias=args[2];
vSignCert.mSignPrivateKeyPass=args[3].toCharArray();
vSignCert.mSignedCert=args[4];
vSignCert.mNewCert=args[5];
vSignCert.mValidityDay=Integer.parseInt(args[6]);
try
{
/**
* 证书签名
*/
vSignCert.getSignCertInfo(); //获取签名证书信息
vSignCert.signCertificate(); //用签名证书信息签发待签名证书
vSignCert.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+".cer");
//保存为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;
}
}
**************************************************************************************************
使用方法:
这个工具是用来做签名的,所以在使用前必须有几个准备工作:
1)用keytool工具生成用于签名的根证书,密锁库为serverstore:
c:\keytool -genkey -keystore .\serverstore -alias root -keyalg RSA -keysize 1024
---期间会有相关提问。特别注意分清keystore密码和privateKey密码!!!!!!
2)用keytool工具生成待签名的证书,密锁库为serverstore:
c:\keytool -genkey -keystore .\serverstore -alias daniel -keyalg RSA -keysize 1024
---期间会有相关提问。特别注意分清keystore密码和privateKey密码!!!!!!
3)导出待签名证书:
c:\keytool -imp
4)使用编译后的工具,用root签名daniel证书(见(2)(3))
java com.app.SignCertificate keystore keystorepass signCertAlias signPrivateKeyPass signedCert newCert validity
--使用语法可以用 ”java com.app.SignCertificate /?“ 查询。
keystore:密锁库完整地址
keystorepass:打开密锁库的密码
signCertAlias:用于签名的证书别名
signPrivateKeyPass:提取签名证书私锁的密码
signedCert:待签名证书完整路径
newCert:被签名后的新证书保存路径全名
validity:被签名后的新证书有效期(天)
5)第4步完成后,会生成你指定的newCert证书,这个证书是被root签名过的。
6)验证证书是否有效:
在windows下你可以先导出root证书:c:\keytool -imp
双击root.cer安装,在安装daniel.cer,你会发现daniel.cer是有效的。
具体签名后的证书用途可以参考相关资料,这里就不多说了。以上代码在jdk1.4.2.05下通过。
***********************************************************************************
程序的关键是使用了sun.security.x509.X509CertImpl和sun.security.x509.X509CertInfo类
X509CertImpl继承了java.security.cert.Certificate,主要实现了x509证书信息的设置、而
标准的java.security.cert.X509Certificate 有实现;X509CertInfo实现了x509证书信息封装,
在标准的java安全库里是没有的。