一.数字证书概念
1. 数字证书集合了多种密码学的算法:
- 自身带有公钥信息,可以完成相应的加密和解密操作;
- 同时还带有数字签名,可以鉴别消息的来源;
- 并且自身带有消息摘要信息,可以验证证书的完整新;
- 由于证书本身还有用户身份信息,因而具有认证性
数字证书中常用的非对称加密算法是RSA算法,签名算法是SHA1withRSA算法,消息摘要算法是SHA算法。
数字证书类似生活中的身份证,只是数字证书是签发给网络用户(计算机)的身份凭证。并且这些身份凭证需要有认证机构(Certificate Authority)CA签发,并且只有经过CA签发的证书在网络中才具有可认证性。
2. 数字证书格式标准:
数字证书有多重文件编码格式,主要包含CER,DER编码等。
所有证书符合公钥基础设置指定的国际标准,目前有三个版本如下:
以上标准用于证书的申请和更新等操作;如PKCS#10文件用来证书的签发申请,PKCS#12文件可以用作Java中的密匙库和信任库直接使用。通常用Base64编码作为数字证书文件的存储格式。
3.模型分析
证书签发
办法流程如下所示:
说明:
1)由数字证书需求方产生自己的密匙对
2)由数字证书需求方将算法、公钥和证书申请者身份信息传送认证机构
3)由认证机构核实用户身份,执行相应的步骤,确保请求确实是用户发送而来
4)由认证机构将数字证书颁发给用户
二、证书管理
要获得数字证书,我们需要使用数字证书管理工具(KeyTool或者OpenSSL)构建CSR(certificate signing request,数字证书申请),交由CA机构签发,形成最终的设资证书。
1.KeyTool证书管理
keyTool是Java中数字证书管理的工具, 用于数字证书的申请、导入、导出和撤销等操作。KeyTool和本地密匙库相关联,将私钥存于密匙库,公钥则以数字证书输出。这个工具位于%JDK_HOME%/bin目录中。需要通过控制台命令行操作。
1.1 构建自签名证书
1.1.1 第一步生成数字证书
在%JDK_HOME%/bin>中执行如下命令,生成本地数字证书:
keytool -genkeypair -keyalg RSA -keysize 2048 -sigalg SHA1withRSA -validity 36000 -alias www.vison.org -keystore vison.keystore
说明:
-genkeypair : 表示生成密匙
-keyalg: 指定密匙算法,这里指定为RSA算法
-keysize:指定密匙长度,默认是1024,这里指定为2048
-sigalg:指定数字签名算法,这里指定为SHA1withRSA算法
-validity:指定证书有效期,这里指定为36000天
-alias:指定别名,这里是www.vison.org
-keystore:指定密匙库存储位置,这里是vison.keystore
然后需要输入相关的用户信息,如下:
1.1.2 第二步 导出数字证书
在%JDK_HOME%/bin>中执行如下命令,导出数字证书:
keytool -exportcert -alias www.vison.org -keystore vison.keystore -file vison.cer -rfc
说明:
-exportcert :表示证书导出操作
-alias :指定导出别名,这里是www.vison.org -keystore :指定密匙库文件,这里是vison.keystore -file :指定导出文件路径,这类是vison.cer -rfc:指定以Base64编码格式输出
1.1.3 第三步 打印数字证书信息
在%JDK_HOME%/bin>中执行如下命令,打印数字证书:
keytool -printcert -file vison.cer
总结:上面就生成了一个自签名的X.509第三版类型的根证书,并且用Base64编码保存,自签名证书虽然可以使用,但是未经CA认证是没有任何的法律效力的。
1.2 构建CA签发证书
如果需要获取CA认证机构认证的数字证书,需要将数字证书签发申请(CAR)导出,然后由CA机构认证颁发,并且将认证后的证书导入本地密匙库和信任库。
1.2.1 第一步 导出数字证书签发申请
执行如下命令,会生成一个vison.csr文件:
keytool -certreq -alias www.vison.org -keystore vison.keystore -file vison.csr -v
这里需要指定密匙库口令和密匙口令。
说明:
-certreq :表示数字证书的申请操作
-alias:指定别名,这里是指www.vison.org
-keystore:指定密匙库,这里是vison.keystore
-file:指定到导出文件地址,这里是vison.csr
-v:详细信息
目前由VeriSign、GeoTrust和Thawte国际权威数字证书颁发认证机构“三巨头”认证数字证书价格不菲,当然我们使用使用免费的CAcert签发证书。当你获取了证书后,然后就可以通过如下方式导入证书了。
1.2.2 第二步 导入数字证书
执行如下命令:
keytool -importcert -trustcacerts -alias www.vison.org -file vison.cer -keystore vison.keystore
说明:
-importcert:表示导入数字证书
-trustcacerts:表示将数字证书导入信任库
-alias:指定导别名,这里为www.vison.org
-file:指定导入数字证书文件路径,这里是vison.cer
-keystore:指定密匙库文件,这里是vison.keystore
这里需要指定密匙库
1.2.3 第三步 查看导入数字证书
keytool -list -alias www.vison.org -keystore vison.keystore
说明:
-list:表示导入数字证书
-alias:指定别名,这里是www.vison.org
-keystore:指定密匙库文件,这里是vison.keystore
2.OpenSSL证书管理
OpenSSL是一个开放源代码的软件包,这个是实现了SSL及相关加密技术,也是常用的证书管理工具,OpenSSL功能远胜于KeyTool,可用于根证书,服务器证书和客户证书的管理,相关的操作可以参考官方文档。
三、证书使用
Java提供完善的数字证书管理实现,通过操作密匙库和数字证书就可以完成加密、解密和签名验证操作,密匙库管理私钥,数字证书管理公钥,私钥和密钥分属消息传递两方,进行加密消息传递,这里的keystore和证书都是用之前用keytool生成的证书来测试。
我们可以简单的将密匙库当做私钥的相关操作的入口,数字证书则是公钥相关操作的入口。
package keytools;
import org.apache.commons.codec.binary.Hex;
import javax.crypto.Cipher;
import java.io.FileInputStream;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
public class CertificateCodeTest {
//证书类型
private static final String CERT_TYPE ="X.509";
//测试
public static void main(String[] args) throws Exception {
String keyStorePath ="C:\\Program Files\\Java\\jdk1.8.0_144\\bin\\vison.keystore";
String alias ="www.vison.org";
String password ="123456";
String certificatePath="C:\\Program Files\\Java\\jdk1.8.0_144\\bin\\vison.cer";
String str ="签名 vison";
byte[] sign = sign(str.getBytes(), keyStorePath, alias, password);
System.out.println("sign: " + Hex.encodeHex(sign) );
//验证
boolean verify = verify(str.getBytes(), sign, certificatePath);
System.out.println("verify: " +verify);
}
/**
* 验证签名
* @param data
* @param sign
* @param certificatePath
* @return
* @throws Exception
*/
public static boolean verify (byte[] data,byte[] sign,String certificatePath) throws Exception{
X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath);
Signature signature = Signature.getInstance(x509Certificate.getSigAlgName());
signature.initVerify(x509Certificate);
signature.update(data);
return signature.verify(sign);
}
/**
* 签名
* @param data
* @param keyStorePath
* @param alias
* @param password
* @return
* @throws Exception
*/
public static byte[] sign(byte[] data,String keyStorePath,String alias, String password)throws Exception{
X509Certificate x509Certificate = (X509Certificate) getCertificate(keyStorePath, alias, password);
Signature signature = Signature.getInstance(x509Certificate.getSigAlgName());
PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);
signature.initSign(privateKey);
signature.update(data);
return signature.sign();
}
/**
* 公钥解密
* @param data
* @param certificatePath
* @return
* @throws Exception
*/
public static byte[] decryptByPublicKey(byte[] data,String certificatePath)throws Exception{
PublicKey publicKey = getPublicKeyByCertificate(certificatePath);
Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE,publicKey);
return cipher.doFinal(data);
}
/**
* 公钥加密
* @param data
* @param certificatePath
* @return
* @throws Exception
*/
public static byte[] encryptByPublicKey(byte[] data,String certificatePath)throws Exception{
PublicKey publicKey = getPublicKeyByCertificate(certificatePath);
Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE,publicKey);
return cipher.doFinal(data);
}
/**
* 私钥解密
* @param data
* @param keyStorePath
* @param alias
* @param password
* @return
* @throws Exception
*/
public static byte[] decryptByPrivateKey(byte[] data,String keyStorePath,String alias,String password)throws Exception{
PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);
Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE,privateKey);
return cipher.doFinal(data);
}
/**
* 私钥加密
* @param data
* @param keyStorePath
* @param alias
* @param password
* @return
* @throws Exception
*/
public static byte[] encryptByPrivateKey(byte[] data,String keyStorePath,String alias,String password)throws Exception{
PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);
Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE,privateKey);
return cipher.doFinal(data);
}
/**
* 获取私钥
* @param keyStorePath 密匙库路径
* @param alias 别名
* @param password 密码
* @return PrivateKey
* @throws Exception
*/
public static PrivateKey getPrivateKey(String keyStorePath,String alias,String password) throws Exception{
KeyStore keyStore = getKeyStore(keyStorePath, password);
return (PrivateKey)keyStore.getKey(alias,password.toCharArray());
}
/**
* 获取公钥
* @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
*/
public static Certificate getCertificate(String certificatePath) throws Exception{
CertificateFactory certificateFactory = CertificateFactory.getInstance(CERT_TYPE);
FileInputStream is = new FileInputStream(certificatePath);
Certificate certificate = certificateFactory.generateCertificate(is);
is.close();
return certificate;
}
/**
* 获取Certificate
* @param keyStorePath 密匙库路径
* @param alias 别名
* @param password 密码
* @return Certificate 证书
* @throws Exception
*/
public static Certificate getCertificate(String keyStorePath,String alias,String password) throws Exception{
KeyStore keyStore = getKeyStore(keyStorePath, password);
return keyStore.getCertificate(alias);
}
/**
* 获取keySotre
* @param keyStorePath 密匙库的路径
* @param password 密匙库的密码
* @return KeyStore
* @throws Exception
*/
public static KeyStore getKeyStore(String keyStorePath,String password) throws Exception {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream is = new FileInputStream(keyStorePath);
keyStore.load(is,password.toCharArray());
is.close();
return keyStore;
}
}