
package com.ylsoft.cert;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.security.InvalidKeyException;import java.security.KeyPair;import java.security.KeyPairGenerator;import java.security.KeyStore;import java.security.KeyStoreException;import java.security.NoSuchAlgorithmException;import java.security.NoSuchProviderException;import java.security.PrivateKey;import java.security.SecureRandom;import java.security.Signature;import java.security.SignatureException;import java.security.UnrecoverableKeyException;import java.security.cert.Certificate;import java.security.cert.CertificateException;import java.security.cert.X509Certificate;import java.util.Date;import java.util.Vector;import sun.misc.BASE64Encoder;import sun.security.util.ObjectIdentifier;import sun.security.x509.AlgorithmId;import sun.security.x509.CertAndKeyGen;import sun.security.x509.CertificateAlgorithmId;import sun.security.x509.CertificateExtensions;import sun.security.x509.CertificateSerialNumber;import sun.security.x509.CertificateValidity;import sun.security.x509.CertificateVersion;import sun.security.x509.CertificateX509Key;import sun.security.x509.ExtendedKeyUsageExtension;import sun.security.x509.Extension;import sun.security.x509.KeyIdentifier;import sun.security.x509.KeyUsageExtension;import sun.security.x509.SubjectKeyIdentifierExtension;import sun.security.x509.X500Name;import sun.security.x509.X500Signer;import sun.security.x509.X509CertImpl;import sun.security.x509.X509CertInfo;/** * 首先生成CA的根證書,然后有CA的根證書簽署生成ScriptX的證書 *  * @author Administrator *  */public class GenX509Cert {	/** 提供強加密隨機數生成器 (RNG)* */	private SecureRandom sr;	public GenX509Cert() throws NoSuchAlgorithmException,			NoSuchProviderException {		// 返回實現指定隨機數生成器 (RNG) 算法的 SecureRandom 對象。		sr = SecureRandom.getInstance("SHA1PRNG", "SUN");	}	public void createCert(X509Certificate certificate, PrivateKey rootPrivKey,			KeyPair kp) throws CertificateException, IOException,			InvalidKeyException, NoSuchAlgorithmException,			NoSuchProviderException, SignatureException {		// X.509 v1 證書的抽象類。此類提供了一種訪問 X.509 v1 證書所有屬性的標準方式。		byte certbytes[] = certificate.getEncoded();		// The X509CertImpl class represents an X.509 certificate.		X509CertImpl x509certimpl = new X509CertImpl(certbytes);		// The X509CertInfo class represents X.509 certificate information.		X509CertInfo x509certinfo = (X509CertInfo) x509certimpl				.get("x509.info");		// This class defines the X509Key attribute for the Certificate.		x509certinfo.set("key", new CertificateX509Key(kp.getPublic()));		// This class defines the Extensions attribute for the Certificate		CertificateExtensions certificateextensions = new CertificateExtensions();		certificateextensions.set("SubjectKeyIdentifier",				new SubjectKeyIdentifierExtension((new KeyIdentifier(kp						.getPublic())).getIdentifier()));		x509certinfo.set("extensions", certificateextensions);		// 設置issuer域		X500Name issuer = new X500Name(				"CN=RootCA,OU=hackwp,O=wp,L=BJ,S=BJ,C=CN");		x509certinfo.set("issuer.dname", issuer);		// Constructs a name from a conventionally formatted string, such as		// "CN=Dave, OU=JavaSoft, O=Sun Microsystems, C=US". (RFC 1779 or RFC		// 2253 style).		X500Name subject = new X500Name(				"CN=scriptx, OU=wps, O=wps, L=BJ, ST=BJ, C=CN");		x509certinfo.set("subject.dname", subject);		// 此 Signature 類用來為應用程序提供數字簽名算法功能。返回實現指定簽名算法的 Signature 對象。		Signature signature = Signature.getInstance("MD5WithRSA");		// 初始化這個用于簽名的對象。如果使用其他參數再次調用此方法,此調用的結果將無效。		signature.initSign(kp.getPrivate());		// This class provides a binding between a Signature object and an		// authenticated X.500 name (from an X.509 certificate chain), which is		// needed in many public key signing applications.		X500Signer signer = new X500Signer(signature, issuer);		// This class identifies algorithms, such as cryptographic transforms,		// each of which may be associated with parameters.		AlgorithmId algorithmid = signer.getAlgorithmId();		// This class defines the AlgorithmId for the Certificate.		x509certinfo				.set("algorithmID", new CertificateAlgorithmId(algorithmid));		// 開始時間		Date bdate = new Date();		// 結束時間		Date edate = new Date();		// 天 小時 分 秒 毫秒		edate.setTime(bdate.getTime() + 3650 * 24L * 60L * 60L * 1000L);		// validity為有效時間長度 單位為秒,This class defines the interval for which the		// certificate is valid.證書的有效時間		CertificateValidity certificatevalidity = new CertificateValidity(				bdate, edate);		x509certinfo.set("validity", certificatevalidity);		// This class defines the SerialNumber attribute for the Certificate.		// 設置有效期域(包含開始時間和到期時間)域名等同與x509certinfo.VALIDITY		x509certinfo.set("serialNumber", new CertificateSerialNumber(				(int) (new Date().getTime() / 1000L)));		// 設置序列號域,This class defines the version of the X509 Certificate.		CertificateVersion cv = new CertificateVersion(CertificateVersion.V3);		x509certinfo.set(X509CertInfo.VERSION, cv);		// 設置版本號 只有v1 ,v2,v3這幾個合法值		/**		 * 以上是證書的基本信息 如果要添加用戶擴展信息 則比較麻煩 首先要確定version必須是v3否則不行 然后按照以下步驟		 */		ObjectIdentifier oid = new ObjectIdentifier(new int[] { 2, 5, 29, 15 });		// 生成擴展域的id 是個int數組 第1位最大2 第2位最大39 最多可以幾位不明....		String userData = "Digital Signature, Non-Repudiation, Key Encipherment, Data Encipherment (f0)";		byte l = (byte) userData.length();// 數據總長17位		byte f = 0x04;		byte[] bs = new byte[userData.length() + 2];		bs[0] = f;		bs[1] = l;		for (int i = 2; i < bs.length; i++) {			bs[i] = (byte) userData.charAt(i - 2);		}		Extension ext = new Extension(oid, true, bs);		// 生成一個extension對象 參數分別為 oid,是否關鍵擴展,byte[]型的內容值		// 其中內容的格式比較怪異 第一位是flag 這里取4暫時沒出錯 估計用來說明數據的用處的 第2位是后面的實際數據的長度,然后就是數據		// 密鑰用法		KeyUsageExtension keyUsage = new KeyUsageExtension();		keyUsage.set(KeyUsageExtension.DIGITAL_SIGNATURE, true);		keyUsage.set(KeyUsageExtension.NON_REPUDIATION, true);		keyUsage.set(KeyUsageExtension.KEY_ENCIPHERMENT, true);		keyUsage.set(KeyUsageExtension.DATA_ENCIPHERMENT, true);		// 增強密鑰用法ObjectIdentifier ekeyOid = new ObjectIdentifier(new int[] { 1, 3, 6, 1,				5, 5, 7, 3, 3 });Vector<ObjectIdentifier> vkeyOid = new Vector<ObjectIdentifier>();vkeyOid.add(ekeyOid);		ExtendedKeyUsageExtension exKeyUsage = new ExtendedKeyUsageExtension(				vkeyOid);		CertificateExtensions exts = new CertificateExtensions();		exts.set("keyUsage", keyUsage);		exts.set("extendedKeyUsage", exKeyUsage);		// 如果有多個extension則都放入CertificateExtensions 類中,		x509certinfo.set(X509CertInfo.EXTENSIONS, exts);		// 設置extensions域		X509CertImpl x509certimpl1 = new X509CertImpl(x509certinfo);		x509certimpl1.sign(rootPrivKey, "MD5WithRSA");		// 使用另一個證書的私鑰來簽名此證書 這里使用 md5散列 用rsa來加密		BASE64Encoder base64 = new BASE64Encoder();		FileOutputStream fos = new FileOutputStream(new File("f://ScriptX.crt"));		base64.encodeBuffer(x509certimpl1.getEncoded(), fos);		try {			Certificate[] certChain = { x509certimpl1 };			savePfx("scriptx", kp.getPrivate(), "123456", certChain,					"f://ScriptX.pfx");			FileInputStream in = new FileInputStream("F://ScriptX.pfx");			KeyStore inputKeyStore = KeyStore.getInstance("pkcs12");			inputKeyStore.load(in, "123456".toCharArray());			Certificate cert = inputKeyStore.getCertificate("scriptx");			System.out.print(cert.getPublicKey());			PrivateKey privk = (PrivateKey) inputKeyStore.getKey("scriptx",					"123456".toCharArray());			FileOutputStream privKfos = new FileOutputStream(new File(					"f://ScriptX.pvk"));			privKfos.write(privk.getEncoded());			System.out.print(privk);			// base64.encode(key.getEncoded(), privKfos);			in.close();		} catch (Exception e) {			// TODO Auto-generated catch block			e.printStackTrace();		}		// 生成文件		x509certimpl1.verify(certificate.getPublicKey(), null);	}	/**	 * 保存此根證書信息KeyStore Personal Information Exchange	 * 	 * @param alias	 * @param privKey	 * @param pwd	 * @param certChain	 * @param filepath	 * @throws Exception	 */	public void savePfx(String alias, PrivateKey privKey, String pwd,			Certificate[] certChain, String filepath) throws Exception {		// 此類表示密鑰和證書的存儲設施。		// 返回指定類型的 keystore 對象。此方法從首選 Provider 開始遍歷已注冊安全提供者列表。返回一個封裝 KeyStoreSpi		// 實現的新 KeyStore 對象,該實現取自第一個支持指定類型的 Provider。		KeyStore outputKeyStore = KeyStore.getInstance("pkcs12");		System.out.println("KeyStore類型:" + outputKeyStore.getType());		// 從給定輸入流中加載此 KeyStore。可以給定一個密碼來解鎖 keystore(例如,駐留在硬件標記設備上的 keystore)或檢驗		// keystore 數據的完整性。如果沒有指定用于完整性檢驗的密碼,則不會執行完整性檢驗。如果要創建空		// keystore,或者不能從流中初始化 keystore,則傳遞 null 作為 stream 的參數。注意,如果此 keystore		// 已經被加載,那么它將被重新初始化,并再次從給定輸入流中加載。		outputKeyStore.load(null, pwd.toCharArray());		// 將給定密鑰(已經被保護)分配給給定別名。如果受保護密鑰的類型為		// java.security.PrivateKey,則它必須附帶證明相應公鑰的證書鏈。如果底層 keystore 實現的類型為		// jks,則必須根據 PKCS #8 標準中的定義將 key 編碼為		// EncryptedPrivateKeyInfo。如果給定別名已經存在,則與別名關聯的 keystore		// 信息將被給定密鑰(還可能包括證書鏈)重寫。		outputKeyStore				.setKeyEntry(alias, privKey, pwd.toCharArray(), certChain);		// KeyStore.PrivateKeyEntry pke=new		// KeyStore.PrivateKeyEntry(kp.getPrivate(),certChain);		// KeyStore.PasswordProtection password=new		// KeyStore.PasswordProtection("123456".toCharArray());		// outputKeyStore.setEntry("scriptx", pke, password);		FileOutputStream out = new FileOutputStream(filepath);		// 將此 keystore 存儲到給定輸出流,并用給定密碼保護其完整性。		outputKeyStore.store(out, pwd.toCharArray());		out.close();	}	public void saveJks(String alias, PrivateKey privKey, String pwd,			Certificate[] certChain, String filepath) throws Exception {		KeyStore outputKeyStore = KeyStore.getInstance("jks");		System.out.println(outputKeyStore.getType());		outputKeyStore.load(null, pwd.toCharArray());		outputKeyStore				.setKeyEntry(alias, privKey, pwd.toCharArray(), certChain);		// KeyStore.PrivateKeyEntry pke=new		// KeyStore.PrivateKeyEntry(kp.getPrivate(),certChain);		// KeyStore.PasswordProtection password=new		// KeyStore.PasswordProtection("123456".toCharArray());		// outputKeyStore.setEntry("scriptx", pke, password);		FileOutputStream out = new FileOutputStream(filepath);		outputKeyStore.store(out, pwd.toCharArray());		out.close();	}	/**	 * 頒布根證書,自己作為CA	 * 	 * @throws NoSuchAlgorithmException	 * @throws NoSuchProviderException	 * @throws InvalidKeyException	 * @throws IOException	 * @throws CertificateException	 * @throws SignatureException	 * @throws UnrecoverableKeyException	 */	public void createRootCA() throws NoSuchAlgorithmException,			NoSuchProviderException, InvalidKeyException, IOException,			CertificateException, SignatureException, UnrecoverableKeyException {		// 參數分別為公鑰算法、簽名算法 providername(因為不知道確切的 只好使用null 既使用默認的provider)		// Generate a pair of keys, and provide access to them.		CertAndKeyGen cak = new CertAndKeyGen("RSA", "MD5WithRSA", null);		// Sets the source of random numbers used when generating keys.		cak.setRandom(sr);		// Generates a random public/private key pair, with a given key size.		cak.generate(1024);		// Constructs a name from a conventionally formatted string, such as		// "CN=Dave, OU=JavaSoft, O=Sun Microsystems, C=US". (RFC 1779 or RFC		// 2253 style)		X500Name subject = new X500Name(				"CN=RootCA,OU=hackwp,O=wp,L=BJ,S=BJ,C=CN");		// Returns a self-signed X.509v3 certificate for the public key. The		// certificate is immediately valid. No extensions.		// Such certificates normally are used to identify a "Certificate		// Authority" (CA). Accordingly, they will not always be accepted by		// other parties. However, such certificates are also useful when you		// are bootstrapping your security infrastructure, or deploying system		// prototypes.自簽名的根證書		X509Certificate certificate = cak.getSelfCertificate(subject,				new Date(), 3650 * 24L * 60L * 60L);		X509Certificate[] certs = { certificate };		try {			savePfx("RootCA", cak.getPrivateKey(), "123456", certs,					"f://RootCa.pfx");		} catch (Exception e) {			e.printStackTrace();		}		// 后一個long型參數代表從現在開始的有效期 單位為秒(如果不想從現在開始算 可以在后面改這個域)		BASE64Encoder base64 = new BASE64Encoder();		FileOutputStream fos = new FileOutputStream(new File("f://RootCa.crt"));		// fos.write(certificate.getEncoded());		// 生成(保存)cert文件 base64加密 當然也可以不加密		base64.encodeBuffer(certificate.getEncoded(), fos);		fos.close();	}	public void signCert() throws NoSuchAlgorithmException,			CertificateException, IOException, UnrecoverableKeyException,			InvalidKeyException, NoSuchProviderException, SignatureException {		try {			KeyStore ks = KeyStore.getInstance("pkcs12");			FileInputStream ksfis = new FileInputStream("f://RootCa.pfx");			char[] storePwd = "123456".toCharArray();			char[] keyPwd = "123456".toCharArray();			// 從給定輸入流中加載此 KeyStore。			ks.load(ksfis, storePwd);			ksfis.close();			// 返回與給定別名關聯的密鑰(私鑰),并用給定密碼來恢復它。必須已經通過調用 setKeyEntry,或者以			// PrivateKeyEntry			// 或 SecretKeyEntry 為參數的 setEntry 關聯密鑰與別名。			PrivateKey privK = (PrivateKey) ks.getKey("RootCA", keyPwd);			// 返回與給定別名關聯的證書。如果給定的別名標識通過調用 setCertificateEntry 創建的條目,或者通過調用以			// TrustedCertificateEntry 為參數的 setEntry			// 創建的條目,則返回包含在該條目中的可信證書。如果給定的別名標識通過調用 setKeyEntry 創建的條目,或者通過調用以			// PrivateKeyEntry 為參數的 setEntry 創建的條目,則返回該條目中證書鏈的第一個元素。			X509Certificate certificate = (X509Certificate) ks					.getCertificate("RootCA");			createCert(certificate, privK, genKey());		} catch (KeyStoreException e) {			// TODO Auto-generated catch block			e.printStackTrace();		}	}	public KeyPair genKey() throws NoSuchAlgorithmException {		KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");		kpg.initialize(1024, sr);		System.out.print(kpg.getAlgorithm());		KeyPair kp = kpg.generateKeyPair();		return kp;	}	public static void main(String[] args) {		try {			GenX509Cert gcert = new GenX509Cert();			gcert.createRootCA();			gcert.signCert();		} catch (Exception e) {			// TODO Auto-generated catch block			e.printStackTrace();		}	}}以上這篇純Java實現數字證書生成簽名的簡單實例就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持武林網。
新聞熱點
疑難解答