1. 程式人生 > >用java實現簽發數字證書

用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.*;

/**

* 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 -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安全庫裡是沒有的。