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的数字证书为例:

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

二、证书如何获取

假如你的公司要上线一个购物网站,那么肯定会使用到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根证书不受信任。要启用信任,将该证书安装到”受信任的根证书颁发机构“存储区。将自己生成的根证书安装到受信任的根证书颁发机构后,你会发现其颁发的用户证书也受信任了。

时间: 2025-01-06 03:33:59

Java安全之数字证书的相关文章

如何用JAVA代码签发数字证书

用JAVA签发数字证书 打开cmd 1.输入D: 2.输入cd keys 3.输入命令查看创建的密钥库的证书列表 keytool -list -v -keystore mykey.keystore -storepass 123456 4. 之前导出过server.cer文件,把这个文件安装到操作系统,过程默认. 5.创建一个密钥对,和之前一样,输入如下命令: --创建密钥对 keytool -genkey -dname "CN=tmp, OU=NC, O=Shanghai University,

java加密与数字证书

加密与数字证书 加密与数字证书 概念 数字摘要 密钥加密技术 私用密钥(对称加密) 公共密钥(非对称加密) 数字签名 数字证书 X.509标准 工具 keytool 示例代码 加密解密 密钥库准备 代码 签名验签 代码 概念 数字摘要 数字摘要就是采用单项Hash函数将需要加密的明文"摘要"成一串固定长度(128位)的密文,这一串密文又称为数字指纹,HASH值或摘要值 ,它有固定的长度,而且不同的明文摘要成密文,其结果总是不同的,而同样的明文其摘要必定一致. 目前常用的摘要算法为MD5

Java数字签名和数字证书

数字签名 作用:检验jar包是否为公钥发布者的发布的,并且没有被篡改. 过程: 1,作者使用散列算法对jar包生成散列(指纹); 2,作者使用私钥对散列进行加密, 3,将jar包和签名后的散列一起发布. 4,用户使用作者发布的公钥解密散列,得到jar包的指纹; 5,用户使用散列算法对jar生成指纹; 6,对比两个指纹是否一致. 数字证书:权威仍证机构使用自己的私钥对申请认证的用户的公钥进行签名的产物. 作用:检验用户得到的公钥的发布者是否可信 过程: 1,申请者携带材料到认证机构申请数字证书(即

【Java】Java与数字证书

Java与数字证书 Java与数字证书 证书的签发和应用 证书的内容和意义 其它 证书(Certificate,也称public-key certificate)是用某种签名算法对某些内容(比如公钥)进行数字签名后得到的.可以用来当成信任关系中介的数字凭证.证书发行机构通过发行证书告知证书使用者或实体其公钥(public-key)以及其它一些辅助信息.证书在电子商务安全交易中有着广泛的应用,证书发行机构也称CA(Certificate Authority). 证书的签发和应用 证书在公钥加密应用

Java加密解密与数字证书的操作

1 keytool命令总结 一.创建数字证书 交互模式 使用默认的密钥库.keystore(文件夹是c: Documents and Settingusername)和算法(DSA) keytool -genkey 默认的别名mykey 密钥库中能够存放多个条目(公钥/私钥对和证书),它们在密钥库中以别名(alias)区分. [plain] view plaincopy keytool -genkey -alias mytest -keyalg RSA -keysize 1024 -keysto

Java 生成数字证书系列(二)剖析数字证书

序 上一篇介绍了一下 CA 证书的几个相关概念,这几个概念还是很重要的,目的在于了解数字证书,以及其的工作原理.这篇文章主要是对 CA 证书进行剖析,讲一下证书的基本构成,这对于生成正确的.可以访问的证书是必不可少的. 构成 废话不多说,直接上内容. 先看一下证书到底是什么,在 Windows 下查看证书时(这里以 cer 为例),界面是这样的. 常规 选项卡里,主要介绍了证书信息,颁发者,和有效日期等. 而 详细信息 选项卡中,包含的信息是比较多的,他们都是以一种 Key - Value 的形

Java 生成数字证书系列(四)生成数字证书(续)

序 上一篇文章讲到了 Java 生成数字证书,使用的是第三方的组件 BC .这篇文章也是介绍生成数字证书的,只不过与上一篇不同的是,这篇采用的是 KeyStore 的存储方式,导出的证书文件格式为 pfx ,这种格式的证书不仅包含有公钥,还包含有私钥.从证书中就可以读取到私钥. 正文 废话不多说,直接上内容. 与上一篇相同,这里也是使用的 Bouncy Castle 提供的组件,不同的是,这里的证书采用的是公钥加密技术12号标准生成的,简写 PKCS12 .具体内容这里就不再详细的介绍了,有需要

Java 密钥库和数字证书

密钥库中可以存放多个条目(公钥/私钥对和证书),它们在密钥库中以别名(alias)区分. 1.cd C:\Program Files (x86)\Java\jdk1.6.0_10\bin 2.生成签名证书:keytool -genkey -v -keystore android.keystore -alias android -keyalg RSA -validity 20000 [-genkey:生成密钥] [-keystore:指定存储位置可加上路径C:\Users\dxcb\android

JAVA中数字证书的维护及生成方

Java中的keytool.exe可以用来创建数字证书,所有的数字证书是以一条一条(采用别名区别)的形式存入证书库的中,证书库中的一条证书包含该条证书的私钥,公钥和对应的数字证书的信息.证书库中的一条证书可以导出数字证书文件,数字证书文件只包括主体信息和对应的公钥.  每一个证书库是一个文件组成,它有访问密码,在首次创建时,它会自动生成证书库,并要求指定访问证书库的密码.  在创建证书的的时候,需要填写证书的一些信息和证书对应的私钥密码.这些信息包括 CN=xx,OU=xx,O=xx,L=xx,