今天来点实际工作中的硬通货!
与计费系统打交道,少不了用到加密/解密实现。为了安全起见,通过非对称加密交换对称加密密钥更是不可或缺。那么需要通过什么载体传递非对称算法公钥/私钥信息?数字证书是公钥的载体,而密钥库可以包含公钥、私钥信息。

JKSPKCS#12都是比较常用的两种密钥库格式/标准。对于前者,搞Java开发,尤其是接触过HTTPS平台的朋友,并不陌生。JKS文件(通常为*.jks或*.keystore,扩展名无关)可以通过Java原生工具——KeyTool生成;而后者PKCS#12文件(通常为*.p12或*.pfx,意味个人信息交换文件),则是通过更为常用的OpenSSL工具产生。

当然,这两者之间是可以通过导入/导出的方式进行转换的!当然,这种转换需要通过KeyTool工具进行!

回归正题,计费同事遇到一个难题:合作方交给他们一个*.pfx文件,需要他们从中提取密钥,然后进行加密交互。其实,通过Java直接操作密钥库文件(或个人信息交换文件)对于一般Java开发人员来说,这都是个冷门。不接触数字安全,根本不知所云。况且,Java原生的密钥库文件格式为JKS,如何操作*.pfx文件?密钥库操作需要获知密钥库别名,*.pfx别名是什么?!接下来就解决这些问题!



方案:

  1. 通过keytool密钥库导入命令importkeystore,将密钥库格式由PKCS#12转换为JKS。
  2. 检索新生成的密钥库文件,提取别名信息。
  3. 由密钥库文件导出数字证书(这里将用到别名)。
  4. 通过代码提取公钥/私钥、签名算法等

先看格式转换:

  1. echo 格式转换
  2. keytool -importkeystore -v  -srckeystore zlex.pfx -srcstoretype pkcs12 -srcstorepass 123456 -destkeystore zlex.keystore -deststoretype jks -deststorepass 123456

-importkeystore导入密钥库,通过格式设定,我们可以将PKCS#12文件转换为JKS格式。

-v显示详情

-srckeystore源密钥库,这里是zlex.pfx

-srcstoretype源密钥库格式,这里为pkcs12

-srcstorepass源密钥库密码,这里为123456

-destkeystore目标密钥库,这里为zlex.keystore

-deststoretype目标密钥库格式,这里为jks,默认值也如此

-deststorepass目标密钥库密码,这里为123456

通过这个操作,我们能够获得所需的密钥库文件zlex.keystore。



这时,我们已经获得了密钥库文件,只要确定对应的别名信息,就可以提取公钥/私钥,以及数字证书,进行加密交互了!

  1. echo 查看证书
  2. keytool -list -keystore zlex.keystore -storepass 123456 -v

-list列举密钥库

-keystore密钥库,这里是zlex.keystore

-storepass密钥库密码,这里是123456

-v显示详情



这里需要细致观察一下别名信息!!!就是红框中的数字1!!!





现在,我们把证书导出!

  1. echo 导出证书
  2. keytool -exportcert -alias 1 -keystore zlex.keystore -file zlex.crt -storepass 123456

-exportcert导出证书

-alias别名,这里是1

-keystore密钥库,这里是zlex.keystore

-file证书文件,这里是zlex.crt

-storepass密钥库密码,这里是123456



现在证书也导出了,我们可以提取公钥/私钥,进行加密/解密,签名/验证操作了!当然,即便没有证书,我们也能够通过密钥库(JKS格式)文件获得证书,以及公钥/私钥、签名算法等。

