天天看点

用java实现签发数字证书

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;

import java.io.*;

import java.security.*;

import java.security.cert.*;

import sun.security.x509.*;

import java.util.*;

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());

}

}

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();

}

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());

}

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();

}

//辅助方法===========================================================================

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);

}

private CertificateSerialNumber getCertSerualNumber()

{

Calendar vCal=null;

vCal=Calendar.getInstance();

int vSerialNum=0;

vSerialNum=(int)(vCal.getTimeInMillis()/1000);

return new CertificateSerialNumber(vSerialNum);

}

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 -import -keystore .\serverstore -alias daniel -files .\daniel.cer

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 -import -keystore .\serverstore -alias root -files .\root.cer

双击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安全库里是没有的。