一、使用keytool工具制作一個jks證書、並導出公鑰
進入D:\jkstest目錄下,使用命令生成merKey.jks證書文件
keytool -genkey -alias yunbo2 -keypass 123456 -keyalg RSA -keysize 1024 -validity 3650 -keystore merKey.jks -storepass abc@2018 -dname "CN=localhost,OU=localhost, O=localhost, L=深圳, ST=廣東, C=CN"
進入D:\jkstest目錄下,導出公鑰yunbo2.cer文件,輸入以下命令,回車,輸入秘鑰庫密碼 abc@2018
keytool -export -alias yunbo2 -keystore merKey.jks -file yunbo2.cer
生成證書文件如下
二、簡單代碼實現加解密、加簽、驗簽
/** * desc area * Created by yunBo on 2018/9/25 0025. */ public class CertificateUtils { public static final String KEY_STORE = "JKS"; public static final String X509 = "X.509"; private static final int CACHE_SIZE = 2048; private static final int MAX_ENCRYPT_BLOCK = 117; private static final int MAX_DECRYPT_BLOCK = 128; private static final String SHA1WithRSA = "SHA1WithRSA"; private static final String RSA = "RSA"; private static final String ECB = "ECB"; public static final Map<String, Object> signData = Maps.newConcurrentMap(); public static final String CRRECEPAY_SIGN_KEYSTORE="YUNBO_SIGN_KEYSTORE"; public static final String CRRECEPAY_SIGN_X509CERTIFICATE="YUNBO_SIGN_X509CERTIFICATE"; public static final String keyPass = "123456"; public static final String storePass = "abc@2018"; public static final String alias = "yunbo2"; public static final String jksFile = "D:/jkstest/merKey.jks"; public static final String pubFile = "D:/jkstest/yunbo2.cer"; public static void initX509Certificate(String cerFilePath) throws Exception { InputStream inputStream = null; try { CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); inputStream = new FileInputStream(cerFilePath); X509Certificate x509Certificate = (X509Certificate) certificateFactory.generateCertificate(inputStream); signData.put(CRRECEPAY_SIGN_X509CERTIFICATE, x509Certificate); } finally { IOUtils.closeQuietly(inputStream); } } /** * 獲取私鑰信息 * @param jksFilePath * @param keyAlias * @param keyPass * @param storePass * @return * @throws Exception */ public static PrivateKey getPrivateKey(String jksFilePath, String keyAlias, String keyPass, String storePass) throws Exception { File jksFile = new File(jksFilePath); InputStream in = new FileInputStream(jksFile); KeyStore keyStore = KeyStore.getInstance("JKS"); keyStore.load(in, storePass.toCharArray()); PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias, keyPass.toCharArray()); if (in != null) { IOUtils.closeQuietly(in); } return privateKey; } /** * 獲取公鑰信息 * @param cerFilePath * @return * @throws KeyStoreException * @throws IOException * @throws CertificateException * @throws NoSuchAlgorithmException */ public static PublicKey getPublicKey(String cerFilePath) throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException { PublicKey publicKey = null; try { X509Certificate x509Certificate = (X509Certificate)signData.get(CRRECEPAY_SIGN_X509CERTIFICATE); if (x509Certificate == null) { initX509Certificate(cerFilePath); x509Certificate = (X509Certificate)signData.get(CRRECEPAY_SIGN_X509CERTIFICATE); } publicKey = x509Certificate.getPublicKey(); } catch (Exception e) { e.printStackTrace(); } return publicKey; } /** * 加密 * @param requestStr * @return * @throws Exception */ public static byte[] encryptContentBytes(String requestStr) throws Exception { try { PublicKey publicKey = getPublicKey(pubFile); String pubKey = Base64.encodeBase64String(publicKey.getEncoded()); byte[] content = encryptByPublicKey(requestStr.getBytes(), pubKey); return content; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 公鑰加密 * @param data * @param publicKey * @return * @throws Exception */ public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception { byte[] keyBytes = org.apache.commons.codec.binary.Base64.decodeBase64(publicKey); X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(RSA); Key publicK = keyFactory.generatePublic(x509KeySpec); // 對數據加密 Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, publicK); int inputLen = data.length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offSet = 0; byte[] cache; int i = 0; // 對數據分段加密 while (inputLen - offSet > 0) { if (inputLen - offSet > MAX_ENCRYPT_BLOCK) { cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK); } else { cache = cipher.doFinal(data, offSet, inputLen - offSet); } out.write(cache, 0, cache.length); i++; offSet = i * MAX_ENCRYPT_BLOCK; } byte[] encryptedData = out.toByteArray(); out.close(); return encryptedData; } /** * 私鑰解密 * * @param encryptedData * @param privateKey * @return * @throws Exception */ public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey) throws Exception { byte[] keyBytes = Base64.decodeBase64(privateKey); PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(RSA); Key privateK = keyFactory.generatePrivate(pkcs8KeySpec); Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.DECRYPT_MODE, privateK); int inputLen = encryptedData.length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offSet = 0; byte[] cache; int i = 0; // 對數據分段解密 while (inputLen - offSet > 0) { if (inputLen - offSet > MAX_DECRYPT_BLOCK) { cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK); } else { cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet); } out.write(cache, 0, cache.length); i++; offSet = i * MAX_DECRYPT_BLOCK; } byte[] decryptedData = out.toByteArray(); out.close(); return decryptedData; } /** * 解密 * @param responseDataBytes * @return * @throws Exception */ public static String decryptContentBytes(byte[] responseDataBytes) throws Exception { try { PrivateKey privateKey = getPrivateKey(jksFile, alias, keyPass, storePass); String priKey = Base64.encodeBase64String(privateKey.getEncoded()); byte[] decryptContentBytes = decryptByPrivateKey(responseDataBytes, priKey); return new String(decryptContentBytes, CharEncoding.UTF_8); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 加簽 * @param signData * @return * @throws Exception */ public static String sign(String signData) throws Exception { InputStream in = new FileInputStream(new File(jksFile)); KeyStore keyStore = KeyStore.getInstance("JKS"); keyStore.load(in, storePass.toCharArray()); // 獲取jks證書別名 Enumeration en = keyStore.aliases(); String pName = null; while (en.hasMoreElements()) { String n = (String) en.nextElement(); if (keyStore.isKeyEntry(n)) { pName = n; } } PrivateKey key = getPrivateKey(jksFile,pName,keyPass,storePass); Signature signature = Signature.getInstance(SHA1WithRSA); signature.initSign(key); signature.update(signData.getBytes("UTF-8")); byte[] signedData = signature.sign(); String signDate = new BASE64Encoder().encode(signedData); signDate = signDate.replaceAll("\r\n", "").replaceAll("\n", ""); return signDate; } /** * 驗簽 * @return * @throws Exception */ public static boolean verifySign2(String originData,String returnSignData) throws Exception { PublicKey publicKey = getPublicKey(pubFile); Signature sign3 = Signature.getInstance(SHA1WithRSA); sign3.initVerify(publicKey); sign3.update(originData.getBytes("UTF-8")); boolean isVerifySign = sign3.verify(new BASE64Decoder().decodeBuffer(returnSignData)); return isVerifySign; } public static void main(String[] args) throws Exception{ String originData = "hello波波"; System.out.println("========> 加密開始"); byte[] enData = encryptContentBytes(originData); String signData = sign(originData); System.out.println("========> 加簽 signData:"+signData); String deData = decryptContentBytes(enData); System.out.println("========> 解密 deData:"+deData); boolean verifySign = verifySign2(originData,signData); System.out.println("========> 解密 verifySign:"+verifySign); } }
輸出結果為:
Connected to the target VM, address: '127.0.0.1:13223', transport: 'socket' ========> 加密開始 ========> 加簽 signData:kR4GMEQGPsgltoQfK+6IboCHsi+JbsjrrUTA6aW...2J0K2UZwqMJfWCarzxsKvUMFThRhwnuqoZ9u1qWlIko= ========> 解密 deData:hello波波 ========> 解密 verifySign:true Disconnected from the target VM, address: '127.0.0.1:13223', transport: 'socket' Process finished with exit code 0
三、總結
1、JKS文件是一個java中的密鑰管理庫,里面可以放各種密鑰文件,倉庫當然會有一把鎖,防范別人隨便亂拿,這個就是JKS文件的密 碼.里面存放的密鑰也各有不同,每個密鑰都有一個名字(在下面叫別名),一類就密鑰對,一類叫公鑰,一類叫私鑰,密鑰對就是包含公鑰和私鑰的。這里的公鑰 只要你能進入倉庫你就可以隨便查看拿走,私鑰則是有密碼的。只允許有權限的人查看拿走.對於讀取公鑰只需要 知道JKS文件(倉庫)的密碼就可以了,但是在讀取私鑰時則必須有私鑰的密碼。
2、在加載證書文件的時候,keyStore.load(in, storePass.toCharArray());storePass為獲取私鑰文件的密碼,即倉庫密碼,我們這里為abc@2018
3、證書加解密、加簽驗簽等功能在項目中非常常見,可以把證書獲取放在靜態對象Maps.newConcurrentMap()中,這樣效率顯著提升。