补充代码, 其实就是对Java加密技术(八)的修改!

  1. /**
  2. * 2010-8-11
  3. */
  4. import java.io.FileInputStream;
  5. import java.security.KeyStore;
  6. import java.security.PrivateKey;
  7. import java.security.PublicKey;
  8. import java.security.Signature;
  9. import java.security.cert.Certificate;
  10. import java.security.cert.CertificateFactory;
  11. import java.security.cert.X509Certificate;
  12. import java.util.Date;
  13. import javax.crypto.Cipher;
  14. /**
  15. * 证书操作类
  16. *
  17. * @author <a href="mailto:zlex.dongliang@gmail.com">梁栋</a>
  18. * @since 1.0
  19. */
  20. public class CertificateCoder {
  21. /**
  22. * Java密钥库(Java Key Store,JKS)KEY_STORE
  23. */
  24. public static final String KEY_STORE = "JKS";
  25. public static final String X509 = "X.509";
  26. /**
  27. * 由 KeyStore获得私钥
  28. *
  29. * @param keyStorePath
  30. * @param keyStorePassword
  31. * @param alias
  32. * @param aliasPassword
  33. * @return
  34. * @throws Exception
  35. */
  36. private static PrivateKey getPrivateKey(String keyStorePath,
  37. String keyStorePassword, String alias, String aliasPassword)
  38. throws Exception {
  39. KeyStore ks = getKeyStore(keyStorePath, keyStorePassword);
  40. PrivateKey key = (PrivateKey) ks.getKey(alias,
  41. aliasPassword.toCharArray());
  42. return key;
  43. }
  44. /**
  45. * 由 Certificate获得公钥
  46. *
  47. * @param certificatePath
  48. * @return
  49. * @throws Exception
  50. */
  51. private static PublicKey getPublicKey(String certificatePath)
  52. throws Exception {
  53. Certificate certificate = getCertificate(certificatePath);
  54. PublicKey key = certificate.getPublicKey();
  55. return key;
  56. }
  57. /**
  58. * 获得Certificate
  59. *
  60. * @param certificatePath
  61. * @return
  62. * @throws Exception
  63. */
  64. private static Certificate getCertificate(String certificatePath)
  65. throws Exception {
  66. CertificateFactory certificateFactory = CertificateFactory
  67. .getInstance(X509);
  68. FileInputStream in = new FileInputStream(certificatePath);
  69. Certificate certificate = certificateFactory.generateCertificate(in);
  70. in.close();
  71. return certificate;
  72. }
  73. /**
  74. * 获得Certificate
  75. *
  76. * @param keyStorePath
  77. * @param keyStorePassword
  78. * @param alias
  79. * @return
  80. * @throws Exception
  81. */
  82. private static Certificate getCertificate(String keyStorePath,
  83. String keyStorePassword, String alias) throws Exception {
  84. KeyStore ks = getKeyStore(keyStorePath, keyStorePassword);
  85. Certificate certificate = ks.getCertificate(alias);
  86. return certificate;
  87. }
  88. /**
  89. * 获得KeyStore
  90. *
  91. * @param keyStorePath
  92. * @param password
  93. * @return
  94. * @throws Exception
  95. */
  96. private static KeyStore getKeyStore(String keyStorePath, String password)
  97. throws Exception {
  98. FileInputStream is = new FileInputStream(keyStorePath);
  99. KeyStore ks = KeyStore.getInstance(KEY_STORE);
  100. ks.load(is, password.toCharArray());
  101. is.close();
  102. return ks;
  103. }
  104. /**
  105. * 私钥加密
  106. *
  107. * @param data
  108. * @param keyStorePath
  109. * @param keyStorePassword
  110. * @param alias
  111. * @param aliasPassword
  112. * @return
  113. * @throws Exception
  114. */
  115. public static byte[] encryptByPrivateKey(byte[] data, String keyStorePath,
  116. String keyStorePassword, String alias, String aliasPassword)
  117. throws Exception {
  118. // 取得私钥
  119. PrivateKey privateKey = getPrivateKey(keyStorePath, keyStorePassword,
  120. alias, aliasPassword);
  121. // 对数据加密
  122. Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
  123. cipher.init(Cipher.ENCRYPT_MODE, privateKey);
  124. return cipher.doFinal(data);
  125. }
  126. /**
  127. * 私钥解密
  128. *
  129. * @param data
  130. * @param keyStorePath
  131. * @param alias
  132. * @param keyStorePassword
  133. * @param aliasPassword
  134. * @return
  135. * @throws Exception
  136. */
  137. public static byte[] decryptByPrivateKey(byte[] data, String keyStorePath,
  138. String alias, String keyStorePassword, String aliasPassword)
  139. throws Exception {
  140. // 取得私钥
  141. PrivateKey privateKey = getPrivateKey(keyStorePath, keyStorePassword,
  142. alias, aliasPassword);
  143. // 对数据加密
  144. Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
  145. cipher.init(Cipher.DECRYPT_MODE, privateKey);
  146. return cipher.doFinal(data);
  147. }
  148. /**
  149. * 公钥加密
  150. *
  151. * @param data
  152. * @param certificatePath
  153. * @return
  154. * @throws Exception
  155. */
  156. public static byte[] encryptByPublicKey(byte[] data, String certificatePath)
  157. throws Exception {
  158. // 取得公钥
  159. PublicKey publicKey = getPublicKey(certificatePath);
  160. // 对数据加密
  161. Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
  162. cipher.init(Cipher.ENCRYPT_MODE, publicKey);
  163. return cipher.doFinal(data);
  164. }
  165. /**
  166. * 公钥解密
  167. *
  168. * @param data
  169. * @param certificatePath
  170. * @return
  171. * @throws Exception
  172. */
  173. public static byte[] decryptByPublicKey(byte[] data, String certificatePath)
  174. throws Exception {
  175. // 取得公钥
  176. PublicKey publicKey = getPublicKey(certificatePath);
  177. // 对数据加密
  178. Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
  179. cipher.init(Cipher.DECRYPT_MODE, publicKey);
  180. return cipher.doFinal(data);
  181. }
  182. /**
  183. * 验证Certificate
  184. *
  185. * @param certificatePath
  186. * @return
  187. */
  188. public static boolean verifyCertificate(String certificatePath) {
  189. return verifyCertificate(new Date(), certificatePath);
  190. }
  191. /**
  192. * 验证Certificate是否过期或无效
  193. *
  194. * @param date
  195. * @param certificatePath
  196. * @return
  197. */
  198. public static boolean verifyCertificate(Date date, String certificatePath) {
  199. boolean status = true;
  200. try {
  201. // 取得证书
  202. Certificate certificate = getCertificate(certificatePath);
  203. // 验证证书是否过期或无效
  204. status = verifyCertificate(date, certificate);
  205. } catch (Exception e) {
  206. status = false;
  207. }
  208. return status;
  209. }
  210. /**
  211. * 验证证书是否过期或无效
  212. *
  213. * @param date
  214. * @param certificate
  215. * @return
  216. */
  217. private static boolean verifyCertificate(Date date, Certificate certificate) {
  218. boolean status = true;
  219. try {
  220. X509Certificate x509Certificate = (X509Certificate) certificate;
  221. x509Certificate.checkValidity(date);
  222. } catch (Exception e) {
  223. status = false;
  224. }
  225. return status;
  226. }
  227. /**
  228. * 签名
  229. *
  230. * @param keyStorePath
  231. * @param alias
  232. * @param keyStorePassword
  233. * @param aliasPassword
  234. * @return
  235. * @throws Exception
  236. */
  237. public static byte[] sign(byte[] sign, String keyStorePath, String alias,
  238. String keyStorePassword, String aliasPassword) throws Exception {
  239. // 获得证书
  240. X509Certificate x509Certificate = (X509Certificate) getCertificate(
  241. keyStorePath, keyStorePassword, alias);
  242. // 取得私钥
  243. PrivateKey privateKey = getPrivateKey(keyStorePath, keyStorePassword,
  244. alias, aliasPassword);
  245. // 构建签名
  246. Signature signature = Signature.getInstance(x509Certificate
  247. .getSigAlgName());
  248. signature.initSign(privateKey);
  249. signature.update(sign);
  250. return signature.sign();
  251. }
  252. /**
  253. * 验证签名
  254. *
  255. * @param data
  256. * @param sign
  257. * @param certificatePath
  258. * @return
  259. * @throws Exception
  260. */
  261. public static boolean verify(byte[] data, byte[] sign,
  262. String certificatePath) throws Exception {
  263. // 获得证书
  264. X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath);
  265. // 获得公钥
  266. PublicKey publicKey = x509Certificate.getPublicKey();
  267. // 构建签名
  268. Signature signature = Signature.getInstance(x509Certificate
  269. .getSigAlgName());
  270. signature.initVerify(publicKey);
  271. signature.update(data);
  272. return signature.verify(sign);
  273. }
  274. /**
  275. * 验证Certificate
  276. *
  277. * @param keyStorePath
  278. * @param keyStorePassword
  279. * @param alias
  280. * @return
  281. */
  282. public static boolean verifyCertificate(Date date, String keyStorePath,
  283. String keyStorePassword, String alias) {
  284. boolean status = true;
  285. try {
  286. Certificate certificate = getCertificate(keyStorePath,
  287. keyStorePassword, alias);
  288. status = verifyCertificate(date, certificate);
  289. } catch (Exception e) {
  290. status = false;
  291. }
  292. return status;
  293. }
  294. /**
  295. * 验证Certificate
  296. *
  297. * @param keyStorePath
  298. * @param keyStorePassword
  299. * @param alias
  300. * @return
  301. */
  302. public static boolean verifyCertificate(String keyStorePath,
  303. String keyStorePassword, String alias) {
  304. return verifyCertificate(new Date(), keyStorePath, keyStorePassword,
  305. alias);
  306. }
  307. }

