引言
数字证书是一个经证书授权中心数字签名的包含公开密钥拥有者信息以及公开密钥的文件。为现实网络安全化标准,如今大部分的 B2B、B2C、P2P、O2O 等商业网站,含有重要企业资料个人资料的信息资信网站,政府机构金融机构等服务网站大部分都使用了数字证书来加强网络的安全性。
数字证书一般由经过国家认证的权威机构颁发,即CA(例如中国各地方的CA公司)中心签发的证书,也可以由企业级CA系统进行签发,例如:Symantec、ResellerClub、数安时代等。开发人员也可以通过工具自动生成证书进行开发,但不经过认证的证书将被视为无效证书,不保安全保护,但仍可正常运行。
在这篇文章里将为大家介绍数字证书的生成使用过程,以及对数据进行加密、解密、签名、验签的使用方式。
希望能对各位的学习研究有所帮助,当中有所错漏的地方敬请点评。
目录
一、数字证书介绍
三、生成数字证书的方式
六、数字证书签名与验签
一、数字证书介绍
1.1 什么是数字证书
数字证书就是互联网通讯中标志通讯各方身份信息的一串数字,提供了一种在Internet上验证通信实体身份的方式,数字证书不是数字身份证,而是身份认证机构盖在数字身份证上的一个章或印(或者说加在数字身份证上的一个签名)。它是由权威机构——CA机构,又称为证书授权(Certificate Authority)中心发行的,人们可以在网上用它来识别对方的身份。
最简单的证书包含密钥、名称以及证书授权中心的数字签名。数字证书还有一个重要的特征就是只在特定的时间段内有效。数字证书是一种权威性的电子文档,可以由权威公正的第三方机构,即CA(例如中国各地方的CA公司)中心签发的证书,也可以由企业级CA系统进行签发。
图 V.1.1
1.2 数字证书的分类
从数字签名使用对象的角度分,目前的数字证书类型主要包括:个人身份证书、企业或机构身份证书、支付网关证书、服务器证书、安全电子邮件证书、个人代码签名证书,这些数字证书特点各有不同。
符合 X.509 标准的数字安全证书,证书中包含个人身份信息和个人的公钥,用于标识证书持有人的个人身份。数字安全证书和对应的私钥存储于 E-key 中,用于个人在网上进行合同签定、定单、录入审核、操作权限、支付信息等活动中标明身份。
符合 X.509 标准的数字安全证书,证书中包含企业信息和企业的公钥,用于标识证书持有企业的身份。数字安全证书和对应的私钥存储于 E-key 或 IC 卡中,可以用于企业在电子商务方面的对外活动,如合同签定、网上证券交易、交易支付信息等方面。
支付网关证书是证书签发中心针对支付网关签发的数字证书,是支付网关实现数据加解密的主要工具,用于数字签名和信息加密。支付网关证书仅用于支付网关提供的服务(Internet 上各种安全协议与银行现有网络数据格式的转换)。支付网关证书只能在有效状态下使用。支付网关证书不可被申请者转让。
符合 X.509 标准的数字安全证书,证书中包含服务器信息和服务器的公钥,在网络通讯中用于标识和验证服务器的身份。数字安全证书和对应的私钥存储于 E-key 中。服务器软件利用证书机制保证与其他服务器或客户端通信时双方身份的真实性、安全性、可信任度等。
代码签名证书是 CA 中心签发给软件提供商的数字证书,包含软件提供商的身份信息、公钥及 CA 的签名。软件提供商使用代码签名证书对软件进行签名后放到 Internet 上,当用户在 Internet 上下载该软件时,将会得到提示,从而可以确信:软件的来源;软件自签名后到下载前,没有遭到修改或破坏。代码签名证书可以对 32-bit .exe 、 .cab 、 .ocx 、 .class 等程序和文件 进行签名。
符合 X.509 标准的数字安全证书,通过 IE 或 Netscape 申请,用 IE 申请的证书存储于 WINDOWS 的注册表中,用 NETSCAPE 申请的存储于个人用户目录下的文件中。用于安全电子邮件或向需要客户验证的 WEB 服务器(https 服务) 表明身份。
个人代码签名证书是 CA 中心签发给软件提供人的数字证书,包含软件提供个人的身份信息、公钥及 CA 的签名。软件提供人使用代码签名证书对软件进行签名后放到 Internet 上,当用户在 Internet 上下载该软件时,将会得到提示,从而可以确信:软件的来源;软件自签名后到下载前,没有遭到修改或破坏。代码签名证书可以对 32-bit .exe 、 .cab 、 .ocx 、 .class 等程序和文件进行签名。
从数字证书的技术角度分,CA中心发放的证书分为两类:SSL证书和SET证书。一般地说,SSL 证书(安全套接层)是服务于银行对企业或企业对企业的电子商务活动的;而SET(安全电子交易)证书则服务于持卡消费、网上购物。虽然它们都是用于识别身份和数字签名的证书,但它们的信任体系完全不同,而且所符合的标准也不一样。
1.3 数字证书的格式
证书主要的文件类型和协议有: PEM、DER、PFX、JKS、KDB、CER、KEY、CSR、CRT、CRL 、OCSP、SCEP等。
1.3.1 PEM 格式
Openssl使用 PEM(Privacy Enhanced Mail)格式来存放各种信息,它是 openssl 默认采用的信息存放方式。Openssl 中的 PEM 文件一般包含如下信息:
内容类型:表明本文件存放的是什么信息内容,它的形式为“——-BEGIN XXXX ——”,与结尾的“——END XXXX——”对应。
头信息:表明数据是如果被处理后存放,openssl 中用的最多的是加密信息,比如加密算法以及初始化向量 iv。
信息体:为 BASE64 编码的数据。可以包括所有私钥(RSA 和 DSA)、公钥(RSA 和 DSA)和 (x509) 证书。它存储用 Base64 编码的 DER 格式数据,用 ascii 报头包围,因此适合系统之间的文本模式传输。
使用PEM格式存储的证书:
1 -----BEGIN CERTIFICATE-----
2 MIIF6TCCBNGgAwIBAgIQSSOR8EYFvAGtG16qv0lZ4DANBgkqhkiG9w0BAQsFADBC
3 MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMS
4 UmFwaWRTU0wgU0hBMjU2IENBMB4XDTE3MDQyNDAwMDAwMFoXDTE5MDQyNDIzNTk1
5 OVowITEfMB0GA1UEAwwWc2VjdXJpdHkucHVqaW53YW5nLmNvbTCCASIwDQYJKoZI
6 hvcNAQEBBQADggEPADCCAQoCggEBANrPWriCfyigreL9cVAyEPesYScRd176xhH0
7 .
8 -----END CERTIFICATE-----
使用PEM格式存储的私钥:
1 -----BEGIN RSA PRIVATE KEY-----
2 MIIF6TCCBNGgAwIBAgIQSSOR8EYFvAGtG16qv0lZ4DANBgkqhkiG9w0BAQsFADBC
3 MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMS
4 UmFwaWRTU0wgU0hBMjU2IENBMB4XDTE3MDQyNDAwMDAwMFoXDTE5MDQyNDIzNTk1
5 OVowITEfMB0GA1UEAwwWc2VjdXJpdHkucHVqaW53YW5nLmNvbTCCASIwDQYJKoZI
6 hvcNAQEBBQADggEPADCCAQoCggEBANrPWriCfyigreL9cVAyEPesYScRd176xhH0
7
8 -----END RSA PRIVATE KEY-----
使用PEM格式存储的证书请求文件:
1 -----BEGIN CERTIFICATE REQUEST-----
2 MIIF6TCCBNGgAwIBAgIQSSOR8EYFvAGtG16qv0lZ4DANBgkqhkiG9w0BAQsFADBC
3 MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMS
4 UmFwaWRTU0wgU0hBMjU2IENBMB4XDTE3MDQyNDAwMDAwMFoXDTE5MDQyNDIzNTk1
5 OVowITEfMB0GA1UEAwwWc2VjdXJpdHkucHVqaW53YW5nLmNvbTCCASIwDQYJKoZI
6 hvcNAQEBBQADggEPADCCAQoCggEBANrPWriCfyigreL9cVAyEPesYScRd176xhH0
7 .
8 -----END CERTIFICATE REQUEST-----
1.3.2 DER 格式
辨别编码规则 (DER) 可包含所有私钥、公钥和证书。它是大多数浏览器的缺省格式,并按 ASN1 DER 格式存储。它是无报头的 - PEM 是用文本报头包围的 DER。
PFX 或 P12 – 公钥加密标准 #12 (PKCS#12) 可包含所有私钥、公钥和证书。其以二进制格式存储,也称为 PFX 文件。通常可以将Apache/OpenSSL使用的“KEY文件 + CRT文件”格式合并转换为标准的PFX文件,你可以将PFX文件格式导入到微软IIS 5/6、微软ISA、微软Exchange Server等软件。转换时需要输入PFX文件的加密密码。
1.3.3 JKS格式
通常可以将Apache/OpenSSL使用的“KEY文件 + CRT文件”格式”转换为标准的Java Key Store(JKS)文件。JKS文件格式被广泛的应用在基于Java的WEB服务器、应用服务器、中间件。你可以将JKS文件导入到TOMCAT、 WEBLOGIC 等软件。
1.3.4 KDB格式
通常可以将Apache/OpenSSL使用的“KEY文件 + CRT文件”格式转换为标准的IBM KDB文件。KDB文件格式被广泛的应用在IBM的WEB服务器、应用服务器、中间件。你可以将KDB文件导入到IBM HTTP Server、IBM Websphere 等软件。
1.3.5 CSR 格式(证书请求文件 Certificate Signing Request)
生成 X509 数字证书前,一般先由用户提交证书申请文件,然后由 CA 来签发证书。大致过程如下(X509 证书申请的格式标准为 pkcs#10 和 rfc2314):
用户生成自己的公私钥对;
构造自己的证书申请文件,符合 PKCS#10 标准。该文件主要包括了用户信息、公钥以及一些可选的属性信息,并用自己的私钥给该内容签名;
用户将证书申请文件提交给 CA;
CA 验证签名,提取用户信息,并加上其他信息(比如颁发者等信息),用 CA 的私钥签发数字证书;
说明:数字证书(如x.509)是将用户(或其他实体)身份与公钥绑定的信息载体。一个合法的数字证书不仅要符合 X509 格式规范,还必须有 CA的签名。用户不仅有自己的数字证书,还必须有对应的私钥。X509v3数字证书主要包含的内容有:证书版本、证书序列号、签名算法、颁发者信息、有效时间、持有者信息、公钥信息、颁发者 ID、持有者 ID 和扩展项。
1.3.6 OCSP格式(在线证书状态协议 Online Certificate StatusProtocol,rfc2560)
用于实时表明证书状态。OCSP 客户端通过查询 OCSP服务来确定一个证书的状态,可以提供给使用者一个或多个数字证书的有效性资料,它建立一个可实时响应的机制,让用户可以实时确认每一张证书的有效性,解决由CRL引发的安全问题。。OCSP 可以通过 HTTP协议来实现。rfc2560 定义了 OCSP 客户端和服务端的消息格式。
1.3.7 CRL格式(证书吊销列表 Certification Revocation List)
是一种包含撤销的证书列表的签名数据结构。CRL是证书撤销状态的公布形式,CRL 就像信用卡的黑名单,用于公布某些数字证书不再有效。CRL是一种离线的证书状态信息。它以一定的周期进行更新。CRL 可以分为完全 CRL和增量 CRL。在完全 CRL中包含了所有的被撤销证书信息,增量 CRL 由一系列的 CRL 来表明被撤销的证书信息,它每次发布的 CRL 是对前面发布 CRL的增量扩充。基本的 CRL 信息有:被撤销证书序列号、撤销时间、撤销原因、签名者以及 CRL 签名等信息。基于 CRL的验证是一种不严格的证书认证。CRL 能证明在 CRL 中被撤销的证书是无效的。但是,它不能给出不在 CRL中的证书的状态。如果执行严格的认证,需要采用在线方式进行认证,即 OCSP认证。一般是由CA签名的一组电子文档,包括了被废除证书的唯一标识(证书序列号),CRL用来列出已经过期或废除的数字证书。它每隔一段时间就会更新,因此必须定期下载该清单,才会取得最新信息。
1.3.8 SCEP (简单证书注册协议)
基于文件的证书登记方式需要从您的本地计算机将文本文件复制和粘贴到证书发布中心,和从证书发布中心复制和粘贴到您的本地计算机。 SCEP可以自动处理这个过程但是CRLs仍然需要手工的在本地计算机和CA发布中心之间进行复制和粘贴。
1.3.9 PKCS7 (加密消息语法(pkcs7)
是各种消息存放的格式标准。这些消息包括:数据、签名数据、数字信封、签名数字信封、摘要数据和加密数据。
1.3.10 PKCS12 (个人数字证书标准,Public Key Cryptography Standards #12)
包含了公钥和私钥的二进制格式的证书形式,一般以 pfx 作为证书文件后缀名。用于存放用户证书、crl、用户私钥以及证书链,pkcs12 中的私钥是加密存放的。
1.3.11 CER 一般指使用DER格式的证书
CER 证书一般是以 DER 二进制编码的证书,证书中没有私钥,以 *.cer 作为证书文件后缀名。证书可以以 BASE64 编码输出,以Base64 编码的证书证书中没有私钥,BASE64 编码格式的证书文件,也是以 *.cer 作为证书文件后缀名。
1.3.12 CRT 证书文件,可以是PEM格式
1.3.13 KEY 一般指PEM格式的私钥文件
二、加密算法介绍
在生成数据证书是用户可选择不同的加密方式对数据进行加密,常见的加密算法可以分成三类,对称加密算法,非对称加密算法和Hash算法。
2.1 对称加密
在对称加密算法中,加密使用的密钥和解密使用的密钥是相同的。也就是说,加密和解密都是使用的同一个密钥。因此对称加密算法要保证安全性的话,密钥要做好保密,只能让使用的人知道,不能对外公开。在对称加密算法中,加密和解密都是使用同一个密钥,不区分公钥和私钥。
对称加密算法的优点在于加解密的高速度和使用长密钥时的难破解性。假设两个用户需要使用对称加密方法加密然后交换数据,则用户最少需要2个密钥并交换使用,如果企业内用户有n个,则整个企业共需要n×(n-1) 个密钥,密钥的生成和分发将成为企业信息部门的恶梦。对称加密算法的安全性取决于加密密钥的保存情况,但要求企业中每一个持有密钥的人都保守秘密是不可能的,他们通常会有意无意的把密钥泄漏出去——如果一个用户使用的密钥被入侵者所获得,入侵者便可以读取该用户密钥加密的所有文档,如果整个企业共用一个加密密钥,那整个企业文档的保密性便无从谈起。
常见的对称加密算法:DES、3DES、DESX、Blowfish、IDEA、RC4、RC5、RC6和AES。
DES是一种分组数据加密技术(先将数据分成固定长度的小数据块,之后进行加密),速度较快,适用于大量数据加密,而3DES是一种基于DES的加密算法,使用3个不同密匙对同一个分组数据块进行3次加密,如此以使得密文强度更高。
相较于DES和3DES算法而言,AES算法有着更高的速度和资源使用效率,安全级别也较之更高了,被称为下一代加密标准。
2.2 非对称加密
在非对称加密算法中,加密使用的密钥和解密使用的密钥是不相同的,也称为公私钥加密,也就是说加密使用的密钥和解密使用的密钥不同。
假设两个用户要加密交换数据,双方交换公钥,使用时一方用对方的公钥加密,另一方即可用自己的私钥解密。如果企业中有n个用户,企业需要生成n对密钥,并分发n个公钥。由于公钥是可以公开的,用户只要保管好自己的私钥即可,因此加密密钥的分发将变得十分简单。同时,由于每个用户的私钥是唯一的,其他用户除了可以可以通过信息发送者的公钥来验证信息的来源是否真实,还可以确保发送者无法否认曾发送过该信息。非对称加密的缺点是加解密速度要远远慢于对称加密,在某些极端情况下,甚至能比非对称加密慢上1000倍。
常见的非对称加密算法:RSA、ECC(移动设备用)、Diffie-Hellman、El Gamal、DSA(数字签名用)。
RSA和DSA的安全性及其它各方面性能都差不多,而ECC较之则有着很多的性能优越,包括处理速度,带宽要求,存储空间等等。
2.3 Hash算法
Hash算法特别的地方在于它是一种单向算法,用户可以通过Hash算法对目标信息生成一段特定长度的唯一的Hash值,却不能通过这个Hash值重新获得目标信息。因此Hash算法常用在不可还原的密码存储、信息完整性校验等。
常见的Hash算法:MD2、MD4、MD5、HAVAL、SHA、SHA-1、HMAC、HMAC-MD5、HMAC-SHA1。
这几种算法只生成一串不可逆的密文,经常用其效验数据传输过程中是否经过修改,因为相同的生成算法对于同一明文只会生成唯一的密文,若相同算法生成的密文不同,则证明传输数据进行过了修改。通常在数据传说过程前,使用MD5和SHA1算法均需要发送和接收数据双方在数据传送之前就知道密匙生成算法,而HMAC与之不同的是需要生成一个密匙,发送方用此密匙对数据进行摘要处理(生成密文),接收方再利用此密匙对接收到的数据进行摘要处理,再判断生成的密文是否相同。
加密算法的效能通常可以按照算法本身的复杂程度、密钥长度(密钥越长越安全)、加解密速度等来衡量。上述的算法中,除了DES密钥长度不够、MD2速度较慢已逐渐被淘汰外,其他算法仍在目前的加密系统产品中使用。
三、生成数字证书的方式
数字证书可以通过在线工具,脚本代码,KEYTOOL工具,OPEN SSL工具等多种方式生成。下面以常用的RSA非对称加密为例子,向大家介绍几种常用的数字证书生成方式。
3.1 利用KEYTOOL工具生成数字证书
KEYTOOL 是个密钥和证书管理工具,可以在 JAVA 环境下管理安全钥匙与证书的生成与安装。它还是一个有效的安全钥匙和证书的管理工具,使用户能够管理自己的公钥/私钥对及相关证书。它管理一个存储了私有钥匙和验证相应公共钥匙的与它们相关联的X.509 证书链的keystore(相当一个数据库,里面可存放多个X.509标准的证书)。能够使用户使用数字签名来管理他们自己的私有/公共钥匙对,管理用来作自我鉴定的相关的证书,管理数据完整性和鉴定服务,还能使用户在通信时缓存它们的公共钥匙。
3.1.1 KEYTOOL命令介绍
KEYTOOL通是以keytool开关,当中常用命令有:
1 keytool -genkey -alias casserver -keypass cas123 -keyalg RSA -keystore casserver.keystore -validity 365
2 keytool -export -alias casserver -storepass cas123 -file casserver.cer -keystore casserver.keystore
3 keytool -import -trustcacerts -alias casserver -storepass cas123 -file casserver.cer –keystore cacerts
- -genkey 在用户主目录中创建一个默认文件".keystore",还会产生一个mykey的别名,mykey中包含用户的公钥、私钥和证书
在没有指定生成位置的情况下,keystore会存在用户系统默认目录,如:对于window xp系统,会生成在系统的C:/Documents and Settings/UserName/文件名为“.keystore”
- -alias 产生别名 -keystore 指定密钥库的名称(产生的各类信息将不在.keystore文件中) -keyalg 指定密钥的算法 (如 RSA DSA(如果不指定默认采用DSA))
- -validity 指定创建的证书有效期多少天-keysize 指定密钥长度
- -storepass 指定密钥库的密码(获取keystore信息所需的密码)
- -keypass 指定别名条目的密码(私钥的密码)
- -dname 指定证书拥有者信息
例如: "CN=名字与姓氏,OU=组织单位名称,O=组织名称,L=城市或区域名称,ST=州或省份名称,C=单位的两字母国家代码"
- -list 显示密钥库中的证书信息
例如: keytool -list -v -keystore 是指定 keystore -storepass 密码 -v 显示密钥库中的证书详细信息
- -export 将别名指定的证书导出到文件
例如:keytool -export -alias 需要导出的别名 -keystore 指定keystore -file 指定导出的证书位置及证书名称 -storepass 密码
- -file 参数指定导出到文件的文件名
- -delete 删除密钥库中某条目
例如:keytool -delete -alias 指定需删除的别 -keystore 指定keystore -storepass 密码
- -printcert 查看导出的证书信息
例如:keytool -printcert -file leslie.crt
- -keypasswd 修改密钥库中指定条目口令
例如:keytool -keypasswd -alias 需修改的别名 -keypass 旧密码 -new 新密码 -storepass keystore密码 -keystore sage
- -storepasswd 修改keystore口令
例如:keytool -storepasswd -keystore c:/leslie.keystore(需修改口令的keystore) -storepass 123456(原始密码) -new 888888(新密码)
- -import 将已签名数字证书导入密钥库
例如: keytool -import -alias 指定导入条目的别名 -keystore 指定keystore -file 需导入的证书
3.1.2 生成 *.keystore 文件流程
首先执行以下命令,分别输入密钥库口令、姓名,单位,组织,城市,省份,国家等信息,经确认后生成对应的 leslie.keystore 文件。注意 *.keystore 文件相当于一个资源库,后面的公钥、私钥、证书等都依赖于它生成,必须谨慎保管。
1 keytool -genkey -alias everygold -keypass 123456 -keyalg RSA -keystore leslie.keystore -validity 365
提示: -alias指定别名为 everygold; -keyalg 指定 RSA 算法;-keypass 指定私钥密码为123456;
-keystore 指定密钥文件名称为 leslie.keystore;-validity指定有效期为365天。
3.1.3 生成数字证书
根据上述生成的 leslie.keystore 文件,执行以下命令就可以生成数字证书 leslie.cer
1 keytool -export -alias everygold -storepass 123456 -file leslie.cer -keystore leslie.keystore
提示: -alias 指定别名为 everygold; -storepass 指定私钥为 123456;
-file 指定导出证书的文件名为 leslie.cer;-keystore 指定之前生成的密钥文件的文件名
注意: -alias 和- storepass 必须为生成 leslie.keystore 密钥文件时所指定的别名和密码一致,否则证书导出失败
生成证书
若需要获取 BASE64 或 DER 证书,可以使用导出功能,在图片上按 “复制到文件”,选择文件格式即可。
以文本格式打开导出的 BASE64 证书,可以看到
1 -----BEGIN CERTIFICATE-----
2 MIICRjCCAa+gAwIBAgIEIvzKsDANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD
3 TjELMAkGA1UECBMCR0QxCzAJBgNVBAcTAkdaMQ4wDAYDVQQKEwVwdWppbjEMMAoG
4 A1UECxMDU3VuMQ8wDQYDVQQDEwZMZXNsaWUwHhcNMTcwODI5MDMwMjE4WhcNMTgw
5 ODI5MDMwMjE4WjBWMQswCQYDVQQGEwJDTjELMAkGA1UECBMCR0QxCzAJBgNVBAcT
6 AkdaMQ4wDAYDVQQKEwVwdWppbjEMMAoGA1UECxMDU3VuMQ8wDQYDVQQDEwZMZXNs
7 aWUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKFVrCaKFi2GtJjyuWSPrJah
8 9 -----END CERTIFICATE-----
或者使用以下语句,也可查看到导出的证书
1 keytool -list -rfc -keystore d:/leslie.keystore -storepass 123456
显示结果与上述方法一致
3.2 脚本代码生成证书
若对KEYTOOL工具不太熟悉的朋友 ,也可通过JAVA代码直接生成数字证书,原理与KEYTOOL生成的基本一致。
1 public class CerTest {
2 public static void main(String[] args){
3 CerTest test=new CerTest();
4 //生成 keystore 文件
5 test.getKeyStore();
6 //生成 *.cer 证书文件
7 test.export();
8 }
9
10 public void execCommand(String[] arstringCommand) {
11 for (int i = 0; i < arstringCommand.length; i++) {
12 System.out.print(arstringCommand[i] + " ");
13 }
14 try {
15 Runtime.getRuntime().exec(arstringCommand);
16
17 } catch (Exception e) {
18 System.out.println(e.getMessage());
19 }
20 }
21
22 public void execCommand(String arstringCommand) {
23 try {
24 Runtime.getRuntime().exec(arstringCommand);
25
26 } catch (Exception e) {
27 System.out.println(e.getMessage());
28 }
29 }
30
31 /**
32 * 生成 *.keystore
33 */
34 public void getKeyStore() {
35 String[] arstringCommand = new String[] {
36
37 "cmd ", "/k",
38 "start", // cmd Shell命令
39
40 "keytool",
41 "-genkey", // -genkey表示生成密钥
42 "-validity", // -validity指定证书有效期(单位:天),这里是365天
43 "365",
44 "-keysize",// 指定密钥长度
45 "1024",
46 "-alias", // -alias指定别名,这里是everygold
47 "everygold",
48 "-keyalg", // -keyalg 指定密钥的算法 (如 RSA DSA(如果不指定默认采用DSA))
49 "RSA",
50 "-keystore", // -keystore指定存储位置,这里是d:/leslie.keystore
51 "d:/leslie.keystore",
52 "-dname",// CN=(名字与姓氏), OU=(组织单位名称), O=(组织名称), L=(城市或区域名称),
53 // ST=(州或省份名称), C=(单位的两字母国家代码)"
54 "CN=(leslie), OU=(everygold), O=(pujinwang), L=(Guangzhou), ST=(Guangdong), C=(CN)",
55 "-storepass", // 指定密钥库的密码(获取keystore信息所需的密码)
56 "123456",
57 "-keypass",// 指定别名条目的密码(私钥的密码)
58 "123456",
59 "-v"// -v 显示密钥库中的证书详细信息
60 };
61 execCommand(arstringCommand);
62 }
63
64 /**
65 * 导出证书文件
66 */
67 public void export() {
68
69 String[] arstringCommand = new String[] {
70
71 "cmd ", "/k",
72 "start", // cmd Shell命令
73 "keytool",
74 "-export", // - export指定为导出操作
75 "-keystore", // -keystore指定keystore文件,这里是d:/leslie.keystore
76 "d:/leslie.keystore",
77 "-alias", // -alias指定别名,这里是ss
78 "everygold",
79 "-file",//-file指向导出路径
80 "d:/leslie.cer",
81 "-storepass",// 指定密钥库的密码
82 "123456"
83 };
84 execCommand(arstringCommand);
85
86 }
87 }
运行成功后,可获取与3.1节相同的 leslie.keystore 文件与 leslie.cer 数字证书。
若需要获取 BASE64 或 DER 证书,也可使用与 3.1.3 节所述方式获取 ,在此不再重复介绍。
3.3 利用在线工具获取数字证书
如果觉得使用KEYTOOL或代码生成数字证书过于繁琐,可以直接使用在线生成工具生成免费数字证书。一般在线生成的证书有效期为 3 个月到 1年,到期后需要续费或证书无效。以下是几个常用的在线证书生成工具,由于用法比较简单,在此不作详细介绍。
- Amazon Web Services (AWS) 是 Amazon.com 旗下的一个网络云服务站点
address:https://aws.amazon.com/cn/?nc2=h_lg
- ChinaSSL是亚狐科技旗下专为客户提供数字证书、网络安全服务的站点
address:https://csr.chinassl.net/index.html
- MySSL 则是亚洲诚信(TRUSTASIA)旗下专为用户提供网络安全云服务平台
address:https://myssl.com/csr_create.html
四、获取公钥和私钥
在第二节已经介绍过,在加密算法中有对称加密,非对称加密,Hash算法等几类。在对称加密算法中,加密使用的密钥和解密使用的密钥是相同的,使用起来比较简单。而公钥与私钥一般用于非对称的加密方式,是安全性最高,使用最为频密的加密方式,下面几节,将为大家介绍一下非对称加密的使用方式。
公钥(Public Key)与私钥(Private Key)是通过一种算法得到的一个密钥对(即一个公钥和一个私钥),公钥是密钥对中公开的部分,私钥则是非公开的部分。公钥通常用于加密会话密钥、验证数字签名,加密数据可以用相应的私钥进行数据解密。通过这种算法得到的密钥对能保证在世界范围内是唯一的。使用这个密钥对的时候,如果用其中一个密钥加密一段数据,必须用另一个密钥解密。比如用公钥加密数据就必须用私钥解密,如果用私钥签名则必须用公钥验签,否则数据将不会成功生成。
由于使用 KEYTOOL 等工具无法直接导出公钥和私钥,所以必须通过代码进行导出。而公钥和私钥都是二进制数据,所以一般 用Base 64 方式进行保存。下面以上述有证书为例子,导出对应的公钥与私钥。
1 public abstract class Coder {
2
3 /**
4 * BASE64解密
5 *
6 * @param key
7 * @return
8 * @throws Exception
9 */
10 public static byte[] decryptBASE64(String key) throws Exception {
11 return (new BASE64Decoder()).decodeBuffer(key);
12 }
13
14 /**
15 * BASE64加密
16 *
17 * @param key
18 * @return
19 * @throws Exception
20 */
21 public static String encryptBASE64(byte[] key) throws Exception {
22 return (new BASE64Encoder()).encodeBuffer(key).replace("\r", "").replace("\n", "");
23 }
24 }
25
26 public class KeyStoreTool{
27
28 /**
29 * Java密钥库(Java Key Store,JKS)KEY_STORE
30 */
31 public static final String KEY_STORE = "JKS";
32
33 public static final String X509 = "X.509";
34
35 /**
36 * 获得KeyStore
37 *
38 * @version 2016-3-16
39 * @param keyStorePath
40 * @param password
41 * @return
42 * @throws Exception
43 */
44 public static KeyStore getKeyStore(String keyStorePath, String password)
45 throws Exception {
46
47 FileInputStream is = new FileInputStream(keyStorePath);
48 KeyStore ks = KeyStore.getInstance(KEY_STORE);
49 ks.load(is, password.toCharArray());
50 is.close();
51 return ks;
52 }
53
54 /**
55 * 由KeyStore获得私钥
56 * @param keyStorePath
57 * @param alias
58 * @param storePass
59 * @return
60 * @throws Exception
61 */
62 public static PrivateKey getPrivateKey(String keyStorePath, String alias, String storePass, String keyPass) throws Exception {
63 KeyStore ks = getKeyStore(keyStorePath, storePass);
64 PrivateKey key = (PrivateKey) ks.getKey(alias, keyPass.toCharArray());
65 return key;
66 }
67
68 /**
69 * 由Certificate获得公钥
70 * @param keyStorePath
71 * KeyStore路径
72 * @param alias
73 * 别名
74 * @param storePass
75 * KeyStore访问密码
76 * @return
77 * @throws Exception
78 */
79 public static PublicKey getPublicKey(String keyStorePath, String alias, String storePass) throws Exception {
80 KeyStore ks = getKeyStore(keyStorePath, storePass);
81 PublicKey key = ks.getCertificate(alias).getPublicKey();
82 return key;
83 }
84
85 /**
86 * 从KeyStore中获取公钥,并经BASE64编码
87 * @param keyStorePath
88 * @param alias
89 * @param storePass
90 * @return
91 * @throws Exception
92 */
93 public static String getStrPublicKey(String keyStorePath, String alias,String storePass) throws Exception{
94 PublicKey key = getPublicKey(keyStorePath, alias, storePass);
95 String strKey = Coder.encryptBASE64(key.getEncoded());
96 return strKey;
97 }
98
99 /*
100 * 获取经BASE64编码后的私钥
101 * @param keyStorePath
102 * @param alias
103 * @param storePass
104 * @param keyPass
105 * @return
106 * @throws Exception
107 */
108 public static String getStrPrivateKey(String keyStorePath, String alias,String storePass, String keyPass) throws Exception{
109
110 PrivateKey key = getPrivateKey(keyStorePath, alias, storePass, keyPass);
111 String strKey = Coder.encryptBASE64(key.getEncoded());
112 return strKey;
113 }
114
115 public static void main(String args[]){
116 // 公钥
117 String strPublicKey = "";
118 // 私钥
119 String strPrivateKey = "";
120
121 try {
122 strPublicKey = KeyStoreCoder.getStrPublicKey("d://leslie.keystore", "everygold", "123456");
123 System.out.println("公钥 = 【" + strPublicKey + "】");
124
125 strPrivateKey = KeyStoreCoder.getStrPrivateKey("d://leslie.keystore", "everygold", "123456", "123456");
126 System.out.println("\n私钥 = 【" + strPrivateKey + "】");
127 } catch (Exception e1) {
128 e1.printStackTrace();
129 }
130 }
131 }
输出结果:
为方便保存,一般我们会以Base64位方式把公钥与私钥存储起来
publicKey.key 公钥文件:
1 -----BEGIN PUBLIC KEY-----
2 MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDbUPe3WGFA0QPGCrPrXCUR7K7M
3 aZQY1btYZrAFjpT/k00zkj/AfcUeEZk6Tf+9mgvZ3KRVvSFaA9kYiVCJOjGfnW2H
4 fk6u7iOwSs/kwpC5uUzdoWlc5ZX7iC9SACXJgDg/T5HBRpXpsEkxhzWLUKy1FQDC
5 KduLuEFdzaO4XsSX7QIDAQAB
6 -----END PUBLIC KEY-----
privateKey.key 私钥文件:
1 -----BEGIN PRIVATE KEY-----
2 MIICeQIBADANBgkqhkiG9w0BAQEFAASCAmMwggJfAgEAAoGBANtQ97dYYUDRA8YK
3 s+tcJRHsrsxplBjVu1hmsAWOlP+TTTOSP8B9xR4RmTpN/72aC9ncpFW9IVoD2RiJ
4 UIk6MZ+dbYd+Tq7uI7BKz+TCkLm5TN2haVzllfuIL1IAJcmAOD9PkcFGlemwSTGH
5 NYtQrLUVAMIp24u4QV3No7hexJftAgMBAAECgYEAkcm/8Yv5kimfFY3VzhXBuqmY
6 BOAGB4BEel5AkmEWoNIdVrPYVzAD0ZonPn/NCg+V4yvtveTsf7bhIJNfCum5Q8NL
7 V0YNn5+C1JMZoI9BrRXQjCH30Oy78QfHH9ATigDZ7cr/ke/0hJqO4hks++XlM6Oy
8 MIuoHy1WUTy5Hm3qbWECQQDvZFDixgwYwiyC9fzEj8NmWyINZx+Ny2DhnZKtKN6r
9 o7aplGfBPU9NZ/vLQk7AZPS+24hhu7CYlOUhhhoQjWr1AkEA6ogaMAQfPslyrl1W
10 R2KkPEOVbSSy4IJ5ZIBeJCDisgEtLb9EZ4JzXIfN6usyiJNtwf5k04zEkWBz1f5r
11 mtTOGQJBAMSodkI1TA6yxPo4thOLvovBZfH4u1UytD3jwnD52CLMdOxOfAWlJhaC
12 y7iomiU3Sk/X7OvM0kAmYSzvC055vlkCQQDOR0sRNG7u4Gv3pKyAOOhPAPpqdr7F
13 7LwsgyNKD4qUGajM9c+KYxhoKCIbHybhLRp6Z+/yiXtSik0XyKCIG+fxAkEAsdlz
14 YkpcG6T38wC0px+Mhq06AIhEF3sy3wLbM3d4ABlNMj3HqlHMPtvCV1L3dpc/8y89
15 dAPu9OiHf8nyar9eVQ==
16 -----END PRIVATE KEY-----
现在 *.keystore 、*.cer 证书、Base64 证书、公钥文件 public.key 私钥文件 private.key 都已成功生成,下面的章节将为大家介绍数据加密、数据解密、数字签名、数字验签的使用方式。但在此之前,我想先为大家讲解一下它们的概念与应用场景。
记得在第二节曾经向大家介绍过对称加密与非对称加密的区别,由于对称加密的加密与解密的密钥都是一致,加密解密双方都同时拥有密钥,容易造成密钥的泄露。所以一般企业在不同的业务流程下都会使用不同的密钥,以防数据被泄露。但在大型的企业中,若使用对称加密这种方式,企业将会产生大量的密钥,难于管理而且安全性难以保障,并不可取,所以就产生了非对称加密方式。
非对称加密的情况下,在企业需要获取客户端数据时,可以把公钥向客户端公开,数据进行加密后,就算加密数据被涉取,在没有私钥的情况,数据内容都不会被破解,确保了数据的安全性。这时,只要企业保证私钥的保密前提下,一个公钥可以向多个客户端进行公开用作数据传输加密。
而数字签名的应用场景有点相反,数字签名是企业为客户端确认数据来源的准确性而提供的服务。一般应用于政府机关、行政部门、金融行业、资讯行业等企业的数据发布上。数据都是由企业通过私钥进行签名,只要客户端拥有对应的公钥,就可以对数据进行验签。只要验签成功,就能证明该数据是来源此数字证书所属的企业,以保证数据来源的可靠性。一般在国家政策的发布,企业数据的公开,经济数据的公开等场景下应该最为广泛。
五、数字证书加密与解密
经过上面的介绍,大家应该了解到数据加密、数据解密、数字签名、数字验签的使用场景。
下面将为大家介绍数据加密与解密方式:
1 public abstract class Coder {
2
3 /**
4 * BASE64解密
5 *
6 * @param key
7 * @return
8 * @throws Exception
9 */
10 public static byte[] decryptBASE64(String key) throws Exception {
11 return (new BASE64Decoder()).decodeBuffer(key);
12 }
13
14 /**
15 * BASE64加密
16 *
17 * @param key
18 * @return
19 * @throws Exception
20 */
21 public static String encryptBASE64(byte[] key) throws Exception {
22 return (new BASE64Encoder()).encodeBuffer(key).replace("\r", "").replace("\n", "");
23 }
24 }
25
26 public class MyCoder extends Coder{
27
28 /**
29 * 使用公钥加密数据
30 * @param publicKey
31 * @param srcData
32 * @return
33 * @throws Exception
34 */
35 public static String encryptByPublicKey(String publicKey, String srcData) throws Exception{
36 //解密
37 byte[] pk = Coder.decryptBASE64(publicKey);
38 X509EncodedKeySpec spec = new X509EncodedKeySpec(pk);
39 KeyFactory kf = KeyFactory.getInstance("RSA");
40 //获取公钥
41 PublicKey pubKey = kf.generatePublic(spec);
42
43 // 对数据加密
44 Cipher cipher = Cipher.getInstance("RSA");
45 cipher.init(Cipher.ENCRYPT_MODE, pubKey);
46
47 byte[] doFinal = cipher.doFinal(srcData.getBytes());
48 return encryptBASE64(doFinal);
49 }
50
51
52 /*
53 * 使用私钥解密数据
54 * @param privateKey
55 * @param data
56 * @return
57 * @throws Exception
58 */
59 public static String descryptByPrivateKey(String privateKey, String data) throws Exception{
60 // BASE64转码解密私钥
61 byte[] pk = Coder.decryptBASE64(privateKey);
62 // BASE64转码解密密文
63 byte[] text = decryptBASE64(data);
64 PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(pk);
65 KeyFactory kf = KeyFactory.getInstance("RSA");
66 // 获取私钥
67 PrivateKey prvKey = kf.generatePrivate(spec);
68
69 // 对数据加密
70 Cipher cipher = Cipher.getInstance("RSA");
71 cipher.init(Cipher.DECRYPT_MODE, prvKey);
72
73 byte[] doFinal = cipher.doFinal(text);
74 return new String(doFinal);
75 }
76
77 public static void main(){
78 // 公钥
79 String strPublicKey = "";
80 // 私钥
81 String strPrivateKey = "";
82
83 try {
84 strPublicKey = KeyStoreTool.getStrPublicKey("d://leslie.keystore", "everygold", "123456");
85 strPrivateKey = KeyStoreTool.getStrPrivateKey("d://leslie.keystore", "everygold", "123456", "123456");
86 } catch (Exception e1) {
87 e1.printStackTrace();
88 }
89
90 // 原文
91 String originalText = "原文 = 虽然我穷,但是再穷也要去旅游!";
92 System.out.println(originalText);
93
94 try {
95 // RSA算法 公钥加密随机数
96 String secretText = MyCoder.encryptByPublicKey(strPublicKey, originalText);
97 System.out.println("\n经RSA公钥加密后 = " + secretText);
98 System.out.println("\n经RSA公钥加密后长度 = " + secretText.length());
99
100 String text = MyCoder.descryptByPrivateKey(strPrivateKey, secretText);
101 System.out.println("\n经RSA私钥解密后 = 【" + text + "】");
102 System.out.println("\n经RSA私钥解密后长度 = 【" + text.length() + "】");
103
104 } catch (Exception e) {
105 e.printStackTrace();
106 }
107 }
108 }
测试结果
当然,如果公钥和私钥已经保存在 public.key 与 private.key 文件里,就可以直接从文件读取,无须再通过 *. keystore 文件获取。
六、数字证书签名与验签
上面介绍过签名与验签主要用于政策机关,金融机构,权威信息网站对外公报信息时使用。
一般使用场景下,签名与验签往往会与加密解密同时使用,企业会生成两对密钥,一对用于对企业正式的注册名称进行签名,另一对用于详细数据的加密。客户验签后就可证明信息来源的真确性,然后再对详细信息进行解密。
签名与验签代码如下:
1 public class MySign {
2 /*
3 * @param keyStorePath 密钥库存储路径
4 * @param alias 密钥库别名
5 * @param password 密钥库密码
6 */
7 private static String keyStorePath,alias,password;
8
9 private static Certificate getCertificate()
10 throws Exception {
11 KeyStore keyStore = KeyStoreTool.getKeyStore(keyStorePath, password);
12 Certificate certificate = keyStore.getCertificate(alias);
13 return certificate;
14 }
15
16 public static void setKeyStorePath(String path){
17 MySign.keyStorePath=path;
18 }
19
20 public static void setAlias(String alias){
21 MySign.alias=alias;
22 }
23
24 public static void setPassword(String password){
25 MySign.password=password;
26 }
27
28 /*
29 * 生成数据签名
30 * @param data 源数据
31 */
32 public static byte[] sign(byte[] data)
33 throws Exception {
34 // 获得证书
35 X509Certificate x509Certificate = (X509Certificate) getCertificate();
36 // 获取KeyStore
37 KeyStore keyStore = KeyStoreTool.getKeyStore(keyStorePath, password);
38 // 取得私钥
39 PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, password.toCharArray());
40 // 构建签名
41 Signature signature = Signature.getInstance(x509Certificate.getSigAlgName());
42 signature.initSign(privateKey);
43 signature.update(data);
44 return signature.sign();
45 }
46
47 /*
48 * 生成数据签名并以BASE64编码
49 * @param data 源数据
50 */
51 public static String signToBase64(String data)
52 throws Exception {
53 byte[] byteData=data.getBytes();
54 return Base64.encode(sign(byteData));
55 }
56
57 /*
58 * 对二进制数据进行验签
59 * @param data 已加密数据
60 * @param sign 数据签名[BASE64]
61 */
62 public static boolean verifySign(byte[] data, String sign)
63 throws Exception {
64 // 获得证书
65 X509Certificate x509Certificate = (X509Certificate) getCertificate();
66 // 获得公钥
67 PublicKey publicKey = x509Certificate.getPublicKey();
68 // 构建签名
69 Signature signature = Signature.getInstance(x509Certificate.getSigAlgName());
70 signature.initVerify(publicKey);
71 signature.update(data);
72 return signature.verify(Base64.decode(sign));
73 }
74
75 /*
76 * 对String数据进行验签
77 * @param data 字符串
78 * @param sign 数据签名[BASE64]
79 */
80 public static boolean verifySginString(String data, String sign)
81 throws Exception {
82 byte[] byteData = data.getBytes();
83 return verifySign(byteData, sign);
84 }
85
86 public static void main(String[] args) throws Exception {
87
88 MySign.setKeyStorePath("d://leslie.keystore");
89 MySign.setPassword("123456");
90 MySign.setAlias("everygold");
91 String sign="驴友的天空俱乐部";
92 String base64=MySign.signToBase64(sign);
93 System.out.println("签名为:"+sign+"\n\n签名后数据:\n"+base64);
94 boolean isRight=MySign.verifySginString(sign,base64);
95 System.out.println("\n验签结果:"+isRight);
96 }
97 }