數(shù)字證書也稱電子證書矾芙,由數(shù)字證書頒發(fā)認(rèn)證機(jī)構(gòu)(CA)簽發(fā)才具備可認(rèn)證性。數(shù)字證書采用了公鑰基礎(chǔ)設(shè)施(PKI)潮瓶,使用了相應(yīng)的加密算法確保網(wǎng)絡(luò)應(yīng)用安全性:
- 非對(duì)稱加密算法用于對(duì)數(shù)據(jù)進(jìn)行加密/解密操作伟恶,確保數(shù)據(jù)的機(jī)密性。
- 數(shù)字簽名算法用于數(shù)據(jù)進(jìn)行簽名/驗(yàn)證操作歹苦,確保數(shù)據(jù)的完整性和抗否性。
- 消息摘要算法用于對(duì)數(shù)字證書本身做摘要處理督怜,確保數(shù)字證書完整性殴瘦。
數(shù)字證書常用算法
1、非對(duì)稱加密算法:RSA
号杠、DSA
(無(wú)法完成加密/解密實(shí)現(xiàn)蚪腋,這樣的數(shù)字證書不包括數(shù)據(jù)加密/解密功能)
2、簽名算法:SHA1withRSA
(sha1RSA
)
3姨蟋、消息摘要算法:SHA1
數(shù)字證書文件編碼格式
1屉凯、CER(規(guī)范編碼格式),是BER(基本編碼格式)的一個(gè)變種眼溶,使用變長(zhǎng)模式悠砚。
2、DER(卓越編碼格式)堂飞,也是BER的一個(gè)變種灌旧,并使用定長(zhǎng)模式。
公鑰基礎(chǔ)設(shè)施(PKI)
所有證書都符合PKI
制定的X.509
標(biāo)準(zhǔn)绰筛,如:PKCS
(公鑰加密標(biāo)準(zhǔn))
PKCS常用標(biāo)準(zhǔn)如下:
公鑰加密標(biāo)準(zhǔn) | 描述信息 | 文件名后綴 |
---|---|---|
PKCS#7 | 密碼消息語(yǔ)法標(biāo)準(zhǔn) | .p7b枢泰、.p7c、.spc |
PKCS#10 | 證書請(qǐng)求語(yǔ)法標(biāo)準(zhǔn) | .p10铝噩、.csr |
PKCS#12 | 個(gè)人信息交換語(yǔ)法標(biāo)準(zhǔn) | .p12衡蚂、.pfx |
以上標(biāo)準(zhǔn)主要用于證書的申請(qǐng)和更新等操作。例如:PKCS#10文件用于證書簽發(fā)申請(qǐng),PKCS#12文件可作為JAVA中的密鑰庫(kù)或信任庫(kù)直接而使用讳窟。
數(shù)字證書模型
1、數(shù)字證書頒發(fā)流程
2敞恋、數(shù)字證書服務(wù)請(qǐng)求與響應(yīng)
數(shù)字證書管理
KeyTool證書管理
KeyTool是JAVA中的數(shù)字證書管理工具丽啡,用于數(shù)字證書的申請(qǐng)、導(dǎo)入硬猫、導(dǎo)出和撤銷等操作补箍。KeyTool與本地密鑰庫(kù)相關(guān)聯(lián),將私鑰存于密鑰庫(kù)啸蜜,公鑰則以數(shù)字證書輸出坑雅。
1、構(gòu)建自簽名證書
下面演示先生成一個(gè)自簽名證書衬横,然后導(dǎo)出數(shù)字證書裹粤,最后打印數(shù)字證書。使用keytool工具導(dǎo)出的證書蜂林,是一個(gè)自簽名的X.509第三版類型根證書遥诉,并以Base64編碼保存。但是噪叙,沒(méi)有經(jīng)過(guò)CA機(jī)構(gòu)認(rèn)證矮锈,幾乎不具備任何法律效力。
詳細(xì)參數(shù)說(shuō)明:
生成本地?cái)?shù)字證書
D:\MyData\majx2>keytool -genkeypair -keyalg RSA -keysize 2048 -sigalg SHA1withRSA -validity 36000 -alias www.crazyxing.com -keystore crazyxing.keystore -dname "CN=www.crazyxing.com,OU=crazyxing,O=crazyxing,L=GZ,ST=GD,C=CN"
輸入密鑰庫(kù)口令:
再次輸入新口令:
輸入 <www.crazyxing.com> 的密鑰口令
(如果和密鑰庫(kù)口令相同, 按回車):
Warning:
JKS 密鑰庫(kù)使用專用格式睁蕾。建議使用 "keytool -importkeystore -srckeystore crazyxing.keystore -destkeystore crazyxing.keystore -deststoretype pkcs12" 遷移到行業(yè)標(biāo)準(zhǔn)格式 PKCS12苞笨。
命令參數(shù) | 命令說(shuō)明 |
---|---|
-genkeypair | 表示生成密鑰 |
-keyalg | 指定密鑰算法,這里制定RSA |
-keysize | 指定密鑰長(zhǎng)度子眶,默認(rèn)1024瀑凝,這里制定2048 |
-sigalg | 指定數(shù)字簽名算法,這里指定SHA1withRSA算法 |
-validity | 指定證書有效期壹店,這里指定為36000天 |
-alias | 指定別名猜丹,這里www.crazyxing.com |
-keystore | 指定密鑰庫(kù)存儲(chǔ)位置,這里是crazyxing.keystore |
-storepass | 指定密碼 |
-dname | 值得你個(gè)用戶信息 |
導(dǎo)出數(shù)字證書
D:\MyData\majx2>keytool -exportcert -alias www.crazyxing.com -keystore crazyxing.keystore -file crazyxing.cer -rfc
輸入密鑰庫(kù)口令:
存儲(chǔ)在文件 <crazyxing.cer> 中的證書
Warning:
JKS 密鑰庫(kù)使用專用格式硅卢。建議使用 "keytool -importkeystore -srckeystore crazyxing.keystore -destkeystore crazyxing.keystore -deststoretype pkcs12" 遷移到行業(yè)標(biāo)準(zhǔn)格式 PKCS12射窒。
D:\MyData\majx2>keytool -printcert -file crazyxing.cer
所有者: CN=www.crazyxing.com, OU=crazyxing, O=crazyxing, L=GZ, ST=GD, C=CN
發(fā)布者: CN=www.crazyxing.com, OU=crazyxing, O=crazyxing, L=GZ, ST=GD, C=CN
序列號(hào): 1b2162b5
有效期為 Sat Mar 09 22:11:16 CST 2019 至 Fri Oct 01 22:11:16 CST 2117
證書指紋:
MD5: A4:50:BA:56:C5:84:9A:2F:EE:73:5E:53:D2:AD:F8:4A
SHA1: 17:FF:93:47:A6:C0:26:58:86:60:6E:8C:0A:BE:03:FE:07:53:A2:D7
SHA256: E5:09:24:87:25:F1:C2:CE:4F:F7:D8:D6:CA:07:B8:88:01:70:A4:F1:D9:31:51:B0:C5:D2:F1:9D:F1:D8:FD:22
簽名算法名稱: SHA1withRSA
主體公共密鑰算法: 2048 位 RSA 密鑰
版本: 3
擴(kuò)展:
#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 40 DA 0E 81 94 6C 51 41 57 4A 56 CD 21 D6 8D 5E @....lQAWJV.!..^
0010: 38 48 96 20 8H.
]
]
命令參數(shù) | 命令說(shuō)明 |
---|---|
-exportcert | 表示證書導(dǎo)出操作 |
-alias | 指定別名,這里為www.crazyxing.com |
-keystore | 指定密鑰庫(kù)文件将塑,這里crazyxing.keystore |
-file | 指定導(dǎo)出文件路徑脉顿,這里為crazyxing.cer |
-rfc | 指定為Base64編碼格式輸出 |
-storepass | 指定密碼 |
遷移到行業(yè)標(biāo)準(zhǔn)格式 PKCS12
D:\MyData\majx2>keytool -importkeystore -srckeystore crazyxing.keystore -destkeystore crazyxing.keystore -deststoretype pkcs12
輸入源密鑰庫(kù)口令:
已成功導(dǎo)入別名 www.crazyxing.com 的條目。
已完成導(dǎo)入命令: 1 個(gè)條目成功導(dǎo)入, 0 個(gè)條目失敗或取消
Warning:
已將 "crazyxing.keystore" 遷移到 Non JKS/JCEKS点寥。將 JKS 密鑰庫(kù)作為 "crazyxing.keystore.old" 進(jìn)行了備份艾疟。
D:\MyData\majx2>keytool -exportcert -alias www.crazyxing.com -keystore crazyxing.keystore -file crazyxing.p12 -rfc
輸入密鑰庫(kù)口令:
存儲(chǔ)在文件 <crazyxing.p12> 中的證書
D:\MyData\majx2>keytool -printcert -file crazyxing.p12
所有者: CN=www.crazyxing.com, OU=crazyxing, O=crazyxing, L=GZ, ST=GD, C=CN
發(fā)布者: CN=www.crazyxing.com, OU=crazyxing, O=crazyxing, L=GZ, ST=GD, C=CN
序列號(hào): 1b2162b5
有效期為 Sat Mar 09 22:11:16 CST 2019 至 Fri Oct 01 22:11:16 CST 2117
證書指紋:
MD5: A4:50:BA:56:C5:84:9A:2F:EE:73:5E:53:D2:AD:F8:4A
SHA1: 17:FF:93:47:A6:C0:26:58:86:60:6E:8C:0A:BE:03:FE:07:53:A2:D7
SHA256: E5:09:24:87:25:F1:C2:CE:4F:F7:D8:D6:CA:07:B8:88:01:70:A4:F1:D9:31:51:B0:C5:D2:F1:9D:F1:D8:FD:22
簽名算法名稱: SHA1withRSA
主體公共密鑰算法: 2048 位 RSA 密鑰
版本: 3
擴(kuò)展:
#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 40 DA 0E 81 94 6C 51 41 57 4A 56 CD 21 D6 8D 5E @....lQAWJV.!..^
0010: 38 48 96 20 8H.
]
]
2、構(gòu)建CA簽發(fā)證書
獲取CA機(jī)構(gòu)認(rèn)證的數(shù)字證書,需要將數(shù)字證書簽發(fā)申請(qǐng)(CSR)導(dǎo)出蔽莱,經(jīng)由CA機(jī)構(gòu)認(rèn)證并頒發(fā)弟疆,同時(shí)將認(rèn)證后的證書導(dǎo)入本地鑰匙庫(kù)和信任庫(kù)。
導(dǎo)出數(shù)字證書簽發(fā)申請(qǐng)
D:\MyData\majx2>keytool -certreq -alias www.crazyxing.com -keystore crazyxing.keystore -file crazyxing.pfx -v
輸入密鑰庫(kù)口令:
存儲(chǔ)在文件 <crazyxing.pfx> 中的認(rèn)證請(qǐng)求
將此提交給您的 CA
命令參數(shù) | 命令說(shuō)明 |
---|---|
-certreq | 表示數(shù)字證書申請(qǐng)操作 |
-alias | 指定別名盗冷,這里為www.crazyxing.com |
-keystore | 指定密鑰庫(kù)文件怠苔,這里crazyxing.keystore |
-flie | 指定導(dǎo)出文件路徑,這里crazyxing.pfx(文件后綴仪糖,參考PKCS常用標(biāo)準(zhǔn)) |
-v | 詳細(xì)信息 |
-storepass | 指定密碼 |
導(dǎo)入數(shù)字證書
D:\MyData\majx2>keytool -delete -alias www.crazyxing.com -keystore crazyxing.keystore
輸入密鑰庫(kù)口令:
D:\MyData\majx2>keytool -importcert -trustcacerts -alias www.crazyxing.com -file crazyxing.p12 -keystore crazyxing.keystore
輸入密鑰庫(kù)口令:
所有者: CN=www.crazyxing.com, OU=crazyxing, O=crazyxing, L=GZ, ST=GD, C=CN
發(fā)布者: CN=www.crazyxing.com, OU=crazyxing, O=crazyxing, L=GZ, ST=GD, C=CN
序列號(hào): 1b2162b5
有效期為 Sat Mar 09 22:11:16 CST 2019 至 Fri Oct 01 22:11:16 CST 2117
證書指紋:
MD5: A4:50:BA:56:C5:84:9A:2F:EE:73:5E:53:D2:AD:F8:4A
SHA1: 17:FF:93:47:A6:C0:26:58:86:60:6E:8C:0A:BE:03:FE:07:53:A2:D7
SHA256: E5:09:24:87:25:F1:C2:CE:4F:F7:D8:D6:CA:07:B8:88:01:70:A4:F1:D9:31:51:B0:C5:D2:F1:9D:F1:D8:FD:22
簽名算法名稱: SHA1withRSA
主體公共密鑰算法: 2048 位 RSA 密鑰
版本: 3
擴(kuò)展:
#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 40 DA 0E 81 94 6C 51 41 57 4A 56 CD 21 D6 8D 5E @....lQAWJV.!..^
0010: 38 48 96 20 8H.
]
]
是否信任此證書? [否]: 是
證書已添加到密鑰庫(kù)中
D:\MyData\majx2>keytool -list -alias www.crazyxing.com -keystore crazyxing.keystore
輸入密鑰庫(kù)口令:
www.crazyxing.com, 2019-3-9, trustedCertEntry,
證書指紋 (SHA1): 17:FF:93:47:A6:C0:26:58:86:60:6E:8C:0A:BE:03:FE:07:53:A2:D7
命令參數(shù) | 命令說(shuō)明 |
---|---|
-importcert | 表示導(dǎo)入數(shù)字證書 |
-trustcacerts | 表示將數(shù)字證書導(dǎo)入信任庫(kù) |
-alias | 指定別名 www.crazyxing.com |
-file | 指定導(dǎo)入證書的文件路徑柑司,這里為crazyxing.p12 |
-keystore | 指定密鑰庫(kù)文件,這里為crazyxing.keystore |
-storepass | 指定密碼 |
3锅劝、證書的使用
先實(shí)現(xiàn)一個(gè)認(rèn)證工具:
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.crypto.Cipher;
public abstract class CertificateCoder {
/**
* 類型證書X509
*/
public static final String CERT_TYPE = "X.509";
/**
* 由KeyStore獲得私鑰
*
* @param keyStorePath
* 密鑰庫(kù)路徑
* @param alias
* 別名
* @param password
* 密碼
* @return PrivateKey 私鑰
* @throws Exception
*/
private static PrivateKey getPrivateKeyByKeyStore(String keyStorePath,
String alias, String password) throws Exception {
// 獲得密鑰庫(kù)
KeyStore ks = getKeyStore(keyStorePath, password);
// 獲得私鑰
return (PrivateKey) ks.getKey(alias, password.toCharArray());
}
/**
* 由Certificate獲得公鑰
*
* @param certificatePath
* 證書路徑
* @return PublicKey 公鑰
* @throws Exception
*/
private static PublicKey getPublicKeyByCertificate(String certificatePath)
throws Exception {
// 獲得證書
Certificate certificate = getCertificate(certificatePath);
// 獲得公鑰
return certificate.getPublicKey();
}
/**
* 獲得Certificate
*
* @param certificatePath
* 證書路徑
* @return Certificate 證書
* @throws Exception
*/
private static Certificate getCertificate(String certificatePath)
throws Exception {
// 實(shí)例化證書工廠
CertificateFactory certificateFactory = CertificateFactory
.getInstance(CERT_TYPE);
// 取得證書文件流
FileInputStream in = new FileInputStream(certificatePath);
// 生成證書
Certificate certificate = certificateFactory.generateCertificate(in);
// 關(guān)閉證書文件流
in.close();
return certificate;
}
/**
* 獲得Certificate
*
* @param keyStorePath
* 密鑰庫(kù)路徑
* @param alias
* 別名
* @param password
* 密碼
* @return Certificate 證書
* @throws Exception
*/
private static Certificate getCertificate(String keyStorePath,
String alias, String password) throws Exception {
// 獲得密鑰庫(kù)
KeyStore ks = getKeyStore(keyStorePath, password);
// 獲得證書
return ks.getCertificate(alias);
}
/**
* 獲得KeyStore
*
* @param keyStorePath
* 密鑰庫(kù)路徑
* @param password
* 密碼
* @return KeyStore 密鑰庫(kù)
* @throws Exception
*/
private static KeyStore getKeyStore(String keyStorePath, String password)
throws Exception {
// 實(shí)例化密鑰庫(kù)
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
// 獲得密鑰庫(kù)文件流
FileInputStream is = new FileInputStream(keyStorePath);
// 加載密鑰庫(kù)
ks.load(is, password.toCharArray());
// 關(guān)閉密鑰庫(kù)文件流
is.close();
return ks;
}
/**
* 私鑰加密
*
* @param data
* 待加密數(shù)據(jù)
* @param keyStorePath
* 密鑰庫(kù)路徑
* @param alias
* 別名
* @param password
* 密碼
* @return byte[] 加密數(shù)據(jù)
* @throws Exception
*/
public static byte[] encryptByPrivateKey(byte[] data, String keyStorePath,
String alias, String password) throws Exception {
// 取得私鑰
PrivateKey privateKey = getPrivateKeyByKeyStore(keyStorePath, alias,
password);
// 對(duì)數(shù)據(jù)加密
Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 私鑰解密
*
* @param data
* 待解密數(shù)據(jù)
* @param keyStorePath
* 密鑰庫(kù)路徑
* @param alias
* 別名
* @param password
* 密碼
* @return byte[] 解密數(shù)據(jù)
* @throws Exception
*/
public static byte[] decryptByPrivateKey(byte[] data, String keyStorePath,
String alias, String password) throws Exception {
// 取得私鑰
PrivateKey privateKey = getPrivateKeyByKeyStore(keyStorePath, alias,
password);
// 對(duì)數(shù)據(jù)加密
Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 公鑰加密
*
* @param data
* 待加密數(shù)據(jù)
* @param certificatePath
* 證書路徑
* @return byte[] 加密數(shù)據(jù)
* @throws Exception
*/
public static byte[] encryptByPublicKey(byte[] data, String certificatePath)
throws Exception {
// 取得公鑰
PublicKey publicKey = getPublicKeyByCertificate(certificatePath);
// 對(duì)數(shù)據(jù)加密
Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 公鑰解密
*
* @param data
* 待解密數(shù)據(jù)
* @param certificatePath
* 證書路徑
* @return byte[] 解密數(shù)據(jù)
* @throws Exception
*/
public static byte[] decryptByPublicKey(byte[] data, String certificatePath)
throws Exception {
// 取得公鑰
PublicKey publicKey = getPublicKeyByCertificate(certificatePath);
// 對(duì)數(shù)據(jù)加密
Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 簽名
*
* @param keyStorePath
* 密鑰庫(kù)路徑
* @param alias
* 別名
* @param password
* 密碼
* @return byte[] 簽名
* @throws Exception
*/
public static byte[] sign(byte[] sign, String keyStorePath, String alias,
String password) throws Exception {
// 獲得證書
X509Certificate x509Certificate = (X509Certificate) getCertificate(
keyStorePath, alias, password);
// 構(gòu)建簽名攒驰,由證書指定簽名算法
Signature signature = Signature.getInstance(x509Certificate
.getSigAlgName());
// 獲取私鑰
PrivateKey privateKey = getPrivateKeyByKeyStore(keyStorePath, alias,
password);
// 初始化簽名,由私鑰構(gòu)建
signature.initSign(privateKey);
signature.update(sign);
return signature.sign();
}
/**
* 驗(yàn)證簽名
*
* @param data
* 數(shù)據(jù)
* @param sign
* 簽名
* @param certificatePath
* 證書路徑
* @return boolean 驗(yàn)證通過(guò)為真
* @throws Exception
*/
public static boolean verify(byte[] data, byte[] sign,
String certificatePath) throws Exception {
// 獲得證書
X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath);
// 由證書構(gòu)建簽名
Signature signature = Signature.getInstance(x509Certificate
.getSigAlgName());
// 由證書初始化簽名故爵,實(shí)際上是使用了證書中的公鑰
signature.initVerify(x509Certificate);
signature.update(data);
return signature.verify(sign);
}
}
下面是相應(yīng)的單元測(cè)試:
import org.apache.commons.codec.binary.Hex;
import org.junit.Test;
import static org.junit.Assert.*;
public class CertificateCoderTest {
private String password = "123456";
private String alias = "www.crazyxing.com";
private String certificatePath = "D:/MyData/majx2/crazyxing.cer";
private String keyStorePath = "D:/MyData/majx2/crazyxing.keystore.old";
/**
* 公鑰加密——私鑰解密
*
* @throws Exception
*/
@Test
public void test1() throws Exception {
System.err.println("公鑰加密——私鑰解密");
String inputStr = "Ceritifcate";
byte[] data = inputStr.getBytes();
// 公鑰加密
byte[] encrypt = CertificateCoder.encryptByPublicKey(data,
certificatePath);
// 私鑰解密
byte[] decrypt = CertificateCoder.decryptByPrivateKey(encrypt,
keyStorePath, alias, password);
String outputStr = new String(decrypt);
System.err.println("加密前:\n" + inputStr);
System.err.println("解密后:\n" + outputStr);
// 驗(yàn)證數(shù)據(jù)一致
assertArrayEquals(data, decrypt);
}
/**
* 私鑰加密——公鑰解密
*
* @throws Exception
*/
@Test
public void test2() throws Exception {
System.err.println("私鑰加密——公鑰解密");
String inputStr = "sign";
byte[] data = inputStr.getBytes();
// 私鑰加密
byte[] encodedData = CertificateCoder.encryptByPrivateKey(data,
keyStorePath, alias, password);
// 公鑰加密
byte[] decodedData = CertificateCoder.decryptByPublicKey(encodedData,
certificatePath);
String outputStr = new String(decodedData);
System.err.println("加密前:\n" + inputStr);
System.err.println("解密后:\n" + outputStr);
// 校驗(yàn)
assertEquals(inputStr, outputStr);
}
/**
* 簽名驗(yàn)證
*
* @throws Exception
*/
@Test
public void testSign() throws Exception {
String inputStr = "簽名";
byte[] data = inputStr.getBytes();
System.err.println("私鑰簽名——公鑰驗(yàn)證");
// 產(chǎn)生簽名
byte[] sign = CertificateCoder
.sign(data, keyStorePath, alias, password);
System.err.println("簽名:\n" + Hex.encodeHexString(sign));
// 驗(yàn)證簽名
boolean status = CertificateCoder.verify(data, sign, certificatePath);
System.err.println("狀態(tài):\n" + status);
// 校驗(yàn)
assertTrue(status);
}
}
OpenSSL證書管理
下面示例生成雙向認(rèn)證所需的全部證書玻粪。
安裝OpenSSL工具
1、首先下載OpenSSL
http://slproweb.com/download/Win64OpenSSL-1_1_1b.exe
2诬垂、給OpenSSL設(shè)置環(huán)境變量
3奶段、修改ca證書路徑
編輯%OpenSSL_HOME%\bin\openssl.cfg配置文件,設(shè)置ca路徑
4剥纷、構(gòu)建CA目錄結(jié)構(gòu)
D:\MyData\majx2>mkdir demoCA
D:\MyData\majx2>cd demoCA
# 構(gòu)建已發(fā)行證書存放目錄
D:\MyData\majx2\demoCA>mkdir certs
# 構(gòu)建新證書存放目錄
D:\MyData\majx2\demoCA>mkdir newcerts
# 構(gòu)建私鑰存放目錄
D:\MyData\majx2\demoCA>mkdir private
# 構(gòu)建證書吊銷列表存放目錄
D:\MyData\majx2\demoCA>mkdir cr1
# 構(gòu)建索引文件
D:\MyData\majx2\demoCA>echo 0>index.txt
# 構(gòu)建序列號(hào)文件
D:\MyData\majx2\demoCA>echo 01>serial
構(gòu)建根證書
1痹籍、構(gòu)建隨機(jī)數(shù)
D:\MyData\majx2\demoCA>openssl rand -out private/.rand 1000
命令參數(shù) | 命令說(shuō)明 |
---|---|
-rand | 隨機(jī)數(shù)命令 |
-out | 輸出文件路徑,這里將隨機(jī)數(shù)文件輸出到private目錄下 |
2晦鞋、構(gòu)建根證書
openssl通常使用PEM(隱私增強(qiáng)郵件)編碼格式保存私鑰
D:\MyData\majx2\demoCA>openssl genrsa -aes256 -out private/ca.key.pem 2048
Generating RSA private key, 2048 bit long modulus (2 primes)
...............................................+++++
...................+++++
e is 65537 (0x010001)
Enter pass phrase for private/ca.key.pem:
Verifying - Enter pass phrase for private/ca.key.pem:
命令參數(shù) | 命令說(shuō)明 |
---|---|
genrsa | 產(chǎn)生RSA密鑰命令 |
-aes256 | 使用AES算法(256位密鑰)對(duì)產(chǎn)生的私鑰加密蹲缠。可選算法包括DES/DESede/IDEA/AES |
-out | 輸出路徑悠垛,這里是private/ca.key.pem |
3线定、生成根證書簽發(fā)申請(qǐng)
D:\MyData\majx2\demoCA>openssl req -new -key private/ca.key.pem -out private/ca.csr -subj "/C=CN/ST=GD/L=GZ/O=crazyxing/OU=crazyxing/CN=*.crazyxing.com"
Enter pass phrase for private/ca.key.pem:
命令參數(shù) | 命令說(shuō)明 |
---|---|
req | 生成證書簽發(fā)申請(qǐng)命令 |
-new | 表示新請(qǐng)求 |
-key | 密鑰,這里pirvate/ca.key.pem文件 |
-out | 輸出路徑确买,這里private/ca.csr文件 |
-subj | 指定用戶信息斤讥,這里使用泛域名"*.crazyxing.com"作為用戶名 |
4、簽發(fā)根證書
得到根證書申請(qǐng)文件后湾趾,可以發(fā)送給CA機(jī)構(gòu)簽發(fā)芭商,也可以自行簽發(fā)根證書
D:\MyData\majx2\demoCA>openssl x509 -req -days 10000 -sha1 -extensions v3_ca -signkey private/ca.key.pem -in private/ca.csr -out certs/ca.cer
Signature ok
subject=C = CN, ST = GD, L = GZ, O = crazyxing, OU = crazyxing, CN = *.crazyxing.com
Getting Private key
Enter pass phrase for private/ca.key.pem:
命令參數(shù) | 命令說(shuō)明 |
---|---|
x509 | 簽發(fā)X.509格式證書命令 |
-req | 表示證書輸入請(qǐng)求 |
-days | 表示有效天數(shù),這里10000天 |
-sha1 | 表示證書摘要算法搀缠,這里為SHA1算法 |
-extensions | 表示按OpenSSL配置文件v3_ca項(xiàng)添加擴(kuò)展 |
-signkey | 表示自簽名密鑰铛楣,這里private/ca.key.pem |
-in | 表示輸入文件,這里private/ca.csr |
-out | 表示輸出文件艺普,這里certs/ca.cer |
5簸州、根證書轉(zhuǎn)換
OpenSSL產(chǎn)生的數(shù)字證書不能在Java語(yǔ)言中直接使用鉴竭,需要將其轉(zhuǎn)化成PKCS#12編碼格式
D:\MyData\majx2\demoCA>openssl pkcs12 -export -cacerts -inkey private/ca.key.pem -in certs/ca.cer -out certs/ca.p12
Enter pass phrase for private/ca.key.pem:
Enter Export Password:
Verifying - Enter Export Password:
D:\MyData\majx2\demoCA>keytool -list -keystore certs/ca.p12 -storetype pkcs12 -v -storepass 123456
密鑰庫(kù)類型: PKCS12
密鑰庫(kù)提供方: SunJSSE
您的密鑰庫(kù)包含 1 個(gè)條目
別名: 1
創(chuàng)建日期: 2019-3-10
條目類型: PrivateKeyEntry
證書鏈長(zhǎng)度: 1
證書[1]:
所有者: CN=*.crazyxing.com, OU=crazyxing, O=crazyxing, L=GZ, ST=GD, C=CN
發(fā)布者: CN=*.crazyxing.com, OU=crazyxing, O=crazyxing, L=GZ, ST=GD, C=CN
序列號(hào): 6fad9a9cb9fb61a60c976403ee5fe42228c956fa
有效期為 Sun Mar 10 09:29:11 CST 2019 至 Thu Jul 26 09:29:11 CST 2046
證書指紋:
MD5: 4D:FA:6B:F3:A9:91:16:37:34:C1:7E:E3:66:1B:3C:A3
SHA1: F8:52:09:30:17:02:07:CF:82:45:4C:92:66:7A:85:73:C6:BE:40:4D
SHA256: 03:5B:03:ED:C8:15:9F:B5:76:3F:F6:F9:43:EB:7D:4A:ED:B5:6F:88:73:0D:C2:7C:3C:CB:08:6A:04:05:56:F4
簽名算法名稱: SHA1withRSA
主體公共密鑰算法: 2048 位 RSA 密鑰
版本: 1
*******************************************
*******************************************
命令參數(shù) | 命令說(shuō)明 |
---|---|
pkcs12 | PKCS#12編碼格式證書命令 |
-export | 表示導(dǎo)出證書 |
-cacerts | 表示僅導(dǎo)出CA證書 |
-inkey | 表示輸入密鑰,這里為private/ca.key.pem |
-in | 表示輸入文件岸浑,這里為certs/ca.cer |
-out | 表示輸出文件搏存,這里為certs/ca.p12 |
構(gòu)建服務(wù)器證書
1、構(gòu)建服務(wù)器私鑰
D:\MyData\majx2\demoCA>openssl genrsa -aes256 -out private/server.key.pem 2048
Generating RSA private key, 2048 bit long modulus (2 primes)
.......................................+++++
.................................................................................................................................................+++++
e is 65537 (0x010001)
Enter pass phrase for private/server.key.pem:
Verifying - Enter pass phrase for private/server.key.pem:
命令參數(shù) | 命令說(shuō)明 |
---|---|
genrsa | 產(chǎn)生RSA密鑰命令 |
-aes256 | 使用AES算法(256位密鑰)對(duì)產(chǎn)生的私鑰加密矢洲〖拦。可選算法包括DES/DESede/IDEA/AES |
-out | 輸出路徑,這里是private/server.key.pem |
2兵钮、生成服務(wù)器證書簽發(fā)申請(qǐng)
D:\MyData\majx2\demoCA>openssl req -new -key private/server.key.pem -out private/server.csr -subj "/C=CN/ST=GD/L=GZ/O=crazyxing/OU=crazyxing/CN=www.crazyxing.com"
Enter pass phrase for private/server.key.pem:
命令參數(shù) | 命令說(shuō)明 |
---|---|
req | 生成證書簽發(fā)申請(qǐng)命令 |
-new | 表示新請(qǐng)求 |
-key | 密鑰,這里pirvate/server.key.pem文件 |
-out | 輸出路徑舌界,這里private/server.csr文件 |
-subj | 指定用戶信息掘譬,這里使用泛域名"www.crazyxing.com"作為用戶名 |
3、簽發(fā)服務(wù)器證書
D:\MyData\majx2\demoCA>openssl x509 -req -days 3650 -sha1 -extensions v3_req -CA certs/ca.cer -CAkey private/ca.key.pem -CAserial ca.sr1 -CAcreateserial -in private/server.csr -out certs/server.cer
Signature ok
subject=C = CN, ST = GD, L = GZ, O = crazyxing, OU = crazyxing, CN = www.crazyxing.com
Getting CA Private Key
Enter pass phrase for private/ca.key.pem:
命令參數(shù) | 命令說(shuō)明 |
---|---|
x509 | 簽發(fā)X.509格式證書命令 |
-req | 表示證書輸入請(qǐng)求 |
-days | 表示有效天數(shù)呻拌,這里10000天 |
-sha1 | 表示證書摘要算法葱轩,這里為SHA1算法 |
-extensions | 表示按OpenSSL配置文件v3_ca項(xiàng)添加擴(kuò)展 |
-CA | 表示CA證書,這里certs/ca.cer |
-CAkey | 表示CA證書密鑰藐握,這里private/ca.key.pem |
-CAserial | 表示CA證書序列號(hào)文件靴拱,這里ca.sr1 |
-CAcreateserial | 表示創(chuàng)建CA證書序列號(hào) |
-in | 表示輸入文件,這里private/ca.csr |
-out | 表示輸出文件猾普,這里certs/ca.cer |
4袜炕、服務(wù)器證書轉(zhuǎn)換
D:\MyData\majx2\demoCA>openssl pkcs12 -export -clcerts -inkey private/server.key.pem -in certs/server.cer -out certs/server.p12
Enter pass phrase for private/server.key.pem:
Enter Export Password:
Verifying - Enter Export Password:
命令參數(shù) | 命令說(shuō)明 |
---|---|
pkcs12 | PKCS#12編碼格式證書命令 |
-export | 表示導(dǎo)出證書 |
-clcerts | 表示僅導(dǎo)出客戶證書 |
-inkey | 表示輸入密鑰,這里為private/server.key.pem |
-in | 表示輸入文件初家,這里為certs/server.cer |
-out | 表示輸出文件偎窘,這里為certs/server.p12 |
構(gòu)建客戶端證書
1、產(chǎn)生客戶密鑰
D:\MyData\majx2\demoCA>openssl genrsa -aes256 -out private/client.key.pem 2048
Generating RSA private key, 2048 bit long modulus (2 primes)
................................................................................................+++++
.................+++++
e is 65537 (0x010001)
Enter pass phrase for private/client.key.pem:
Verifying - Enter pass phrase for private/client.key.pem:
命令參數(shù) | 命令說(shuō)明 |
---|---|
genrsa | 產(chǎn)生RSA密鑰命令 |
-aes256 | 使用AES算法(256位密鑰)對(duì)產(chǎn)生的私鑰加密溜在∧爸可選算法包括DES/DESede/IDEA/AES |
-out | 輸出路徑,這里是private/server.key.pem |
2掖肋、生成客戶證書簽發(fā)申請(qǐng)
D:\MyData\majx2\demoCA>openssl req -new -key private/client.key.pem -out private/client.csr -subj "/C=CN/ST=GD/L=GZ/O=crazyxing/OU=crazyxing/CN=crazyxing"
Enter pass phrase for private/client.key.pem:
命令參數(shù) | 命令說(shuō)明 |
---|---|
req | 生成證書簽發(fā)申請(qǐng)命令 |
-new | 表示新請(qǐng)求 |
-key | 密鑰仆葡,這里pirvate/client.key.pem文件 |
-out | 輸出路徑,這里private/client.csr文件 |
-subj | 指定用戶信息志笼,這里使用"crazyxing"作為用戶名 |
3沿盅、簽發(fā)客戶證書
D:\MyData\majx2\demoCA>openssl ca -days 3650 -in private/client.csr -out certs/client.cer -cert certs/ca.cer -keyfile private/ca.key.pem
Using configuration from C:\Program Files\Common Files\SSL/openssl.cnf
Enter pass phrase for private/ca.key.pem:
Check that the request matches the signature
Signature ok
Certificate Details:
Serial Number: 1 (0x1)
Validity
Not Before: Mar 10 02:44:01 2019 GMT
Not After : Mar 7 02:44:01 2029 GMT
Subject:
countryName = CN
stateOrProvinceName = GD
organizationName = crazyxing
organizationalUnitName = crazyxing
commonName = crazyxing
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
Netscape Comment:
OpenSSL Generated Certificate
X509v3 Subject Key Identifier:
AB:6F:ED:7E:D7:15:C2:6B:8C:F4:C7:5E:20:5A:15:A0:5C:DF:ED:8E
X509v3 Authority Key Identifier:
DirName:/C=CN/ST=GD/L=GZ/O=crazyxing/OU=crazyxing/CN=*.crazyxing.com
serial:6F:AD:9A:9C:B9:FB:61:A6:0C:97:64:03:EE:5F:E4:22:28:C9:56:FA
Certificate is to be certified until Mar 7 02:44:01 2029 GMT (3650 days)
Sign the certificate? [y/n]:y
1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated
命令參數(shù) | 命令說(shuō)明 |
---|---|
ca | 簽發(fā)證書命令 |
-days | 表示有效天數(shù),這里3650天 |
-in | 表示輸入文件纫溃,這里private/client.csr |
-out | 表示輸出文件嗡呼,這里certs/client.cer |
-cert | 表示證書文件,這里為certs/ca.cer |
-keyfile | 表示根證書密鑰文件皇耗,這里為private/ca.key.pem |
4南窗、客戶證書轉(zhuǎn)換
D:\MyData\majx2\demoCA>openssl pkcs12 -export -inkey private/client.key.pem -in certs/client.cer -out certs/client.p12
Enter pass phrase for private/client.key.pem:
Enter Export Password:
Verifying - Enter Export Password:
命令參數(shù) | 命令說(shuō)明 |
---|---|
pkcs12 | PKCS#12編碼格式證書命令 |
-export | 表示導(dǎo)出證書 |
-clcerts | 表示僅導(dǎo)出客戶證書 |
-inkey | 表示輸入密鑰,這里為private/client.key.pem |
-in | 表示輸入文件,這里為certs/client.cer |
-out | 表示輸出文件万伤,這里為certs/client.p12 |
證書使用
先給出一個(gè)證書工具類
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.crypto.Cipher;
public abstract class CertificateCoder {
/**
* 證書類型X509
*/
public static final String CERT_TYPE = "X.509";
/**
* 密鑰庫(kù)類型PCKS12
*/
private static final String STORE_TYPE = "PKCS12";
/**
* 由KeyStore獲得私鑰
*
* @param keyStorePath
* 密鑰庫(kù)路徑
* @param alias
* 別名
* @param password
* 密碼
* @return PrivateKey 私鑰
* @throws Exception
*/
public static PrivateKey getPrivateKeyByKeyStore(String keyStorePath,
String alias, String password) throws Exception {
// 獲得密鑰庫(kù)
KeyStore ks = getKeyStore(keyStorePath, password);
// 獲得私鑰
return (PrivateKey) ks.getKey(alias, password.toCharArray());
}
/**
* 由Certificate獲得公鑰
*
* @param certificatePath
* 證書路徑
* @return PublicKey 公鑰
* @throws Exception
*/
public static PublicKey getPublicKeyByCertificate(String certificatePath)
throws Exception {
// 獲得證書
Certificate certificate = getCertificate(certificatePath);
// 獲得公鑰
return certificate.getPublicKey();
}
/**
* 獲得Certificate
*
* @param certificatePath
* 證書路徑
* @return Certificate 證書
* @throws Exception
*/
private static X509Certificate getCertificate(String certificatePath)
throws Exception {
// 實(shí)例化證書工廠
CertificateFactory certificateFactory = CertificateFactory
.getInstance(CERT_TYPE);
// 取得證書文件流
FileInputStream in = new FileInputStream(certificatePath);
// 生成證書
Certificate certificate = certificateFactory.generateCertificate(in);
// 關(guān)閉證書文件流
in.close();
return (X509Certificate) certificate;
}
/**
* 獲得KeyStore
*
* @param keyStorePath
* 密鑰庫(kù)路徑
* @param password
* 密碼
* @return KeyStore 密鑰庫(kù)
* @throws Exception
*/
private static KeyStore getKeyStore(String keyStorePath, String password)
throws Exception {
// 實(shí)例化密鑰庫(kù)
KeyStore ks = KeyStore.getInstance(STORE_TYPE);
// 獲得密鑰庫(kù)文件流
FileInputStream in = new FileInputStream(keyStorePath);
// 加載密鑰庫(kù)
ks.load(in, password.toCharArray());
// 關(guān)閉密鑰庫(kù)文件流
in.close();
return ks;
}
/**
* 私鑰加密
*
* @param data
* 待加密數(shù)據(jù)
* @param keyStorePath
* 密鑰庫(kù)路徑
* @param alias
* 別名
* @param password
* 密碼
* @return byte[] 加密數(shù)據(jù)
* @throws Exception
*/
public static byte[] encryptByPrivateKey(byte[] data, String keyStorePath,
String alias, String password) throws Exception {
// 取得私鑰
PrivateKey privateKey = getPrivateKeyByKeyStore(keyStorePath, alias,
password);
// 對(duì)數(shù)據(jù)加密
Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 私鑰解密
*
* @param data
* 待解密數(shù)據(jù)
* @param keyStorePath
* 密鑰庫(kù)路徑
* @param alias
* 別名
* @param password
* 密碼
* @return byte[] 解密數(shù)據(jù)
* @throws Exception
*/
public static byte[] decryptByPrivateKey(byte[] data, String keyStorePath,
String alias, String password) throws Exception {
// 取得私鑰
PrivateKey privateKey = getPrivateKeyByKeyStore(keyStorePath, alias,
password);
// 對(duì)數(shù)據(jù)加密
Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 公鑰加密
*
* @param data
* 待加密數(shù)據(jù)
* @param certificatePath
* 證書路徑
* @return byte[] 加密數(shù)據(jù)
* @throws Exception
*/
public static byte[] encryptByPublicKey(byte[] data, String certificatePath)
throws Exception {
// 取得公鑰
PublicKey publicKey = getPublicKeyByCertificate(certificatePath);
// 對(duì)數(shù)據(jù)加密
Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 公鑰解密
*
* @param data
* 待解密數(shù)據(jù)
* @param certificatePath
* 證書路徑
* @return byte[] 解密數(shù)據(jù)
* @throws Exception
*/
public static byte[] decryptByPublicKey(byte[] data, String certificatePath)
throws Exception {
// 取得公鑰
PublicKey publicKey = getPublicKeyByCertificate(certificatePath);
// 對(duì)數(shù)據(jù)加密
Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 簽名
*
* @param keyStorePath
* 密鑰庫(kù)路徑
* @param alias
* 別名
* @param password
* 密碼
* @return byte[] 簽名
* @throws Exception
*/
public static byte[] sign(byte[] sign, String keyStorePath, String alias,
String password, String certificatePath) throws Exception {
// 獲得證書
X509Certificate x509Certificate = getCertificate(certificatePath);
// 構(gòu)建簽名窒悔,由證書指定簽名算法
Signature signature = Signature.getInstance(x509Certificate
.getSigAlgName());
// 獲取私鑰
PrivateKey privateKey = getPrivateKeyByKeyStore(keyStorePath, alias,
password);
// 初始化簽名,由私鑰構(gòu)建
signature.initSign(privateKey);
signature.update(sign);
return signature.sign();
}
/**
* 驗(yàn)證簽名
*
* @param data
* 數(shù)據(jù)
* @param sign
* 簽名
* @param certificatePath
* 證書路徑
* @return boolean 驗(yàn)證通過(guò)為真
* @throws Exception
*/
public static boolean verify(byte[] data, byte[] sign,
String certificatePath) throws Exception {
// 獲得證書
X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath);
// 由證書構(gòu)建簽名
Signature signature = Signature.getInstance(x509Certificate
.getSigAlgName());
// 由證書初始化簽名敌买,實(shí)際上是使用了證書中的公鑰
signature.initVerify(x509Certificate);
signature.update(data);
return signature.verify(sign);
}
}
下面是相應(yīng)的單元測(cè)試
import org.apache.commons.codec.binary.Hex;
import org.junit.Test;
import static org.junit.Assert.*;
public class CertificateCoderTest {
private String password = "123456";
private String alias = "1";
private String certificatePath = "D:/MyData/majx2/demoCA/certs/ca.cer";
private String keyStorePath = "D:/MyData/majx2/demoCA/certs/ca.p12";
/**
* 公鑰加密——私鑰解密
*
* @throws Exception
*/
@Test
public void test1() {
try {
System.err.println("公鑰加密——私鑰解密");
String inputStr = "Ceritifcate";
byte[] data = inputStr.getBytes();
// 公鑰加密
byte[] encrypt = CertificateCoder.encryptByPublicKey(data,
certificatePath);
// 私鑰解密
byte[] decrypt = CertificateCoder.decryptByPrivateKey(encrypt,
keyStorePath, alias, password);
String outputStr = new String(decrypt);
System.err.println("加密前:\n" + inputStr);
System.err.println("解密后:\n" + outputStr);
// 驗(yàn)證數(shù)據(jù)一致
assertArrayEquals(data, decrypt);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
fail(e.getMessage());
}
}
/**
* 私鑰加密——公鑰解密
*
* @throws Exception
*/
@Test
public void test2() {
System.err.println("私鑰加密——公鑰解密");
String inputStr = "sign";
byte[] data = inputStr.getBytes();
try {
// 私鑰加密
byte[] encodedData = CertificateCoder.encryptByPrivateKey(data,
keyStorePath, alias, password);
// 公鑰加密
byte[] decodedData = CertificateCoder.decryptByPublicKey(
encodedData, certificatePath);
String outputStr = new String(decodedData);
System.err.println("加密前:\n" + inputStr);
System.err.println("解密后:\n" + outputStr);
// 校驗(yàn)
assertEquals(inputStr, outputStr);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
fail(e.getMessage());
}
}
/**
* 簽名驗(yàn)證
*
* @throws Exception
*/
@Test
public void testSign() {
try {
String inputStr = "簽名";
byte[] data = inputStr.getBytes();
System.err.println("私鑰簽名——公鑰驗(yàn)證");
// 產(chǎn)生簽名
byte[] sign = CertificateCoder.sign(data, keyStorePath, alias,
password,certificatePath);
System.err.println("簽名:\n" + Hex.encodeHexString(sign));
// 驗(yàn)證簽名
boolean status = CertificateCoder.verify(data, sign,
certificatePath);
System.err.println("狀態(tài):\n" + status);
// 校驗(yàn)
assertTrue(status);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
fail(e.getMessage());
}
}
}