相信上述代码已经帮朋友们解决了相当多的问题!

给出测试类:

  1. import static org.junit.Assert.*;
  2. import java.util.Date;
  3. import org.apache.commons.codec.binary.Hex;
  4. import org.junit.Test;
  5. /**
  6. * 证书操作验证类
  7. *
  8. * @author <a href="mailto:zlex.dongliang@gmail.com">梁栋</a>
  9. * @version 1.0
  10. * @since 1.0
  11. */
  12. public class CertificateCoderTest {
  13. private String certificatePath = "zlex.crt";
  14. private String keyStorePath = "zlex.keystore";
  15. private String keyStorePassword = "123456";
  16. private String aliasPassword = "123456";
  17. private String alias = "1";
  18. @Test
  19. public void test() throws Exception {
  20. System.err.println("公钥加密——私钥解密");
  21. String inputStr = "Ceritifcate";
  22. byte[] data = inputStr.getBytes();
  23. byte[] encrypt = CertificateCoder.encryptByPublicKey(data,
  24. certificatePath);
  25. byte[] decrypt = CertificateCoder.decryptByPrivateKey(encrypt,
  26. keyStorePath, alias, keyStorePassword, aliasPassword);
  27. String outputStr = new String(decrypt);
  28. System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);
  29. // 验证数据一致
  30. assertArrayEquals(data, decrypt);
  31. // 验证证书有效
  32. assertTrue(CertificateCoder.verifyCertificate(certificatePath));
  33. }
  34. @Test
  35. public void testSign() throws Exception {
  36. System.err.println("私钥加密——公钥解密");
  37. String inputStr = "sign";
  38. byte[] data = inputStr.getBytes();
  39. byte[] encodedData = CertificateCoder.encryptByPrivateKey(data,
  40. keyStorePath, keyStorePassword, alias, aliasPassword);
  41. byte[] decodedData = CertificateCoder.decryptByPublicKey(encodedData,
  42. certificatePath);
  43. String outputStr = new String(decodedData);
  44. System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);
  45. assertEquals(inputStr, outputStr);
  46. System.err.println("私钥签名——公钥验证签名");
  47. // 产生签名
  48. byte[] sign = CertificateCoder.sign(encodedData, keyStorePath, alias,
  49. keyStorePassword, aliasPassword);
  50. System.err.println("签名:\r" + Hex.encodeHexString(sign));
  51. // 验证签名
  52. boolean status = CertificateCoder.verify(encodedData, sign,
  53. certificatePath);
  54. System.err.println("状态:\r" + status);
  55. assertTrue(status);
  56. }
  57. @Test
  58. public void testVerify() throws Exception {
  59. System.err.println("密钥库证书有效期验证");
  60. boolean status = CertificateCoder.verifyCertificate(new Date(),
  61. keyStorePath, keyStorePassword, alias);
  62. System.err.println("证书状态:\r" + status);
  63. assertTrue(status);
  64. }
  65. }

