天天看點

java生成證書_純Java實作數字證書生成簽名的簡單執行個體

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;

public class GenX509Cert {

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這幾個合法值

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 vkeyOid = new Vector();

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

}

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

}

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

}

}

}