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