第一个测试方法,用于提取公钥/私钥进行加密/解密操作。

第二个测试方法,用于提取签名算法进行签名/验证操作。

第三个测试方法,用于测试密钥库该别名对应的证书,当前日期下,是否有效。







OK,任务完成,密钥成功提取,剩下的都是代码基本功了!

JKS和PKCS#12的更多相关文章

  1. PKCS#12

    http://blog.csdn.net/cuiran/article/details/7816696 数字证书介绍 一.什么是数字证书 数字证书就是互联网通讯中标志通讯各方身份信息的一系列数据,提供 ...

  2. BouncyCastle产生一个PKCS#12规范的PFX/p12证书

    RT,在C#中实现,依赖.netFramework2.0 BouncyCastle中提供了PKCS12Store,Pkcs12StoreBuilder,AsymmetricKeyEntry,X509C ...

  3. RSA私钥和公钥文件格式 (pkcs#1, pkcs#8, pkcs#12, pem)

    RSA私钥和公钥文件格式 (pkcs#1, pkcs#8, pkcs#12, pem) 2018年03月07日 11:57:22 阅读数:674 Format Name Description PKC ...

  4. C#使用BouncyCastle生成PKCS#12数字证书

    背景 生成数字证书用于PDF文档数字签名 数字证书需要考虑环境兼容性,如linux.windows 网上资料不全或版本多样 本文章主要介绍了在C#中使用BouncyCastle生成PKCS#12个人信 ...

  5. SSL证书请求文件(CSR)生成指南 - Tomcat

    SSL证书请求文件(CSR)生成指南 - Tomcat http://www.zhenssl.com/support/CSRgen/tomcat_CSR.htm   重要注意事项 An Importa ...

  6. 移动互联网实战--Apple的APNS桩推送服务的实现(1)

    前记: 相信大家在搞IOS推送服务的开发时, 会直接使用javapns api来简单实现, 调试也直连Apple的APNS服务(产品/测试版)来实现. 很少有人会写个APNS的桩服务, 事实也是如此. ...

  7. openssl命令

    author:JevonWei 版权声明:原创作品 1.构建根证书 构建根证书前,需要构建随机数文件(.rand),完整命令如 openssl rand -out private/.rand 1000 ...

  8. An annotation based command line parser

    Java命令行选项解析之Commons-CLI & Args4J & JCommander http://rensanning.iteye.com/blog/2161201 JComm ...

  9. java-信息安全(十七)-*.PFX(*.p12)&个人信息交换文件

    原文地址 http://snowolf.iteye.com/blog/735294 与计费系统打交道,少不了用到加密/解密实现.为了安全起见,通过非对称加密交换对称加密密钥更是不可或缺.那么需要通过什 ...

随机推荐

  1. android 自定义控件——(四)圆形进度条

    ----------------------------------↓↓圆形进度条(源代码下有属性解释)↓↓---------------------------------------------- ...

  2. UI-切圆角、透明度、取消按钮点击高亮效果、按钮文字带下划线

    一.切UIView的某个角为圆角 如果需要将UIView的4个角全部都为圆角,做法相当简单,只需设置其Layer的cornerRadius属性即可(项目需要使用QuartzCore框架).而若要指定某 ...

  3. MapReduce 的架构

    主从结构 主节点,只有一个 : JobTracker   ,JobTracker 一般情况下,运行在 namenode 这台机器上. 从节点,有很多个 : TaskTrackers  ,  部署在剩下 ...

  4. [Erlang 0123] Erlang EPMD

     epmd进程和Erlang节点进程如影随形,在Rabbitmq集群,Ejabberd集群,Couchbase集群产品文档中都会有相当多的内容讲epmd,epmd是什么呢?   epmd 是Erlan ...

  5. mysql半同步(semi-sync)源码实现

    mysql复制简单介绍了mysql semi-sync的出现的原因,并说明了semi-sync如何保证不丢数据.这篇文章主要侧重于semi-sync的实现,结合源码将semi-sync的实现过程展现给 ...

  6. ubuntu 常见错误--Could not get lock /var/lib/dpkg/lock

    ubuntu 常见错误--Could not get lock /var/lib/dpkg/lock 通过终端安装程序sudo apt-get install xxx时出错:E: Could not ...

  7. 关于Spring的构造函数,init-method,和依赖注入的先后顺序

    接触学习Spring一段时间了,今天突然脑子短路,竟然一时间忘记了构造函数,init-method,和依赖注入的先后顺序,然后打开IDE去验证后.构造函数-->依赖注入-->init-me ...

  8. Nginx 访问日志轮询切割

    Nginx 访问日志轮询切割脚本 #!/bin/sh Dateformat=`date +%Y%m%d` Basedir="/application/nginx" Nginxlog ...

  9. Eclipse注释模板设置详解

    设置注释模板的入口:Window->Preference->Java->Code Style->Code Template 然后展开Comments节点就是所有需设置注释的元素 ...

  10. 揭开C++类中虚表的“神秘面纱”

    C++类中的虚表结构是C++对象模型中一个重要的知识点,这里咱们就来深入分析下虚表的在内存中的结构. C++一个类中有虚函数的话就会有一个虚表指针,其指向对应的虚表,一般一个类只会有一个虚表,每个虚表 ...