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

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开发第五章SharedPreferences与文件管理

    一.SharedPreferences (1)SharedPreferences能够用来保存一些属于基本数据类型的数据. (2)保存数据,删除数据都是由SharedPreferences的内部接口Ed ...

  2. 设置这些之后,Google突然可以打开了

    打开的是:https://www.google.com.hk

  3. Freemarker 程序开发

    Freemarker 程序开发 现在web开发中,多使用freemarker 来描述页面.通常会使用的macro来定义各种组件,从而达到UI组件的复用.结合使用其它的指定,可快速的描述一个html页面 ...

  4. MySQL误操作后如何快速恢复数据

    基本上每个跟数据库打交道的程序员(当然也可能是你同事)都会碰一个问题,MySQL误操作后如何快速回滚?比如,delete一张表,忘加限制条件,整张表没了.假如这还是线上环境核心业务数据,那这事就闹大了 ...

  5. MySQL 数据库的备份与恢复

    一.MySQL 常见的备份方式 1. 直接拷贝数据库文件(物理拷贝) 2. 使用 mysqldump 工具备份 3. 使用 mysqlhotcopy 工具备份 4. 使用 mysql 的主从同步复制, ...

  6. linux命令在线手册

    下面几个网址有一些 Linux命令的在线手册,而且还是中文的,还可以搜索.非常方便 Linux命令手册 Linux命令大全 Linux中文man在线手册 每日一linux命令

  7. jQuery.extend 函数详解

    JQuery的extend扩展方法:      Jquery的扩展方法extend是我们在写插件的过程中常用的方法,该方法有一些重载原型,在此,我们一起去了解了解.      一.Jquery的扩展方 ...

  8. [LeetCode] Word Ladder II 词语阶梯之二

    Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from ...

  9. [LeetCode] Longest Common Prefix 最长共同前缀

    Write a function to find the longest common prefix string amongst an array of strings. 这道题让我们求一系列字符串 ...

  10. 怎样简单灵活地将DataTable中的数据赋值给model

    最近在做的一个项目中,有13个方法都需要用到同一种处理方式:通过SQL语句从数据库获取一条指定的数据,并将该数据中的每个值都赋值给一个model,再将这个model中的数据通过微信发送出去.每个方法都 ...