純Java實(shí)現(xiàn)數(shù)字證書生成簽名的簡單實(shí)例

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 {
/** 提供強(qiáng)加密隨機(jī)數(shù)生成器 (RNG)* */
private SecureRandom sr;
public GenX509Cert() throws NoSuchAlgorithmException,
NoSuchProviderException {
// 返回實(shí)現(xiàn)指定隨機(jī)數(shù)生成器 (RNG) 算法的 SecureRandom 對(duì)象。
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 證書所有屬性的標(biāo)準(zhǔn)方式。
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);
// 設(shè)置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 類用來為應(yīng)用程序提供數(shù)字簽名算法功能。返回實(shí)現(xiàn)指定簽名算法的 Signature 對(duì)象。
Signature signature = Signature.getInstance("MD5WithRSA");
// 初始化這個(gè)用于簽名的對(duì)象。如果使用其他參數(shù)再次調(diào)用此方法,此調(diào)用的結(jié)果將無效。
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));
// 開始時(shí)間
Date bdate = new Date();
// 結(jié)束時(shí)間
Date edate = new Date();
// 天 小時(shí) 分 秒 毫秒
edate.setTime(bdate.getTime() + 3650 * 24L * 60L * 60L * 1000L);
// validity為有效時(shí)間長度 單位為秒,This class defines the interval for which the
// certificate is valid.證書的有效時(shí)間
CertificateValidity certificatevalidity = new CertificateValidity(
bdate, edate);
x509certinfo.set("validity", certificatevalidity);
// This class defines the SerialNumber attribute for the Certificate.
// 設(shè)置有效期域(包含開始時(shí)間和到期時(shí)間)域名等同與x509certinfo.VALIDITY
x509certinfo.set("serialNumber", new CertificateSerialNumber(
(int) (new Date().getTime() / 1000L)));
// 設(shè)置序列號(hào)域,This class defines the version of the X509 Certificate.
CertificateVersion cv = new CertificateVersion(CertificateVersion.V3);
x509certinfo.set(X509CertInfo.VERSION, cv);
// 設(shè)置版本號(hào) 只有v1 ,v2,v3這幾個(gè)合法值
/**
* 以上是證書的基本信息 如果要添加用戶擴(kuò)展信息 則比較麻煩 首先要確定version必須是v3否則不行 然后按照以下步驟
*/
ObjectIdentifier oid = new ObjectIdentifier(new int[] { 2, 5, 29, 15 });
// 生成擴(kuò)展域的id 是個(gè)int數(shù)組 第1位最大2 第2位最大39 最多可以幾位不明....
String userData = "Digital Signature, Non-Repudiation, Key Encipherment, Data Encipherment (f0)";
byte l = (byte) userData.length();// 數(shù)據(jù)總長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);
// 生成一個(gè)extension對(duì)象 參數(shù)分別為 oid,是否關(guān)鍵擴(kuò)展,byte[]型的內(nèi)容值
// 其中內(nèi)容的格式比較怪異 第一位是flag 這里取4暫時(shí)沒出錯(cuò) 估計(jì)用來說明數(shù)據(jù)的用處的 第2位是后面的實(shí)際數(shù)據(jù)的長度,然后就是數(shù)據(jù)
// 密鑰用法
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);
// 增強(qiáng)密鑰用法
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);
// 如果有多個(gè)extension則都放入CertificateExtensions 類中,
x509certinfo.set(X509CertInfo.EXTENSIONS, exts);
// 設(shè)置extensions域
X509CertImpl x509certimpl1 = new X509CertImpl(x509certinfo);
x509certimpl1.sign(rootPrivKey, "MD5WithRSA");
// 使用另一個(gè)證書的私鑰來簽名此證書 這里使用 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 {
// 此類表示密鑰和證書的存儲(chǔ)設(shè)施。
// 返回指定類型的 keystore 對(duì)象。此方法從首選 Provider 開始遍歷已注冊(cè)安全提供者列表。返回一個(gè)封裝 KeyStoreSpi
// 實(shí)現(xiàn)的新 KeyStore 對(duì)象,該實(shí)現(xiàn)取自第一個(gè)支持指定類型的 Provider。
KeyStore outputKeyStore = KeyStore.getInstance("pkcs12");
System.out.println("KeyStore類型:" + outputKeyStore.getType());
// 從給定輸入流中加載此 KeyStore??梢越o定一個(gè)密碼來解鎖 keystore(例如,駐留在硬件標(biāo)記設(shè)備上的 keystore)或檢驗(yàn)
// keystore 數(shù)據(jù)的完整性。如果沒有指定用于完整性檢驗(yàn)的密碼,則不會(huì)執(zhí)行完整性檢驗(yàn)。如果要?jiǎng)?chuàng)建空
// keystore,或者不能從流中初始化 keystore,則傳遞 null 作為 stream 的參數(shù)。注意,如果此 keystore
// 已經(jīng)被加載,那么它將被重新初始化,并再次從給定輸入流中加載。
outputKeyStore.load(null, pwd.toCharArray());
// 將給定密鑰(已經(jīng)被保護(hù))分配給給定別名。如果受保護(hù)密鑰的類型為
// java.security.PrivateKey,則它必須附帶證明相應(yīng)公鑰的證書鏈。如果底層 keystore 實(shí)現(xiàn)的類型為
// jks,則必須根據(jù) PKCS #8 標(biāo)準(zhǔn)中的定義將 key 編碼為
// EncryptedPrivateKeyInfo。如果給定別名已經(jīng)存在,則與別名關(guān)聯(lián)的 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 存儲(chǔ)到給定輸出流,并用給定密碼保護(hù)其完整性。
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 {
// 參數(shù)分別為公鑰算法、簽名算法 providername(因?yàn)椴恢来_切的 只好使用null 既使用默認(rèn)的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();
}
// 后一個(gè)long型參數(shù)代表從現(xiàn)在開始的有效期 單位為秒(如果不想從現(xiàn)在開始算 可以在后面改這個(gè)域)
BASE64Encoder base64 = new BASE64Encoder();
FileOutputStream fos = new FileOutputStream(new File("f:\\RootCa.crt"));
// fos.write(certificate.getEncoded());
// 生成(保存)cert文件 base64加密 當(dāng)然也可以不加密
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();
// 返回與給定別名關(guān)聯(lián)的密鑰(私鑰),并用給定密碼來恢復(fù)它。必須已經(jīng)通過調(diào)用 setKeyEntry,或者以
// PrivateKeyEntry
// 或 SecretKeyEntry 為參數(shù)的 setEntry 關(guān)聯(lián)密鑰與別名。
PrivateKey privK = (PrivateKey) ks.getKey("RootCA", keyPwd);
// 返回與給定別名關(guān)聯(lián)的證書。如果給定的別名標(biāo)識(shí)通過調(diào)用 setCertificateEntry 創(chuàng)建的條目,或者通過調(diào)用以
// TrustedCertificateEntry 為參數(shù)的 setEntry
// 創(chuàng)建的條目,則返回包含在該條目中的可信證書。如果給定的別名標(biāo)識(shí)通過調(diào)用 setKeyEntry 創(chuàng)建的條目,或者通過調(diào)用以
// PrivateKeyEntry 為參數(shù)的 setEntry 創(chuàng)建的條目,則返回該條目中證書鏈的第一個(gè)元素。
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實(shí)現(xiàn)數(shù)字證書生成簽名的簡單實(shí)例就是小編分享給大家的全部內(nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java變量命名規(guī)則詳解及常見命名錯(cuò)誤(建議收藏)
這篇文章主要介紹了Java中變量命名的規(guī)則及最佳實(shí)踐,包括有效字符、大小寫敏感性、不能使用保留字、駝峰命名法、描述性命名、特定類型的命名習(xí)慣、避免潛在問題、常見命名錯(cuò)誤及如何避免等內(nèi)容,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-02-02
解決J2EE-session在瀏覽器關(guān)閉后失效問題
最近做項(xiàng)目使用的是Spring+SpringMVC+Mybatis框架,maven管理目錄的javaweb端系統(tǒng),對(duì)于session的一些問題,在此小編給大家分享到腳本之家平臺(tái),需要的朋友參考下吧2018-01-01
java獲取登錄者IP和登錄時(shí)間的兩種實(shí)現(xiàn)代碼詳解
這篇文章主要介紹了java獲取登錄者IP和登錄時(shí)間的實(shí)現(xiàn)代碼,本文通過兩種結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07
SpringMVC 跨重定向請(qǐng)求傳遞數(shù)據(jù)的方法實(shí)現(xiàn)
這篇文章主要介紹了SpringMVC 跨重定向請(qǐng)求傳遞數(shù)據(jù)的方法實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
基于UDP協(xié)議實(shí)現(xiàn)聊天系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了基于UDP協(xié)議實(shí)現(xiàn)聊天系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-04-04
MybatisPlus處理大表查詢的實(shí)現(xiàn)步驟
在實(shí)際工作中當(dāng)指定查詢數(shù)據(jù)過大時(shí),我們一般使用分頁查詢的方式一頁一頁的將數(shù)據(jù)放到內(nèi)存處理,本文主要介紹了MybatisPlus處理大表查詢的實(shí)現(xiàn)步驟,感興趣的可以了解一下2024-08-08
解決使用this.getClass().getResource()獲取文件時(shí)遇到的坑
這篇文章主要介紹了解決使用this.getClass().getResource()獲取文件時(shí)遇到的坑問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12

