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相当于客户端,用收到的数字证书进行解密和验签。

  1. package jdbc.pro.lin;
  2.  
  3. import java.io.FileInputStream;
  4. import java.io.FileNotFoundException;
  5. import java.io.IOException;
  6. import java.io.InputStream;
  7. import java.security.InvalidKeyException;
  8. import java.security.KeyStore;
  9. import java.security.KeyStoreException;
  10. import java.security.NoSuchAlgorithmException;
  11. import java.security.PrivateKey;
  12. import java.security.PublicKey;
  13. import java.security.Signature;
  14. import java.security.SignatureException;
  15. import java.security.UnrecoverableKeyException;
  16. import java.security.cert.Certificate;
  17. import java.security.cert.CertificateException;
  18. import java.security.cert.CertificateFactory;
  19. import java.security.cert.X509Certificate;
  20.  
  21. import javax.crypto.BadPaddingException;
  22. import javax.crypto.Cipher;
  23. import javax.crypto.IllegalBlockSizeException;
  24. import javax.crypto.NoSuchPaddingException;
  25.  
  26. public class MyCertifacate {
  27. private static final String STORE_PASS = "Changeme123";
  28. private static final String ALIAS = "myCertificate";
  29. private static final String KEYSTORE_PATH = "D:\\JavaDemo\\Certifacate\\myKeystore.keystore";
  30. private static final String CERT_PATH = "D:\\JavaDemo\\Certifacate\\myCer.cer";
  31. private static final String PLAIN_TEXT = "MANUTD is the most greatest club in the world.";
  32. /** JDK6只支持X.509标准的证书 */
  33. private static final String CERT_TYPE = "X.509";
  34.  
  35. public static void main(String[] args) throws IOException {
  36. /**
  37. * 假设现在有这样一个场景 。A机器上的数据,需要加密导出,然后将导出文件放到B机器上导入。 在这个场景中,A相当于服务器,B相当于客户端
  38. */
  39.  
  40. /** A */
  41. KeyStore keyStore = getKeyStore(STORE_PASS, KEYSTORE_PATH);
  42. PrivateKey privateKey = getPrivateKey(keyStore, ALIAS, STORE_PASS);
  43. X509Certificate certificate = getCertificateByKeystore(keyStore, ALIAS);
  44.  
  45. /** 加密和签名 */
  46. byte[] encodedText = encode(PLAIN_TEXT.getBytes(), privateKey);
  47. byte[] signature = sign(certificate, privateKey, PLAIN_TEXT.getBytes());
  48.  
  49. /** 现在B收到了A的密文和签名,以及A的可信任证书 */
  50. X509Certificate receivedCertificate = getCertificateByCertPath(
  51. CERT_PATH, CERT_TYPE);
  52. PublicKey publicKey = getPublicKey(receivedCertificate);
  53. byte[] decodedText = decode(encodedText, publicKey);
  54. System.out.println("Decoded Text : " + new String(decodedText));
  55. System.out.println("Signature is : "
  56. + verify(receivedCertificate, decodedText, signature));
  57. }
  58.  
  59. /**
  60. * 加载密钥库,与Properties文件的加载类似,都是使用load方法
  61. *
  62. * @throws IOException
  63. */
  64. public static KeyStore getKeyStore(String storepass, String keystorePath)
  65. throws IOException {
  66. InputStream inputStream = null;
  67. try {
  68. KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
  69. inputStream = new FileInputStream(keystorePath);
  70. keyStore.load(inputStream, storepass.toCharArray());
  71. return keyStore;
  72. } catch (KeyStoreException | NoSuchAlgorithmException
  73. | CertificateException | IOException e) {
  74. // TODO Auto-generated catch block
  75. e.printStackTrace();
  76. } finally {
  77. if (null != inputStream) {
  78. inputStream.close();
  79. }
  80. }
  81. return null;
  82. }
  83.  
  84. /**
  85. * 获取私钥
  86. *
  87. * @param keyStore
  88. * @param alias
  89. * @param password
  90. * @return
  91. */
  92. public static PrivateKey getPrivateKey(KeyStore keyStore, String alias,
  93. String password) {
  94. try {
  95. return (PrivateKey) keyStore.getKey(alias, password.toCharArray());
  96. } catch (UnrecoverableKeyException | KeyStoreException
  97. | NoSuchAlgorithmException e) {
  98. // TODO Auto-generated catch block
  99. e.printStackTrace();
  100. }
  101. return null;
  102. }
  103.  
  104. /**
  105. * 获取公钥
  106. *
  107. * @param certificate
  108. * @return
  109. */
  110. public static PublicKey getPublicKey(Certificate certificate) {
  111. return certificate.getPublicKey();
  112. }
  113.  
  114. /**
  115. * 通过密钥库获取数字证书,不需要密码,因为获取到Keystore实例
  116. *
  117. * @param keyStore
  118. * @param alias
  119. * @return
  120. */
  121. public static X509Certificate getCertificateByKeystore(KeyStore keyStore,
  122. String alias) {
  123. try {
  124. return (X509Certificate) keyStore.getCertificate(alias);
  125. } catch (KeyStoreException e) {
  126. // TODO Auto-generated catch block
  127. e.printStackTrace();
  128. }
  129. return null;
  130. }
  131.  
  132. /**
  133. * 通过证书路径生成证书,与加载密钥库差不多,都要用到流。
  134. *
  135. * @param path
  136. * @param certType
  137. * @return
  138. * @throws IOException
  139. */
  140. public static X509Certificate getCertificateByCertPath(String path,
  141. String certType) throws IOException {
  142. InputStream inputStream = null;
  143. try {
  144. // 实例化证书工厂
  145. CertificateFactory factory = CertificateFactory
  146. .getInstance(certType);
  147. // 取得证书文件流
  148. inputStream = new FileInputStream(path);
  149. // 生成证书
  150. Certificate certificate = factory.generateCertificate(inputStream);
  151.  
  152. return (X509Certificate) certificate;
  153. } catch (CertificateException | IOException e) {
  154. // TODO Auto-generated catch block
  155. e.printStackTrace();
  156. } finally {
  157. if (null != inputStream) {
  158. inputStream.close();
  159. }
  160. }
  161. return null;
  162.  
  163. }
  164.  
  165. /**
  166. * 从证书中获取加密算法,进行签名
  167. *
  168. * @param certificate
  169. * @param privateKey
  170. * @param plainText
  171. * @return
  172. */
  173. public static byte[] sign(X509Certificate certificate,
  174. PrivateKey privateKey, byte[] plainText) {
  175. /** 如果要从密钥库获取签名算法的名称,只能将其强制转换成X509标准,JDK 6只支持X.509类型的证书 */
  176. try {
  177. Signature signature = Signature.getInstance(certificate
  178. .getSigAlgName());
  179. signature.initSign(privateKey);
  180. signature.update(plainText);
  181. return signature.sign();
  182. } catch (NoSuchAlgorithmException | InvalidKeyException
  183. | SignatureException e) {
  184. // TODO Auto-generated catch block
  185. e.printStackTrace();
  186. }
  187.  
  188. return null;
  189. }
  190.  
  191. /**
  192. * 验签,公钥包含在证书里面
  193. *
  194. * @param certificate
  195. * @param decodedText
  196. * @param receivedignature
  197. * @return
  198. */
  199. public static boolean verify(X509Certificate certificate,
  200. byte[] decodedText, final byte[] receivedignature) {
  201. try {
  202. Signature signature = Signature.getInstance(certificate
  203. .getSigAlgName());
  204. /** 注意这里用到的是证书,实际上用到的也是证书里面的公钥 */
  205. signature.initVerify(certificate);
  206. signature.update(decodedText);
  207. return signature.verify(receivedignature);
  208. } catch (NoSuchAlgorithmException | InvalidKeyException
  209. | SignatureException e) {
  210. // TODO Auto-generated catch block
  211. e.printStackTrace();
  212. }
  213. return false;
  214. }
  215.  
  216. /**
  217. * 加密。注意密钥是可以获取到它适用的算法的。
  218. *
  219. * @param plainText
  220. * @param privateKey
  221. * @return
  222. */
  223. public static byte[] encode(byte[] plainText, PrivateKey privateKey) {
  224. try {
  225. Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
  226. cipher.init(Cipher.ENCRYPT_MODE, privateKey);
  227. return cipher.doFinal(plainText);
  228. } catch (NoSuchAlgorithmException | NoSuchPaddingException
  229. | InvalidKeyException | IllegalBlockSizeException
  230. | BadPaddingException e) {
  231. // TODO Auto-generated catch block
  232. e.printStackTrace();
  233. }
  234.  
  235. return null;
  236.  
  237. }
  238.  
  239. /**
  240. * 解密,注意密钥是可以获取它适用的算法的。
  241. *
  242. * @param encodedText
  243. * @param publicKey
  244. * @return
  245. */
  246. public static byte[] decode(byte[] encodedText, PublicKey publicKey) {
  247. try {
  248. Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
  249. cipher.init(Cipher.DECRYPT_MODE, publicKey);
  250. return cipher.doFinal(encodedText);
  251. } catch (NoSuchAlgorithmException | NoSuchPaddingException
  252. | InvalidKeyException | IllegalBlockSizeException
  253. | BadPaddingException e) {
  254. // TODO Auto-generated catch block
  255. e.printStackTrace();
  256. }
  257.  
  258. return null;
  259. }
  260. }

