数字证书 - Java加密与安全

数字证书

我们在前面看到了一些计算机密码学的一些算法

1. 摘要算法确保数据没有被篡改

2. 非对称加密就是对数据进行加解密

3. 数据签名可以确保数据完整性和抗否认性


而数字证书就是集合了多种密码学算法,用于实现数据加解密,身份认证,签名等多种功能的一种网络安全标准


我们看数字证书:

1. 首先实现了非对称加密算法,可以对数据进行加解密

2. 然后他有签名算法,能够确保数据完整性和抗否认性

3. 最后通过摘要算法,确保证书本身没有被篡改

package com.learn.securl;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.X509Certificate;
import java.util.Base64;

import javax.crypto.Cipher;

public class X509 {
	/**
	 * 在这个类中我们定义一个privateKey
	 * privateKey代表的是私钥
	 */
	private final PrivateKey privateKey;
	/**
	 * 和一个X509Certificate的一个变量
	 * X509Certificate表示的是证书,公钥,以及摘要的
	 */
	private final X509Certificate certificate;
	/**
	 * 我们需要传入keyStore和证书的名称和password
	 * 我们通过KeyStore的getKey方法传入名称和password
	 * 就可以得到一个privateKey
	 * 我们通过KeyStore的getCertificate方法传入证书的名称
	 * 就可以得到一个X509Certificate的一个对象
	 * @param ks
	 * @param certName
	 * @param password
	 * @throws GeneralSecurityException
	 */
	public X509(KeyStore ks, String certName, String password) throws GeneralSecurityException{
		/**
		 * 注意密码用于获得私钥
		 */
		this.privateKey = (PrivateKey) ks.getKey(certName, password.toCharArray());
		/**
		 * 获得证书是不需要口令的
		 */
		this.certificate = (X509Certificate) ks.getCertificate(certName);
	}
	
	/**
	 * 然后我们定义一个encrypt仿方法进行加密
	 * @param message
	 * @return
	 * @throws GeneralSecurityException
	 */
	public byte[] encrypt(byte[] message) throws GeneralSecurityException{
		/**
		 * 在加密的时候我们首先通过Cipher.getInstance
		 * 然后我们通过privateKey.getAlgorithm获得加密算法
		 */
		Cipher cipher = Cipher.getInstance(this.privateKey.getAlgorithm());
		/**
		 * 然后我们设置ENCRYPT_MODE并且传入privateKey
		 */
		cipher.init(Cipher.ENCRYPT_MODE, this.privateKey);
		/**
		 * 然后获取密文
		 */
		return cipher.doFinal(message);
	}
	
	/**
	 * 然后我们定义一个decrypt
	 * @param data
	 * @return
	 * @throws GeneralSecurityException
	 */
	public byte[] decrypt(byte[] data) throws GeneralSecurityException{
		/**
		 * 在解密的时候通过certificate对象getPublicKey就可以获得公钥publicKey
		 */
		PublicKey publicKey = this.certificate.getPublicKey();
		/**
		 * 然后我们还是通过Cipher.getInstance获得一个Cipher对象
		 */
		Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
		/**
		 * 设置为解密模式
		 */
		cipher.init(Cipher.DECRYPT_MODE, publicKey);
		/**
		 * 然后传入publicKey
		 * 就可以正常的解密
		 */
		return cipher.doFinal(data);
	}
	
	/**
	 * 我们再定义一个sign方法用于签名
	 * 传入原始的Message
	 * @param message
	 * @return
	 * @throws GeneralSecurityException
	 */
	public byte[] sign(byte[] message) throws GeneralSecurityException{
		/**
		 * 然后通过Signature.getInstance
		 * 通过certificate对象getSigAlgName获得签名算法
		 */
		Signature signature = Signature.getInstance(this.certificate.getSigAlgName());
		/**
		 * 我们用privateKey进行签名
		 */
		signature.initSign(this.privateKey);
		signature.update(message);
		/**
		 * 获得签名以后的数据
		 */
		return signature.sign();
	}
	
	/**
	 * 我们再定义一个verify方法验证签名
	 * 需要传入原始信息和签名信息
	 * 验证签名的时候需要使用公钥
	 * 同时获得的还有签名的算法
	 * @param message
	 * @param sign
	 * @return
	 * @throws GeneralSecurityException
	 */
	public boolean verify(byte[] message, byte[] sign) throws GeneralSecurityException{
		Signature signature = Signature.getInstance(this.certificate.getSigAlgName());
		signature.initVerify(this.certificate);
		signature.update(message);
		return signature.verify(sign);
	}
	
	/**
	 * 我们定义一个新的方法loadKeyStore
	 * 用于加载一个KeyStore文件
	 * 因为JAVA的数字证书是存储在KeyStore里面的
	 * 我们需要指定一个KeyStore的文件
	 * 打开KeyStore用到的密码
	 * @param keyStoreFile
	 * @param password
	 * @return
	 * @throws GeneralSecurityException
	 * @throws IOException
	 */
	static KeyStore loadKeyStore(String keyStoreFile, String password) throws GeneralSecurityException, IOException{
		try(InputStream input = new BufferedInputStream(new FileInputStream(keyStoreFile))){
			/**
			 * 我们通过KeyStore.getInstance得到一个KeyStore对象
			 */
			KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
			/**
			 * 通过KeyStore的load方法
			 * 传入一个inputStream就可以得到KeyStore对象
			 */
			ks.load(input, password.toCharArray());
			return ks;
		}
	}
	
