Java安全之数字证书

   在前面说到,消息摘要用于验证数据完整性,对称与非对称加密用于保证数据保密性,数据签名用于数据的抗否认性,于是集这些安全手段于一身的终极武器--数字证书出现了。数字证书具备了加密/解密的必要信息,包含签名算法,可用于网络数据加密/解密交互,标识网络用户(计算机)身份。数据证书为发布公钥提供了一种简便途径,成为加密算法以及公钥的载体。

   数字证书有多种文件编码格式,主要包含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的数字证书为例:

Java安全之数字证书_第1张图片Java安全之数字证书_第2张图片Java安全之数字证书_第3张图片

从上面的截图可以看到,一张数字证书中包含了很多信息,主要有,版本号、序列号、签名算法、签名哈希算法、颁发者、有效期、使用者、公钥、指纹算法、指纹以及一些数字证书的扩展属性信息。

二、证书如何获取
假如你的公司要上线一个购物网站,那么肯定会使用到https协议,也就肯定会使用到数字证书,那么数字证书从哪里来?是自己生成吗?当然不是,正确的做法是,公司生成一个证书请求文件,再把证书请求文件提交给证书认证机构,然后证书认证机构使用其根证书再根据证书请求文件中的信息为您生成受信任证书,也就是颁发证书,当然要让证书认证机构为您公司颁发证书这是需要money的。但是有些时候也可以自己创建证书,只不过这时候的证书颁者是你自己,只是别人信息你自己的根证书,证书也可以照常使用。

三、证书是否合法(受信任)
说到证书是否受信任的问题就得先知道一条规则:受信任的证书所颁发的证书也是受信任的。那么先有鸡还是先有蛋的问题来了,第一个受信任的证书是从哪里来的,为了解决这个问题就引出了根证书,根证书是自己颁发给自己的证书,只要信任了根证书,这样由根证书颁发的证书也就可以被信任,所以证书颁发其实是一种树形结构,根证书可以颁发一级证书,一级证书在允许的情况下也可以为其它用户颁发二级证书。以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);
	}

	@Before
	public void before() {
		//注册BC Provider,因为有些关于证书的操作使用到了BouncyCastle这个第三方库就顺便注册上了,其实不注册也行
		Provider provider = new BouncyCastleProvider();
		Security.addProvider(provider);
	}
	
	/**
	 * 生成根证书公钥与私钥对
	 */
	@Test
	public 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
	 */
	@Test
	public 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废弃,但可以使用)
	 */
	@Test
	public 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
	 */
	@Test
	public 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());
	}
	
	/**
	 * 生成用户证书
	 */
	@Test
	public 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());
	}
	
	
	/**
	 * 验证根证书签名
	 */
	@Test
	public 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);
	}
	
	/**
	 * 验证用户证书签名
	 */
	@Test
	public 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);
	}
	
	
	/**
	 * 生成证书请求文件
	 */
	@Test
	public 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());
	}
	
	/**
	 * 根据证书请求文件生成用户证书,其实主要是使用根证书私钥为其签名
	 */
	@Test
	public 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根证书不受信任。要启用信任,将该证书安装到”受信任的根证书颁发机构“存储区。将自己生成的根证书安装到受信任的根证书颁发机构后,你会发现其颁发的用户证书也受信任了。

你可能感兴趣的:(安全,证书,certificate,cert)