数字证书简介及Java编码实现的更多相关文章

  1. 数字签名、数字证书的原理以及证书的获得java版

    数字签名原理简介(附数字证书) 首先要了解什么叫对称加密和非对称加密,消息摘要这些知识. 1. 非对称加密 在通信双方,如果使用非对称加密,一般遵从这样的原则:公钥加密,私钥解密.同时,一般一个密钥加 ...

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

    1 keytool命令总结 一.创建数字证书 交互模式 使用默认的密钥库.keystore(文件夹是c: Documents and Settingusername)和算法(DSA) keytool  ...

  3. android利用数字证书对程序签名

     签名的必要性 1.  防止你已安装的应用被恶意的第三方覆盖或替换掉. 2.  开发者的身份标识,签名可以防止抵赖等事件的发生. 开发Android的人这么多,完全有可能大家都把类名,包名起成了一个同 ...

  4. android 利用数字证书对程序签名

    签名的必要性 1.  防止你已安装的应用被恶意的第三方覆盖或替换掉. 2.  开发者的身份标识,签名可以防止抵赖等事件的发生. 开发Android的人这么多,完全有可能大家都把类名,包名起成了一个同样 ...

  5. 【转】Android数字证书

    Android数字证书的作用是非常重要的.Android操作系统每一个应用程序的安装都需要经过这一数字证书的签名. Android手机操作系统作为一款比较流行的开源系统在手机领域占据着举足轻重的地位. ...

  6. Java使用数字证书加密通信(加解密/加签验签)

    本文中使用的Base64Utils.java可参考:http://www.cnblogs.com/shindo/p/6346618.html 证书制作方法可参考:http://www.cnblogs. ...

  7. 如何利用java程序实现加密所需的公钥、密钥、数字证书

    本篇的主要目的在于实现pdf的数字签名问题,只是作为我学习知识的总结. 1.数字签名算法的概述 本部分主要参考于:https://blog.csdn.net/lovelichao12/article/ ...

  8. JAVA对数字证书的常用操作(转载)

    一:需要包含的包 import java.security. * ; import java.io. * ; import java.util. * ; import java.security. * ...

  9. Java加密技术(八)——数字证书

    原文:http://snowolf.iteye.com/blog/391931 请大家在阅读本篇内容时先阅读 Java加密技术(四),预先了解RSA加密算法. 在构建Java代码实现前,我们需要完成证 ...