	/**
	 * 最后我们使用main方法使用数字证书以及加密和签名,
	 * 在执行这个程序之前,
	 * 首先我们要创建我们的数字证书
	 * keytool -genkeypair -keyalg RSA -keysize 1024 
	 * -sigalg SHA1withRSA -validity 36500 -alias mycert 
	 * -keystore my.keystore -dname “CN=test07, OU=sample, 
	 * O=sample, L=BJ, ST=BJ, C=CN” -keypass 123456 
	 * -storepass 456789
	 * genkeypair表示我们要生成一个数字证书
	 * -keyalg RSA他的算法是RSA
	 * -keysize 1024他的密钥长度是1024
	 * -sigalg SHA1withRSA我们指定的算法长度是SHA1withRSA
	 * -validity 36500我们指定数字证书的有效期是36500
	 * -alias mycert我们给这个证书写一个名字叫做mycert
	 * -keystore my.keystore同时我们指定了一个keystore的文件叫做my.keystore
	 * “CN=test07, OU=sample, O=sample, L=BJ, ST=BJ, C=CN”
	 * -keypass 123456然后我们用keypass指定证书的密码123456
	 * -storepass 456789我们用storepass指定store的密码是456789
	 * 紧接着我们要给证书指定CN OU这些属性
	 * 如果这个证书将来要用于网站
	 * CN的属性必须和网站的域名完全一致
	 * keytool -list -keystore my.keystore -storepass 456789
	 * 我们还可以通过keytool的命令查询证书
	 * 我们输入命令然后回车
	 * 我们就可以看到包含了一个证书mycert
	 * 我们就可以看到在项目的根目录中生成一个my.cert的一个文件
	 * 
	 * 我们在执行的时候可以看到
	 * 我们在执行证书得到加密以后的密文
	 * 解密以后得到原始的明文
	 * 我们对数字证书对原始的消息进行签名
	 * 得到Base64表示的签名
	 * 最后我们用证书得到这个签名结果为true
	 * @param args
	 * @throws GeneralSecurityException
	 * @throws IOException
	 */
	public static void main(String[] args) throws GeneralSecurityException, IOException {
		byte[] message = "Hello, 使用X.509证书进行加密和签名!".getBytes();
		// 读取KeyStore:
		/**
		 * 首先我们需要读取一个KeyStore
		 * 然后传入KeyStore的密码
		 * 
		 * 注意到我们用loadKeyStore的时候传入了keyStore文件的密码
		 */
		KeyStore ks = loadKeyStore("my.keystore", "456789");
		// 读取证书:
		/**
		 * 紧接着我们需要读取一个X509证书
		 * 并且传入证书的密码
		 * 并且获取他的私钥
		 * 
		 * 我们在读取证书的时候传入的是证书的密码
		 */
		X509 x509 = new X509(ks, "mycert", "123456");
		// 加密:
		/**
		 * 然后我们进行加密
		 */
		byte[] encrypted = x509.encrypt(message);
		System.out.println("encrypted: " + Base64.getEncoder().encodeToString(encrypted));
		// 解密:
		byte[] decrypted = x509.decrypt(encrypted);
		System.out.println("decrypted: " + new String(decrypted, "UTF-8"));
		// 签名:
		byte[] sign = x509.sign(message);
		System.out.println("sign: " + Base64.getEncoder().encodeToString(sign));
		// 验证签名:
		boolean verified = x509.verify(message, sign);
		System.out.println("verify: " + verified);
	}
}

数字证书采用链式签名认证,他的底层证书是Geo Trust 证书,然后由他签发的GeoTrust SSL CA - G3证书,最后由mail.qq.com
我们最后来看一下数字证书的应用:


1.我们在上网的时候用到的https,这个协议实际上是http over ssl

2. 在使用https的时候,服务器会发送证书给客户端,这个证书包括公钥,签名,和CA证书

3. 客户端可以验证服务器端的证书是否有效,来确认服务器的身份

4. 然后客户端使用证书加密一个随机口令,发送给服务器端,采用的是公钥加密

5. 而服务器端解密获得一个随机口令,采用的是私钥解密

6. 双方随后就可以使用AES加密进行通信,这个时候使用的是对称加密

服务器发送的数字证书实际上是publicKey,就是公钥,以及public的信息,比如摘要签名等,服务器的私钥永远

保存在服务器端,不能泄露,所以数字证书实际上包含的是公钥



最后我们总结一下:

1. 数字证书实际上就是集合了多种密码学算法,用于实现数据加解密,身份认证,签名等多种功能的一种网络安全标准

2. 数字认证采用的是一种链式签名的管理,顶级的CA证书已经内置在操作系统中

3. 常用的算法有MD5,SHA1,SHA256,RSA,DSA

猜你喜欢

转载自blog.csdn.net/Leon_Jinhai_Sun/article/details/89945347