標籤:安全 認證 cert certificate
在前面說到,訊息摘要用於驗證資料完整性,對稱與非對稱式加密用於保證資料保密性,資料簽名用於資料的抗否認性,於是集這些安全手段於一身的終極武器--數位憑證出現了。數位憑證具備了加密/解密的必要資訊,包含簽名演算法,可用於網路資料加密/解密互動,標識網路使用者(電腦)身份。資料認證為發布公開金鑰提供了一種簡便途徑,成為密碼編譯演算法以及公開金鑰的載體。
數位憑證有多種檔案編碼格式,主要包含CER編碼、DER編碼等:
a.CER(Canonical Encoding Rules,規範編碼格式),是數位憑證的一種編碼格式,它是BER(Basic Encoding Rules,基本編碼格式)的一個變種,比BER規定得更加嚴格。
b.DER(Distinguished Encoding Rule, 卓越編碼格式),同樣是BER的一個變種,與CER不的同之處在於:DER使用定長模式,而CER使用變長模式。
c.PKCS(Public-Key Cryptography Standards,公開金鑰加密標準),由RSA實驗室和其它安全系統開發商為促進公開金鑰密碼發展而制定的一系列標準。
其中CER、DER格式認證都符合公開金鑰基礎設施(PKI)制定的X509國際標準(X.509標準),統稱為X509格式認證。PKCS至今共發布過15個標準,常用標準包括PKCS#7、PKCS#10和PKCS#12。PKCS#7為密碼訊息文法標準,檔案名稱尾碼一般為:.p7b、.p7c、.spc;PKCS10#為認證請求文法標準,故認證請求檔案採用該格式,檔案名稱尾碼一般為:.p10、.csr;PKCS#12為個人資訊交換文法標準,故個人資訊認證採用該格式,檔案名稱尾碼一般為:.p12、.pfx。值得一提的是雖然PKCS#12為一種認證格式,但在Java中,個人更覺得是一種KeyStore格式,因為PKCS#12格式檔案中既可以儲存認證,還可以儲存私密金鑰,而一般意義說來,認證是不包含私密金鑰資訊的。
一、數位憑證具體包含了哪些資訊,我們以12306的數位憑證為例:
從上面的可以看到,一張數位憑證中包含了很多資訊,主要有,版本號碼、序號、簽名演算法、簽名雜湊演算法、頒發者、有效期間、使用者、公開金鑰、指紋演算法、指紋以及一些數位憑證的擴充屬性資訊。
二、認證如何擷取
假如你的公司要上線一個購物網站,那麼肯定會使用到https協議,也就肯定會使用到數位憑證,那麼數位憑證從哪裡來?是自己產生嗎?當然不是,正確的做法是,公司產生一個認證請求檔案,再把認證請求檔案提交給認證認證機構,然後認證認證機構使用其根憑證再根據認證請求檔案中的資訊為您產生受信任認證,也就是頒發認證,當然要讓認證認證機構為您公司頒發認證這是需要money的。但是有些時候也可以自己建立認證,只不過這時候的認證頒者是你自己,只是別人資訊你自己的根憑證,認證也可以照常使用。
三、認證是否合法(受信任)
說到認證是否受信任的問題就得Crowdsourced Security Testing道一條規則:受信任的認證所頒發的認證也是受信任的。那麼先有雞還是先有蛋的問題來了,第一個受信任的認證是從哪裡來的,為瞭解決這個問題就引出了根憑證,根憑證是自己頒發給自己的認證,只要信任了根憑證,這樣由根憑證頒發的認證也就可以被信任,所以憑證發行其實是一種樹形結構,根憑證可以頒發一級認證,一級認證在允許的情況下也可以為其它使用者頒發二級認證。以Windows, IE為例(IE共用Windows系統中的認證),在Windows安裝好後,系統就已經信任了一些權威認證認證機構的根憑證,當然你也可以匯入你自己製作的根憑證。
四、何為憑證發行
憑證發行是個專業術語,其實就是使用憑證簽發者的私密金鑰對認證使用者的認證進行簽名,並設定使用者認證的頒發者,認證一般情況下需要由權威的認證認證機構頒發,其原就是對認證進行簽名使用的是私密金鑰,私密金鑰只有頒發機構才有。
下面就看看在Java中是如何完成對認證的各種操作的:
package com.xtayfjpk.security.certificate;import java.io.ByteArrayOutputStream;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.math.BigInteger;import java.security.Key;import java.security.KeyFactory;import java.security.KeyPair;import java.security.KeyPairGenerator;import java.security.PrivateKey;import java.security.Provider;import java.security.PublicKey;import java.security.Security;import java.security.Signature;import java.security.cert.CertificateFactory;import java.security.cert.X509Certificate;import java.security.spec.RSAPublicKeySpec;import java.util.Date;import java.util.HashMap;import java.util.Map;import javax.security.auth.x500.X500Principal;import org.bouncycastle.asn1.DERBMPString;import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;import org.bouncycastle.asn1.x500.X500Name;import org.bouncycastle.asn1.x509.AlgorithmIdentifier;import org.bouncycastle.asn1.x509.Certificate;import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;import org.bouncycastle.cert.X509CertificateHolder;import org.bouncycastle.cert.X509v3CertificateBuilder;import org.bouncycastle.crypto.params.AsymmetricKeyParameter;import org.bouncycastle.crypto.params.RSAKeyParameters;import org.bouncycastle.crypto.util.PrivateKeyFactory;import org.bouncycastle.crypto.util.PublicKeyFactory;import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;import org.bouncycastle.jce.provider.BouncyCastleProvider;import org.bouncycastle.operator.ContentSigner;import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder;import org.bouncycastle.pkcs.PKCS10CertificationRequest;import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;import org.bouncycastle.x509.X509V3CertificateGenerator;import org.junit.Before;import org.junit.Test;/** * issuer憑證簽發者 * subject認證使用者 * * DN:Distinguish Name * 格式:CN=姓名,OU=組織單位名稱,O=組織名稱,L=城市或地區名稱,ST=省/市/自治區名稱,C=國家雙字母 * */@SuppressWarnings("deprecation")public class CertifacateGenerateTest {private static final String KEY_PAIR_ALG = "RSA";private static final String SIG_ALG = "SHA1withRSA";private static final String DN_ZHANGSAN = "CN=zhangsan,OU=development,O=Huawei,L=ShenZhen,ST=GuangDong,C=CN";private static final String DN_CA = "CN=Kingyea,OU=Kingyea,O=Kingyea,L=GuangZou,ST=GuangDong,C=CN";private static Map<String, String> algorithmMap = new HashMap<>();static {/** * 演算法名稱與演算法標識符映射 */algorithmMap.put("1.2.840.113549.1.1.5", SIG_ALG);algorithmMap.put("1.2.840.113549.1.1.1", KEY_PAIR_ALG);}@Beforepublic void before() {//註冊BC Provider,因為有些關於認證的操作使用到了BouncyCastle這個第三方庫就順便註冊上了,其實不註冊也行Provider provider = new BouncyCastleProvider();Security.addProvider(provider);}/** * 產生根憑證公開金鑰與私密金鑰對 */@Testpublic void testGenRootKeyPair() throws Exception {KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_PAIR_ALG);keyPairGenerator.initialize(2048);KeyPair keyPair = keyPairGenerator.generateKeyPair();writeObject("H:/certtest/Kingyea.public", keyPair.getPublic());writeObject("H:/certtest/Kingyea.private", keyPair.getPrivate());}/** * 產生使用者認證公開金鑰與私密金鑰對 * @throws Exception */@Testpublic void testZhangsanKeyPair() throws Exception {KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_PAIR_ALG);keyPairGenerator.initialize(2048);KeyPair keyPair = keyPairGenerator.generateKeyPair();writeObject("H:/certtest/zhangsan.public", keyPair.getPublic());writeObject("H:/certtest/zhangsan.private", keyPair.getPrivate());}/** * 產生根憑證(被BC廢棄,但可以使用) */@Testpublic void testGenRootCert() throws Exception {X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();//設定憑證簽發者certGen.setIssuerDN(new X500Principal(DN_CA));//設定認證有效期間certGen.setNotAfter(new Date(System.currentTimeMillis()+ 100 * 24 * 60 * 60 * 1000));certGen.setNotBefore(new Date());//設定認證公開金鑰certGen.setPublicKey(getRootPublicKey());//設定認證序號certGen.setSerialNumber(BigInteger.TEN);//設定簽名演算法certGen.setSignatureAlgorithm(SIG_ALG);//設定認證使用者certGen.setSubjectDN(new X500Principal(DN_CA));//使用私密金鑰產生認證,主要是為了進行簽名操作X509Certificate certificate = certGen.generate(getRootPrivateKey());PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)certificate;bagAttr.setBagAttribute( PKCSObjectIdentifiers.pkcs_9_at_friendlyName, new DERBMPString("Kingyea Coperation Certificate"));writeFile("H:/certtest/ca.cer", certificate.getEncoded());}/** * 產生根憑證的另外一種方式 * @throws Exception */@Testpublic void testGenRootCertWithBuilder() throws Exception {final AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(SIG_ALG);final AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);PublicKey publicKey = getRootPublicKey();PrivateKey privateKey = getRootPrivateKey();X500Name issuer = new X500Name(DN_CA);BigInteger serial = BigInteger.TEN;Date notBefore = new Date();Date notAfter = new Date(System.currentTimeMillis()+ 100 * 24 * 60 * 60 * 1000);X500Name subject = new X500Name(DN_CA);AlgorithmIdentifier algId = AlgorithmIdentifier.getInstance(PKCSObjectIdentifiers.rsaEncryption.toString());System.out.println(algId.getAlgorithm());AsymmetricKeyParameter publicKeyParameter = PublicKeyFactory.createKey(publicKey.getEncoded());SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(publicKeyParameter);//此種方式不行,產生認證不完整//SubjectPublicKeyInfo publicKeyInfo = new SubjectPublicKeyInfo(algId, publicKey.getEncoded());X509v3CertificateBuilder x509v3CertificateBuilder = new X509v3CertificateBuilder(issuer, serial, notBefore, notAfter, subject, publicKeyInfo);BcRSAContentSignerBuilder contentSignerBuilder = new BcRSAContentSignerBuilder(sigAlgId, digAlgId);AsymmetricKeyParameter privateKeyParameter = PrivateKeyFactory.createKey(privateKey.getEncoded());ContentSigner contentSigner = contentSignerBuilder.build(privateKeyParameter);X509CertificateHolder certificateHolder = x509v3CertificateBuilder.build(contentSigner);Certificate certificate = certificateHolder.toASN1Structure();writeFile("H:/certtest/ca.cer", certificate.getEncoded());}/** * 產生使用者認證 */@Testpublic void testGenZhangsanCert() throws Exception {X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();certGen.setIssuerDN(new X500Principal(DN_CA));certGen.setNotAfter(new Date(System.currentTimeMillis()+ 100 * 24 * 60 * 60 * 1000));certGen.setNotBefore(new Date());certGen.setPublicKey(getZhangsanPublicKey());certGen.setSerialNumber(BigInteger.TEN);certGen.setSignatureAlgorithm(SIG_ALG);certGen.setSubjectDN(new X500Principal(DN_ZHANGSAN));X509Certificate certificate = certGen.generate(getRootPrivateKey());writeFile("H:/certtest/zhangsan.cer", certificate.getEncoded());}/** * 驗證根憑證簽名 */@Testpublic void testVerifyRootCert() throws Exception {CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");FileInputStream inStream = new FileInputStream("H:/certtest/ca.cer");X509Certificate certificate = (X509Certificate) certificateFactory.generateCertificate(inStream);System.out.println(certificate);Signature signature = Signature.getInstance(certificate.getSigAlgName());signature.initVerify(certificate);signature.update(certificate.getTBSCertificate());boolean legal = signature.verify(certificate.getSignature());System.out.println(legal);}/** * 驗證使用者認證簽名 */@Testpublic void testVerifyZhangsanCert() throws Exception {CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");FileInputStream inStream = new FileInputStream("H:/certtest/zhangsan.cer");X509Certificate certificate = (X509Certificate) certificateFactory.generateCertificate(inStream);System.out.println(certificate.getPublicKey().getClass());Signature signature = Signature.getInstance(certificate.getSigAlgName());signature.initVerify(getRootPublicKey());signature.update(certificate.getTBSCertificate());boolean legal = signature.verify(certificate.getSignature());System.out.println(legal);}/** * 產生認證請求檔案 */@Testpublic void testGenCSR() throws Exception {X500Name subject = new X500Name(DN_ZHANGSAN);AsymmetricKeyParameter keyParameter = PrivateKeyFactory.createKey(getZhangsanPrivateKey().getEncoded());SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(keyParameter);PKCS10CertificationRequestBuilder certificationRequestBuilder = new PKCS10CertificationRequestBuilder(subject, publicKeyInfo);final AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(SIG_ALG);final AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);BcRSAContentSignerBuilder contentSignerBuilder = new BcRSAContentSignerBuilder(sigAlgId, digAlgId);PKCS10CertificationRequest certificationRequest = certificationRequestBuilder.build(contentSignerBuilder.build(keyParameter));System.out.println(certificationRequest);writeFile("H:/certtest/zhangsan.csr", certificationRequest.getEncoded());}/** * 根據認證請求檔案產生使用者認證,其實主要是使用根憑證私密金鑰為其簽名 */@Testpublic void testZhangsanCertWithCSR() throws Exception {byte[] encoded = readFile("H:/certtest/zhangsan.csr");PKCS10CertificationRequest certificationRequest = new PKCS10CertificationRequest(encoded);RSAKeyParameters parameter = (RSAKeyParameters) PublicKeyFactory.createKey(certificationRequest.getSubjectPublicKeyInfo());RSAPublicKeySpec keySpec = new RSAPublicKeySpec(parameter.getModulus(), parameter.getExponent());String algorithm = algorithmMap.get(certificationRequest.getSubjectPublicKeyInfo().getAlgorithm().getAlgorithm().toString());PublicKey publicKey = KeyFactory.getInstance(algorithm).generatePublic(keySpec);System.out.println(certificationRequest.getSubject());X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();certGen.setIssuerDN(new X500Principal(DN_CA));certGen.setNotAfter(new Date(System.currentTimeMillis()+ 100 * 24 * 60 * 60 * 1000));certGen.setNotBefore(new Date());certGen.setPublicKey(publicKey);certGen.setSerialNumber(BigInteger.TEN);certGen.setSignatureAlgorithm(algorithmMap.get(certificationRequest.getSignatureAlgorithm().getAlgorithm().toString()));certGen.setSubjectDN(new X500Principal(certificationRequest.getSubject().toString()));X509Certificate certificate = certGen.generate(getRootPrivateKey());writeFile("H:/certtest/zhangsan.cer", certificate.getEncoded());}public PrivateKey getRootPrivateKey() throws Exception {return PrivateKey.class.cast(readKey("H:/certtest/Kingyea.private"));}public PublicKey getRootPublicKey() throws Exception {return PublicKey.class.cast(readKey("H:/certtest/Kingyea.public"));}public PrivateKey getZhangsanPrivateKey() throws Exception {return PrivateKey.class.cast(readKey("H:/certtest/zhangsan.private"));}public PublicKey getZhangsanPublicKey() throws Exception {return PublicKey.class.cast(readKey("H:/certtest/zhangsan.public"));}public byte[] readFile(String path) throws Exception {FileInputStream cntInput = new FileInputStream(path);ByteArrayOutputStream baos = new ByteArrayOutputStream();int b = -1;while((b=cntInput.read())!=-1) {baos.write(b);}cntInput.close();byte[] contents = baos.toByteArray();baos.close();return contents;}public void writeFile(String path, byte[] content) throws Exception {FileOutputStream fos = new FileOutputStream(path);fos.write(content);fos.close();}public void writeObject(String path, Object object) throws Exception {ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));oos.writeObject(object);oos.close();}public Object readObject(String path) throws Exception {ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));Object obj = ois.readObject();ois.close();return obj;}public Key readKey(String path) throws Exception {ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));Key key = Key.class.cast(ois.readObject());ois.close();return key;}}
在根憑證產生後雙擊開啟,Windows會提示:該CA根憑證不受信任。要啟用信任,將該認證安裝到”可信任的根憑證授權單位“儲存區。將自己產生的根憑證安裝到可信任的根憑證授權單位後,你會發現其頒發的使用者認證也受信任了。
Java安全之數位憑證