随机推荐

  1. 码云分布式之 Brzo 服务器

    摘要: 码云是国内最大的代码托管平台,为了支持更大的用户规模,开发团队也在对一些组件进行大规模的重构. 前言 码云是国内最大的代码托管平台.码云基于 Gitlab 5.5 开发,经过几年的开发已经和官 ...

  2. Android中的Parcel机制 实现Bundle传递对象

    Android中的Parcel机制    实现了Bundle传递对象    使用Bundle传递对象,首先要将其序列化,但是,在Android中要使用这种传递对象的方式需要用到Android Parc ...

  3. C++解析JSON之JsonCPP

    一.JSON简介 JSON全称为JavaScript ObjectNotation,它是一种轻量级的数据交换格式,易于阅读.编写.解析. JSON由两种基本结构构成: )"名称/值" ...

  4. java学习多线程之卖票示例

    这一节我们来说一个示例就是卖票示例: 需求: 我们现在有100张票,然后分四个窗口来卖,直到卖完为止. 思路: 1.先定一个一个票类,描述票的属性,还有打印卖出的票,并且实现Runnable中的run ...

  5. 检查string是否为double

    之前写的方法,使用try catch来处理 如果能捕获异常就说明问题 public bool CheckLegal() { double number; bool flag = true; try { ...

  6. PLSQL调用webservice

      1.   用途简介 为什么要在Oracle中访问WebService?在系统实现中,有时会有直接在数据库端利用触发器.存储过程等方式进行数据传递.分发的业务,而其中可能会涉及一些业务逻辑,为了处理 ...

  7. Unity3D游戏开发入门(一)

    视频: 慕课网适合入门 http://www.imooc.com/video/6582 蛮牛网: http://www.manew.com/ 圣殿中文手册 5.3.2破解工具 面试题 pdf 书籍:

  8. HDOJ --- 2151 Worm

    Worm Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  9. ARM学习笔记3——数据处理指令

    一.数据处理指令概述 1.概念 数据处理指令是指对存放在寄存器中的数据进行处理的指令.主要包括算术指令.逻辑指令.比较与测试指令以及乘法指令 如果在数据处理指令前使用S前缀,指令的执行结果将会影响CP ...

  10. 点分治练习: boatherds

    [题面] 求一颗树上距离为K的点对是否存在 输入数据 n,m 接下来n-1条边a,b,c描述a到b有一条长度为c的路径 接下来m行每行询问一个K 输出数据 对于每个K每行输出一个答案,存在输出“AYE ...