JKS和PKCS#12都是比較常用的兩種密鑰庫格式/標準,。對于前者,,搞Java開發(fā),尤其是接觸過HTTPS平臺的朋友,并不陌生,。JKS文件(通常為*.jks或*.keystore,,擴展名無關(guān))可以通過Java原生工具——KeyTool生成;而后者PKCS#12文件(通常為*.p12或*.pfx,,意味個人信息交換文件),,則是通過更為常用的OpenSSL工具產(chǎn)生。
當然,,這兩者之間是可以通過導入/導出的方式進行轉(zhuǎn)換的,!當然,這種轉(zhuǎn)換需要通過KeyTool工具進行,!
回歸正題,,計費同事遇到一個難題:合作方交給他們一個*.pfx文件,需要他們從中提取密鑰,,然后進行加密交互,。其實,通過Java直接操作密鑰庫文件(或個人信息交換文件)對于一般Java開發(fā)人員來說,,這都是個冷門,。不接觸數(shù)字安全,根本不知所云,。況且,,Java原生的密鑰庫文件格式為JKS,如何操作*.pfx文件,?密鑰庫操作需要獲知密鑰庫別名,,*.pfx別名是什么?,!這些難題都留給了我,,當然,我欣然接受并回報碩果,!
方案:
- 通過keytool密鑰庫導入命令importkeystore,,將密鑰庫格式由PKCS#12轉(zhuǎn)換為JKS,。
- 檢索新生成的密鑰庫文件,,提取別名信息。
- 由密鑰庫文件導出數(shù)字證書(這里將用到別名),。
- 通過代碼提取公鑰/私鑰、簽名算法等
先看可是轉(zhuǎn)換:
- echo 格式轉(zhuǎn)換
- keytool -importkeystore -v -srckeystore zlex.pfx -srcstoretype pkcs12 -srcstorepass 123456 -destkeystore zlex.keystore -deststoretype jks -deststorepass 123456
echo 格式轉(zhuǎn)換 keytool -importkeystore -v -srckeystore zlex.pfx -srcstoretype pkcs12 -srcstorepass 123456 -destkeystore zlex.keystore -deststoretype jks -deststorepass 123456
-importkeystore導入密鑰庫,,通過格式設定,,我們可以將PKCS#12文件轉(zhuǎn)換為JKS格式。
-v顯示詳情
-srckeystore源密鑰庫,這里是zlex.pfx
-srcstoretype源密鑰庫格式,,這里為pkcs12
-srcstorepass源密鑰庫密碼,,這里為123456
-destkeystore目標密鑰庫,這里為zlex.keystore
-deststoretype目標密鑰庫格式,,這里為jks,,默認值也如此
-deststorepass目標密鑰庫密碼,這里為123456
通過這個操作,,我們能夠獲得所需的密鑰庫文件zlex.keystore,。
這時,我們已經(jīng)獲得了密鑰庫文件,,只要確定對應的別名信息,,就可以提取公鑰/私鑰,以及數(shù)字證書,,進行加密交互了,!
- echo 查看證書
- keytool -list -keystore zlex.keystore -storepass 123456 -v
echo 查看證書 keytool -list -keystore zlex.keystore -storepass 123456 -v
-list列舉密鑰庫
-keystore密鑰庫,這里是zlex.keystore
-storepass密鑰庫密碼,,這里是123456
-v顯示詳情
這里需要細致觀察一下別名信息?。?!就是紅框中的數(shù)字1?。?!
經(jīng)過我多次嘗試,,將PKCS#12格式的密鑰庫轉(zhuǎn)化為JKS格式時,其別名一律為“1”!也就是說,,我們完全可以不進行密鑰庫轉(zhuǎn)換,,也能獲得公鑰/私鑰。
現(xiàn)在,,我們把證書導出,!
- echo 導出證書
- keytool -exportcert -alias 1 -keystore zlex.keystore -file zlex.crt -storepass 123456
echo 導出證書 keytool -exportcert -alias 1 -keystore zlex.keystore -file zlex.crt -storepass 123456
-exportcert導出證書
-alias別名,這里是1
-keystore密鑰庫,,這里是zlex.keystore
-file證書文件,,這里是zlex.crt
-storepass密鑰庫密碼,這里是123456
現(xiàn)在證書也導出了,,我們可以提取公鑰/私鑰,,進行加密/解密,簽名/驗證操作了,!當然,,即便沒有證書,,我們也能夠通過密鑰庫(JKS格式)文件獲得證書,以及公鑰/私鑰,、簽名算法等,。
補充代碼, 其實就是對Java加密技術(shù)(八)的修改,!
- /**
- * 2010-8-11
- */
- package org.zlex.pfx;
- import java.io.FileInputStream;
- import java.security.KeyStore;
- import java.security.PrivateKey;
- import java.security.PublicKey;
- import java.security.Signature;
- import java.security.cert.Certificate;
- import java.security.cert.CertificateFactory;
- import java.security.cert.X509Certificate;
- import java.util.Date;
- import javax.crypto.Cipher;
- /**
- * 證書操作類
- *
- * @author <a href="mailto:[email protected]">梁棟</a>
- * @since 1.0
- */
- public class CertificateCoder {
- /**
- * Java密鑰庫(Java Key Store,,JKS)KEY_STORE
- */
- public static final String KEY_STORE = "JKS";
- public static final String X509 = "X.509";
- /**
- * 由 KeyStore獲得私鑰
- *
- * @param keyStorePath
- * @param alias
- * @param password
- * @return
- * @throws Exception
- */
- private static PrivateKey getPrivateKey(String keyStorePath, String alias,
- String password) throws Exception {
- KeyStore ks = getKeyStore(keyStorePath, password);
- PrivateKey key = (PrivateKey) ks.getKey(alias, password.toCharArray());
- return key;
- }
- /**
- * 由 Certificate獲得公鑰
- *
- * @param certificatePath
- * @return
- * @throws Exception
- */
- private static PublicKey getPublicKey(String certificatePath)
- throws Exception {
- Certificate certificate = getCertificate(certificatePath);
- PublicKey key = certificate.getPublicKey();
- return key;
- }
- /**
- * 獲得Certificate
- *
- * @param certificatePath
- * @return
- * @throws Exception
- */
- private static Certificate getCertificate(String certificatePath)
- throws Exception {
- CertificateFactory certificateFactory = CertificateFactory
- .getInstance(X509);
- FileInputStream in = new FileInputStream(certificatePath);
- Certificate certificate = certificateFactory.generateCertificate(in);
- in.close();
- return certificate;
- }
- /**
- * 獲得Certificate
- *
- * @param keyStorePath
- * @param alias
- * @param password
- * @return
- * @throws Exception
- */
- private static Certificate getCertificate(String keyStorePath,
- String alias, String password) throws Exception {
- KeyStore ks = getKeyStore(keyStorePath, password);
- Certificate certificate = ks.getCertificate(alias);
- return certificate;
- }
- /**
- * 獲得KeyStore
- *
- * @param keyStorePath
- * @param password
- * @return
- * @throws Exception
- */
- private static KeyStore getKeyStore(String keyStorePath, String password)
- throws Exception {
- FileInputStream is = new FileInputStream(keyStorePath);
- KeyStore ks = KeyStore.getInstance(KEY_STORE);
- ks.load(is, password.toCharArray());
- is.close();
- return ks;
- }
- /**
- * 私鑰加密
- *
- * @param data
- * @param keyStorePath
- * @param alias
- * @param password
- * @return
- * @throws Exception
- */
- public static byte[] encryptByPrivateKey(byte[] data, String keyStorePath,
- String alias, String password) throws Exception {
- // 取得私鑰
- PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);
- // 對數(shù)據(jù)加密
- Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
- cipher.init(Cipher.ENCRYPT_MODE, privateKey);
- return cipher.doFinal(data);
- }
- /**
- * 私鑰解密
- *
- * @param data
- * @param keyStorePath
- * @param alias
- * @param password
- * @return
- * @throws Exception
- */
- public static byte[] decryptByPrivateKey(byte[] data, String keyStorePath,
- String alias, String password) throws Exception {
- // 取得私鑰
- PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);
- // 對數(shù)據(jù)加密
- Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
- cipher.init(Cipher.DECRYPT_MODE, privateKey);
- return cipher.doFinal(data);
- }
- /**
- * 公鑰加密
- *
- * @param data
- * @param certificatePath
- * @return
- * @throws Exception
- */
- public static byte[] encryptByPublicKey(byte[] data, String certificatePath)
- throws Exception {
- // 取得公鑰
- PublicKey publicKey = getPublicKey(certificatePath);
- // 對數(shù)據(jù)加密
- Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
- cipher.init(Cipher.ENCRYPT_MODE, publicKey);
- return cipher.doFinal(data);
- }
- /**
- * 公鑰解密
- *
- * @param data
- * @param certificatePath
- * @return
- * @throws Exception
- */
- public static byte[] decryptByPublicKey(byte[] data, String certificatePath)
- throws Exception {
- // 取得公鑰
- PublicKey publicKey = getPublicKey(certificatePath);
- // 對數(shù)據(jù)加密
- Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
- cipher.init(Cipher.DECRYPT_MODE, publicKey);
- return cipher.doFinal(data);
- }
- /**
- * 驗證Certificate
- *
- * @param certificatePath
- * @return
- */
- public static boolean verifyCertificate(String certificatePath) {
- return verifyCertificate(new Date(), certificatePath);
- }
- /**
- * 驗證Certificate是否過期或無效
- *
- * @param date
- * @param certificatePath
- * @return
- */
- public static boolean verifyCertificate(Date date, String certificatePath) {
- boolean status = true;
- try {
- // 取得證書
- Certificate certificate = getCertificate(certificatePath);
- // 驗證證書是否過期或無效
- status = verifyCertificate(date, certificate);
- } catch (Exception e) {
- status = false;
- }
- return status;
- }
- /**
- * 驗證證書是否過期或無效
- *
- * @param date
- * @param certificate
- * @return
- */
- private static boolean verifyCertificate(Date date, Certificate certificate) {
- boolean status = true;
- try {
- X509Certificate x509Certificate = (X509Certificate) certificate;
- x509Certificate.checkValidity(date);
- } catch (Exception e) {
- status = false;
- }
- return status;
- }
- /**
- * 簽名
- *
- * @param keyStorePath
- * @param alias
- * @param password
- *
- * @return
- * @throws Exception
- */
- public static byte[] sign(byte[] sign, String keyStorePath, String alias,
- String password) throws Exception {
- // 獲得證書
- X509Certificate x509Certificate = (X509Certificate) getCertificate(
- keyStorePath, alias, password);
- // 獲取私鑰
- KeyStore ks = getKeyStore(keyStorePath, password);
- // 取得私鑰
- PrivateKey privateKey = (PrivateKey) ks.getKey(alias, password
- .toCharArray());
- // 構(gòu)建簽名
- Signature signature = Signature.getInstance(x509Certificate
- .getSigAlgName());
- signature.initSign(privateKey);
- signature.update(sign);
- return signature.sign();
- }
- /**
- * 驗證簽名
- *
- * @param data
- * @param sign
- * @param certificatePath
- * @return
- * @throws Exception
- */
- public static boolean verify(byte[] data, byte[] sign,
- String certificatePath) throws Exception {
- // 獲得證書
- X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath);
- // 獲得公鑰
- PublicKey publicKey = x509Certificate.getPublicKey();
- // 構(gòu)建簽名
- Signature signature = Signature.getInstance(x509Certificate
- .getSigAlgName());
- signature.initVerify(publicKey);
- signature.update(data);
- return signature.verify(sign);
- }
- /**
- * 驗證Certificate
- *
- * @param keyStorePath
- * @param alias
- * @param password
- * @return
- */
- public static boolean verifyCertificate(Date date, String keyStorePath,
- String alias, String password) {
- boolean status = true;
- try {
- Certificate certificate = getCertificate(keyStorePath, alias,
- password);
- status = verifyCertificate(date, certificate);
- } catch (Exception e) {
- status = false;
- }
- return status;
- }
- /**
- * 驗證Certificate
- *
- * @param keyStorePath
- * @param alias
- * @param password
- * @return
- */
- public static boolean verifyCertificate(String keyStorePath, String alias,
- String password) {
- return verifyCertificate(new Date(), keyStorePath, alias, password);
- }
- }
/** * 2010-8-11 */ package org.zlex.pfx; import java.io.FileInputStream; import java.security.KeyStore; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.Date; import javax.crypto.Cipher; /** * 證書操作類 * * @author <a href="mailto:[email protected]">梁棟</a> * @since 1.0 */ public class CertificateCoder { /** * Java密鑰庫(Java Key Store,JKS)KEY_STORE */ public static final String KEY_STORE = "JKS"; public static final String X509 = "X.509"; /** * 由 KeyStore獲得私鑰 * * @param keyStorePath * @param alias * @param password * @return * @throws Exception */ private static PrivateKey getPrivateKey(String keyStorePath, String alias, String password) throws Exception { KeyStore ks = getKeyStore(keyStorePath, password); PrivateKey key = (PrivateKey) ks.getKey(alias, password.toCharArray()); return key; } /** * 由 Certificate獲得公鑰 * * @param certificatePath * @return * @throws Exception */ private static PublicKey getPublicKey(String certificatePath) throws Exception { Certificate certificate = getCertificate(certificatePath); PublicKey key = certificate.getPublicKey(); return key; } /** * 獲得Certificate * * @param certificatePath * @return * @throws Exception */ private static Certificate getCertificate(String certificatePath) throws Exception { CertificateFactory certificateFactory = CertificateFactory .getInstance(X509); FileInputStream in = new FileInputStream(certificatePath); Certificate certificate = certificateFactory.generateCertificate(in); in.close(); return certificate; } /** * 獲得Certificate * * @param keyStorePath * @param alias * @param password * @return * @throws Exception */ private static Certificate getCertificate(String keyStorePath, String alias, String password) throws Exception { KeyStore ks = getKeyStore(keyStorePath, password); Certificate certificate = ks.getCertificate(alias); return certificate; } /** * 獲得KeyStore * * @param keyStorePath * @param password * @return * @throws Exception */ private static KeyStore getKeyStore(String keyStorePath, String password) throws Exception { FileInputStream is = new FileInputStream(keyStorePath); KeyStore ks = KeyStore.getInstance(KEY_STORE); ks.load(is, password.toCharArray()); is.close(); return ks; } /** * 私鑰加密 * * @param data * @param keyStorePath * @param alias * @param password * @return * @throws Exception */ public static byte[] encryptByPrivateKey(byte[] data, String keyStorePath, String alias, String password) throws Exception { // 取得私鑰 PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password); // 對數(shù)據(jù)加密 Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, privateKey); return cipher.doFinal(data); } /** * 私鑰解密 * * @param data * @param keyStorePath * @param alias * @param password * @return * @throws Exception */ public static byte[] decryptByPrivateKey(byte[] data, String keyStorePath, String alias, String password) throws Exception { // 取得私鑰 PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password); // 對數(shù)據(jù)加密 Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm()); cipher.init(Cipher.DECRYPT_MODE, privateKey); return cipher.doFinal(data); } /** * 公鑰加密 * * @param data * @param certificatePath * @return * @throws Exception */ public static byte[] encryptByPublicKey(byte[] data, String certificatePath) throws Exception { // 取得公鑰 PublicKey publicKey = getPublicKey(certificatePath); // 對數(shù)據(jù)加密 Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, publicKey); return cipher.doFinal(data); } /** * 公鑰解密 * * @param data * @param certificatePath * @return * @throws Exception */ public static byte[] decryptByPublicKey(byte[] data, String certificatePath) throws Exception { // 取得公鑰 PublicKey publicKey = getPublicKey(certificatePath); // 對數(shù)據(jù)加密 Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm()); cipher.init(Cipher.DECRYPT_MODE, publicKey); return cipher.doFinal(data); } /** * 驗證Certificate * * @param certificatePath * @return */ public static boolean verifyCertificate(String certificatePath) { return verifyCertificate(new Date(), certificatePath); } /** * 驗證Certificate是否過期或無效 * * @param date * @param certificatePath * @return */ public static boolean verifyCertificate(Date date, String certificatePath) { boolean status = true; try { // 取得證書 Certificate certificate = getCertificate(certificatePath); // 驗證證書是否過期或無效 status = verifyCertificate(date, certificate); } catch (Exception e) { status = false; } return status; } /** * 驗證證書是否過期或無效 * * @param date * @param certificate * @return */ private static boolean verifyCertificate(Date date, Certificate certificate) { boolean status = true; try { X509Certificate x509Certificate = (X509Certificate) certificate; x509Certificate.checkValidity(date); } catch (Exception e) { status = false; } return status; } /** * 簽名 * * @param keyStorePath * @param alias * @param password * * @return * @throws Exception */ public static byte[] sign(byte[] sign, String keyStorePath, String alias, String password) throws Exception { // 獲得證書 X509Certificate x509Certificate = (X509Certificate) getCertificate( keyStorePath, alias, password); // 獲取私鑰 KeyStore ks = getKeyStore(keyStorePath, password); // 取得私鑰 PrivateKey privateKey = (PrivateKey) ks.getKey(alias, password .toCharArray()); // 構(gòu)建簽名 Signature signature = Signature.getInstance(x509Certificate .getSigAlgName()); signature.initSign(privateKey); signature.update(sign); return signature.sign(); } /** * 驗證簽名 * * @param data * @param sign * @param certificatePath * @return * @throws Exception */ public static boolean verify(byte[] data, byte[] sign, String certificatePath) throws Exception { // 獲得證書 X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath); // 獲得公鑰 PublicKey publicKey = x509Certificate.getPublicKey(); // 構(gòu)建簽名 Signature signature = Signature.getInstance(x509Certificate .getSigAlgName()); signature.initVerify(publicKey); signature.update(data); return signature.verify(sign); } /** * 驗證Certificate * * @param keyStorePath * @param alias * @param password * @return */ public static boolean verifyCertificate(Date date, String keyStorePath, String alias, String password) { boolean status = true; try { Certificate certificate = getCertificate(keyStorePath, alias, password); status = verifyCertificate(date, certificate); } catch (Exception e) { status = false; } return status; } /** * 驗證Certificate * * @param keyStorePath * @param alias * @param password * @return */ public static boolean verifyCertificate(String keyStorePath, String alias, String password) { return verifyCertificate(new Date(), keyStorePath, alias, password); } }
相信上述代碼已經(jīng)幫朋友們解決了相當多的問題,!
給出測試類:
- package org.zlex.pfx;
- import static org.junit.Assert.*;
- import org.apache.commons.codec.binary.Hex;
- import org.junit.Test;
- /**
- * 證書操作驗證類
- *
- * @author <a href="mailto:[email protected]">梁棟</a>
- * @version 1.0
- * @since 1.0
- */
- public class CertificateCoderTest {
- private String password = "123456";
- private String alias = "1";
- private String certificatePath = "zlex.crt";
- private String keyStorePath = "zlex.keystore";
- @Test
- public void test() throws Exception {
- System.err.println("公鑰加密——私鑰解密");
- String inputStr = "Ceritifcate";
- byte[] data = inputStr.getBytes();
- byte[] encrypt = CertificateCoder.encryptByPublicKey(data,
- certificatePath);
- byte[] decrypt = CertificateCoder.decryptByPrivateKey(encrypt,
- keyStorePath, alias, password);
- String outputStr = new String(decrypt);
- System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);
- // 驗證數(shù)據(jù)一致
- assertArrayEquals(data, decrypt);
- // 驗證證書有效
- assertTrue(CertificateCoder.verifyCertificate(certificatePath));
- }
- @Test
- public void testSign() throws Exception {
- System.err.println("私鑰加密——公鑰解密");
- String inputStr = "sign";
- byte[] data = inputStr.getBytes();
- byte[] encodedData = CertificateCoder.encryptByPrivateKey(data,
- keyStorePath, alias, password);
- byte[] decodedData = CertificateCoder.decryptByPublicKey(encodedData,
- certificatePath);
- String outputStr = new String(decodedData);
- System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);
- assertEquals(inputStr, outputStr);
- System.err.println("私鑰簽名——公鑰驗證簽名");
- // 產(chǎn)生簽名
- byte[] sign = CertificateCoder.sign(encodedData, keyStorePath, alias,
- password);
- System.err.println("簽名:\r" + Hex.encodeHexString(sign));
- // 驗證簽名
- boolean status = CertificateCoder.verify(encodedData, sign,
- certificatePath);
- System.err.println("狀態(tài):\r" + status);
- assertTrue(status);
- }
- }
package org.zlex.pfx; import static org.junit.Assert.*; import org.apache.commons.codec.binary.Hex; import org.junit.Test; /** * 證書操作驗證類 * * @author <a href="mailto:[email protected]">梁棟</a> * @version 1.0 * @since 1.0 */ public class CertificateCoderTest { private String password = "123456"; private String alias = "1"; private String certificatePath = "zlex.crt"; private String keyStorePath = "zlex.keystore"; @Test public void test() throws Exception { System.err.println("公鑰加密——私鑰解密"); String inputStr = "Ceritifcate"; byte[] data = inputStr.getBytes(); byte[] encrypt = CertificateCoder.encryptByPublicKey(data, certificatePath); byte[] decrypt = CertificateCoder.decryptByPrivateKey(encrypt, keyStorePath, alias, password); String outputStr = new String(decrypt); System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr); // 驗證數(shù)據(jù)一致 assertArrayEquals(data, decrypt); // 驗證證書有效 assertTrue(CertificateCoder.verifyCertificate(certificatePath)); } @Test public void testSign() throws Exception { System.err.println("私鑰加密——公鑰解密"); String inputStr = "sign"; byte[] data = inputStr.getBytes(); byte[] encodedData = CertificateCoder.encryptByPrivateKey(data, keyStorePath, alias, password); byte[] decodedData = CertificateCoder.decryptByPublicKey(encodedData, certificatePath); String outputStr = new String(decodedData); System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr); assertEquals(inputStr, outputStr); System.err.println("私鑰簽名——公鑰驗證簽名"); // 產(chǎn)生簽名 byte[] sign = CertificateCoder.sign(encodedData, keyStorePath, alias, password); System.err.println("簽名:\r" + Hex.encodeHexString(sign)); // 驗證簽名 boolean status = CertificateCoder.verify(encodedData, sign, certificatePath); System.err.println("狀態(tài):\r" + status); assertTrue(status); } }
第一個測試方法,,用于提取公鑰/私鑰進行加密/解密操作。
第二個測試方法,,用于提取簽名算法進行簽名/驗證操作,。
OK,任務完成,,密鑰成功提取,,剩下的都是代碼基本功了!
cer證書文件本身就是算法以及公鑰的載體,而私鑰就需要私密保存,,不對外公布了,!