1.数字证书简介
数字证书具备常规加密解密必要的信息,包含签名算法,可用于网络数据加密解密交互,标识网络用户(计算机)身份。数字证书为发布公钥提供了一种简便的途径,其数字证书则成为加密算法以及公钥的载体。依靠数字证书,我们可以构建一个简单的加密网络应用平台。
数字证书类似于个人身份证,由数字证书颁发认证机构(Certificate Authority, CA)签发。只有经过CA签发的证书在网络中才具备可认证性。CA颁发给自己的证书叫根证书。
VeriSign, GeoTrust和Thawte是国际权威数字证书颁发认证机构的三巨头。其中应用最广泛的是VeriSign签发的电子商务用数字证书。
最为常用的非对称加密算法是RSA,与之配套的签名算法是SHA1withRSA,最常用的消息摘要算法是SHA1.
除了RSA,还可以使用DSA算法。只是使用DSA算法无法完成加密解密实现,即这样的证书不包括加密解密功能。
数字证书有多种文件编码格式,主要包含CER编码,DER编码等。
CER(Canonical Encoding Rules, 规范编码格式),DER(Distinguished Encoding Rules 卓越编码格式),两者的区别是前者是变长模式,后者是定长模式。
所有证书都符合公钥基础设施(PKI, Public Key Infrastructure)制定的ITU-T X509国际标准(X.509标准)。
2.模型分析
在实际应用中,很多数字证书都属于自签名证书,即证书申请者为自己的证书签名。这类证书通常应用于软件厂商内部发放的产品中,或约定使用该证书的数据交互双方。数字证书完全充当加密算法的载体,为必要数据做加密解密和签名验签等操作。在我司的开发过程中,数字证书更多是用来做加密和解密。
1)证书签发
2)加密交互,图略。
当客户端获取到服务器下发的数字证书后,就可以进行加密交互了。具体做法是:
客户端使用公钥,加密后发送给服务端,服务端用私钥进行解密验证。
服务端使用私钥进行加密和数字签名。
3. KeyTool 管理证书
KeyTool与本地密钥库相关联,将私钥存于密钥库,公钥则以数字证书输出。KeyTool位于JDK目录下的bin目录中,需要通过命令行进行相应的操作。
1)构建自签名证书
申请数字证书之前,需要在密钥库中以别名的方式生成本地数字证书,建立相应的加密算法,密钥,有效期等信息。
keytool -genkeypair -keyalg RSA -keysize 2048 -sigalg SHA1withRSA -validity 3600 -alias myCertificate -keystore myKeystore.keystore
各参数含义如下:
-genkeypair 表示生成密钥对
-keyalg 指定密钥算法,这里是RSA
-keysize 指定密钥长度,默认1024,这里指定2048
-sigal 指定签名算法,这里是SHA1withRSA
-validity 指定有效期,单位为天
-alias 指定别名
-keystore 指定密钥库存储位置
这里我输入参数Changeme123作为密钥库的密码,也可通过参数-storepass指定密码。可以用-dname "CN=xxx...."这样的形式,避免更多交互。
注意:一个keystore应该是可以存储多套<私钥-数字证书>的信息,通过别名来区分。通过实践,调用上述命令两次(别名不同),生成同一个keystore,用不同别名进行加密解密和签名验签,没有任何问题。
更多命令可参考:http://blog.chinaunix.net/uid-17102734-id-2830223.html
经过上述操作后,密钥库中已经创建了数字证书。虽然这时的数字证书并没有经过CA认证,但并不影响我们使用。我们仍可将证书导出,发送给合作伙伴进行加密交互。
keytool -exportcert -alias myCertificate -keystore myKeystore.keystore -file myCer.cer -rfc
各参数含义如下:
-exportcert 表示证书导出操作
-alias 指定别名
-keystore 指定密钥库文件
-file 指定导出证书的文件路径
-rfc 指定以Base64编码格式输出
打印证书
keytool -printcert -file myCer.cer
2)构建CA签发证书
如果要获取CA机构谁的数字证书,需要将数字证书签发申请(CSR)导出,经由CA机构认证并颁发,将认证后的证书导入本地密钥库和信息库。
keytool -certreq -alias myCertificate -keystore myKeystore.keystore -file myCsr.csr -v
各参数含义如下:
-certreq 表示数字证书申请操作
-alias 指定别名
-keystore 指定密钥库文件路径
-file 指定导出申请的路径
-v 详细信息
获得签发的数字证书后,需要将其导入信任库。
keytool -importcert -trustcacerts -alias myCertificate -file myCer.cer -keystore myKeystore.keystore
参数不作详细讲解,如果是原来的证书文件,那么会报错:
查看证书
keytool -list -alias myCertificate -keystore myKeystore.keystore
经过上述的所有操作后,可以得到下面几个文件
4. 证书使用
终于到了激动人心的时刻,可以用代码通过keystore进行加解密操作了!
Java 6提供了完善的数字证书管理实现,我们几乎无需关注,仅通过操作密钥库和数字证书就可完成相应的加密解密和签名验签过程。
密钥库管理私钥,数字证书管理公钥,公钥和私钥分属消息传递双方,进行加密消息传递。
考虑一个场景。
A机器某模块需要将数据导出到一个文件中,将文件发送到B机器,由B将数据导入。
在这个场景中,A就相当于服务端,需要将证书给B,同时用私钥加密数据,生成签名,导出到文件中。
B相当于客户端,用收到的数字证书进行解密和验签。
package jdbc.pro.lin; import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException; public class MyCertifacate {
private static final String STORE_PASS = "Changeme123";
private static final String ALIAS = "myCertificate";
private static final String KEYSTORE_PATH = "D:\\JavaDemo\\Certifacate\\myKeystore.keystore";
private static final String CERT_PATH = "D:\\JavaDemo\\Certifacate\\myCer.cer";
private static final String PLAIN_TEXT = "MANUTD is the most greatest club in the world.";
/** JDK6只支持X.509标准的证书 */
private static final String CERT_TYPE = "X.509"; public static void main(String[] args) throws IOException {
/**
* 假设现在有这样一个场景 。A机器上的数据,需要加密导出,然后将导出文件放到B机器上导入。 在这个场景中,A相当于服务器,B相当于客户端
*/ /** A */
KeyStore keyStore = getKeyStore(STORE_PASS, KEYSTORE_PATH);
PrivateKey privateKey = getPrivateKey(keyStore, ALIAS, STORE_PASS);
X509Certificate certificate = getCertificateByKeystore(keyStore, ALIAS); /** 加密和签名 */
byte[] encodedText = encode(PLAIN_TEXT.getBytes(), privateKey);
byte[] signature = sign(certificate, privateKey, PLAIN_TEXT.getBytes()); /** 现在B收到了A的密文和签名,以及A的可信任证书 */
X509Certificate receivedCertificate = getCertificateByCertPath(
CERT_PATH, CERT_TYPE);
PublicKey publicKey = getPublicKey(receivedCertificate);
byte[] decodedText = decode(encodedText, publicKey);
System.out.println("Decoded Text : " + new String(decodedText));
System.out.println("Signature is : "
+ verify(receivedCertificate, decodedText, signature));
} /**
* 加载密钥库,与Properties文件的加载类似,都是使用load方法
*
* @throws IOException
*/
public static KeyStore getKeyStore(String storepass, String keystorePath)
throws IOException {
InputStream inputStream = null;
try {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
inputStream = new FileInputStream(keystorePath);
keyStore.load(inputStream, storepass.toCharArray());
return keyStore;
} catch (KeyStoreException | NoSuchAlgorithmException
| CertificateException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (null != inputStream) {
inputStream.close();
}
}
return null;
} /**
* 获取私钥
*
* @param keyStore
* @param alias
* @param password
* @return
*/
public static PrivateKey getPrivateKey(KeyStore keyStore, String alias,
String password) {
try {
return (PrivateKey) keyStore.getKey(alias, password.toCharArray());
} catch (UnrecoverableKeyException | KeyStoreException
| NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
} /**
* 获取公钥
*
* @param certificate
* @return
*/
public static PublicKey getPublicKey(Certificate certificate) {
return certificate.getPublicKey();
} /**
* 通过密钥库获取数字证书,不需要密码,因为获取到Keystore实例
*
* @param keyStore
* @param alias
* @return
*/
public static X509Certificate getCertificateByKeystore(KeyStore keyStore,
String alias) {
try {
return (X509Certificate) keyStore.getCertificate(alias);
} catch (KeyStoreException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
} /**
* 通过证书路径生成证书,与加载密钥库差不多,都要用到流。
*
* @param path
* @param certType
* @return
* @throws IOException
*/
public static X509Certificate getCertificateByCertPath(String path,
String certType) throws IOException {
InputStream inputStream = null;
try {
// 实例化证书工厂
CertificateFactory factory = CertificateFactory
.getInstance(certType);
// 取得证书文件流
inputStream = new FileInputStream(path);
// 生成证书
Certificate certificate = factory.generateCertificate(inputStream); return (X509Certificate) certificate;
} catch (CertificateException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (null != inputStream) {
inputStream.close();
}
}
return null; } /**
* 从证书中获取加密算法,进行签名
*
* @param certificate
* @param privateKey
* @param plainText
* @return
*/
public static byte[] sign(X509Certificate certificate,
PrivateKey privateKey, byte[] plainText) {
/** 如果要从密钥库获取签名算法的名称,只能将其强制转换成X509标准,JDK 6只支持X.509类型的证书 */
try {
Signature signature = Signature.getInstance(certificate
.getSigAlgName());
signature.initSign(privateKey);
signature.update(plainText);
return signature.sign();
} catch (NoSuchAlgorithmException | InvalidKeyException
| SignatureException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} return null;
} /**
* 验签,公钥包含在证书里面
*
* @param certificate
* @param decodedText
* @param receivedignature
* @return
*/
public static boolean verify(X509Certificate certificate,
byte[] decodedText, final byte[] receivedignature) {
try {
Signature signature = Signature.getInstance(certificate
.getSigAlgName());
/** 注意这里用到的是证书,实际上用到的也是证书里面的公钥 */
signature.initVerify(certificate);
signature.update(decodedText);
return signature.verify(receivedignature);
} catch (NoSuchAlgorithmException | InvalidKeyException
| SignatureException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
} /**
* 加密。注意密钥是可以获取到它适用的算法的。
*
* @param plainText
* @param privateKey
* @return
*/
public static byte[] encode(byte[] plainText, PrivateKey privateKey) {
try {
Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return cipher.doFinal(plainText);
} catch (NoSuchAlgorithmException | NoSuchPaddingException
| InvalidKeyException | IllegalBlockSizeException
| BadPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} return null; } /**
* 解密,注意密钥是可以获取它适用的算法的。
*
* @param encodedText
* @param publicKey
* @return
*/
public static byte[] decode(byte[] encodedText, PublicKey publicKey) {
try {
Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return cipher.doFinal(encodedText);
} catch (NoSuchAlgorithmException | NoSuchPaddingException
| InvalidKeyException | IllegalBlockSizeException
| BadPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} return null;
}
}
数字证书简介及Java编码实现的更多相关文章
-
数字签名、数字证书的原理以及证书的获得java版
数字签名原理简介(附数字证书) 首先要了解什么叫对称加密和非对称加密,消息摘要这些知识. 1. 非对称加密 在通信双方,如果使用非对称加密,一般遵从这样的原则:公钥加密,私钥解密.同时,一般一个密钥加 ...
-
Java加密解密与数字证书的操作
1 keytool命令总结 一.创建数字证书 交互模式 使用默认的密钥库.keystore(文件夹是c: Documents and Settingusername)和算法(DSA) keytool ...
-
android利用数字证书对程序签名
签名的必要性 1. 防止你已安装的应用被恶意的第三方覆盖或替换掉. 2. 开发者的身份标识,签名可以防止抵赖等事件的发生. 开发Android的人这么多,完全有可能大家都把类名,包名起成了一个同 ...
-
android 利用数字证书对程序签名
签名的必要性 1. 防止你已安装的应用被恶意的第三方覆盖或替换掉. 2. 开发者的身份标识,签名可以防止抵赖等事件的发生. 开发Android的人这么多,完全有可能大家都把类名,包名起成了一个同样 ...
-
【转】Android数字证书
Android数字证书的作用是非常重要的.Android操作系统每一个应用程序的安装都需要经过这一数字证书的签名. Android手机操作系统作为一款比较流行的开源系统在手机领域占据着举足轻重的地位. ...
-
Java使用数字证书加密通信(加解密/加签验签)
本文中使用的Base64Utils.java可参考:http://www.cnblogs.com/shindo/p/6346618.html 证书制作方法可参考:http://www.cnblogs. ...
-
如何利用java程序实现加密所需的公钥、密钥、数字证书
本篇的主要目的在于实现pdf的数字签名问题,只是作为我学习知识的总结. 1.数字签名算法的概述 本部分主要参考于:https://blog.csdn.net/lovelichao12/article/ ...
-
JAVA对数字证书的常用操作(转载)
一:需要包含的包 import java.security. * ; import java.io. * ; import java.util. * ; import java.security. * ...
-
Java加密技术(八)——数字证书
原文:http://snowolf.iteye.com/blog/391931 请大家在阅读本篇内容时先阅读 Java加密技术(四),预先了解RSA加密算法. 在构建Java代码实现前,我们需要完成证 ...
随机推荐
-
java对xml节点属性的增删改查
学习本文之前请先看我的另一篇文章JAVA对XML节点的操作可以对XML操作有更好的了解. package vastsum; import java.io.File; import java.io.Fi ...
-
Python AES - base64 加解密
首先python引用AES加密 from Crypto.Cipher import AES 需要先安装 Crypto 模块, 可以使用 easy_install 进行安装 会自动去官网进行搜索 ...
-
APP Widget的开发
Android中经常用到APP Widget,如时钟,天气预报等. 长按屏幕,在弹出的对话框中选择“窗口小部件”,然后就列出了可选择的小部件,这些小部件就是APP Widget. 本文开发一个APP ...
-
Greedy:Cow Acrobats(POJ 3045)
牛杂技团 题目大意:一群牛想逃跑,他们想通过搭牛梯来通过,现在定义risk(注意可是负的)为当前牛上面的牛的总重量-当前牛的strength,问应该怎么排列才能使risk最小? 说实话这道题我一开始给 ...
-
第三章 深入 ZAB 协议
上一节介绍了ZAB协议的内容,本节将从系统模型.问题描述.算法描述和运行分析四方面来深入了解 ZAB 协议. 系统模型 在一个由一组进程 n ={P1,P2,...Pn}组成的分布式系统中,每一个进程 ...
-
fekit前端代码模块化工具
fekit是一套前端开发工具,是由去哪儿网开发.目前在github上开源.使用fekit的优点: a.本地开发支持环境:从开发调试到上线,均是前后端工程独立开发.调试.部署,打破了原来前后端揉在一个工 ...
-
C# 多线程学习总结
C# 多线程学习总结 C#多线程学习(一) 多线程的相关概念 什么是进程? 当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源.而一个进程又是由多个线程所组成的. ...
-
VMware 中安装虚拟机和宿主机通信
网络上对于三种连接模式说的很多了,这里就不在具体的说明了.此处采用的NAT模式连接虚拟机,让虚拟机和宿主机互相通讯,并且让虚拟机能访问互联网. 1.首先设置虚拟机的网络,如下图.通过如下操作进入虚拟机 ...
-
layer弹框插件使用
需要在jquery之后导入 <link rel="stylesheet" href="${pageContext.request.contextPath }/js/ ...
-
【洛谷p1164】小A点菜
(……) 小A点菜[传送门] 上标签: (一个神奇的求背包问题方案总数的题) 核心算法: ;i<=n;i++) for(int j=m;j>=a[i];j--) f[j]+=f[j-a[i ...