什麼是Rsa加密? RSA算法是最流行的公鑰密碼算法,使用長度可以變化的密鑰。RSA是第一個既能用於數據加密也能用於數字簽名的算法。
該如何使用呢? package com.wallet.util;
import java.math.BigInteger; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.Signature; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.RSAPrivateKeySpec; import java.security.spec.RSAPublicKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; import java.util.HashMap; import java.util.Map;
import javax.crypto.Cipher;
import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.binary.Hex; import org.slf4j.Logger; import org.slf4j.LoggerFactory;
/** * RSA 加解密 * * 參數在經過Http傳輸後會對部分特殊字符進行轉義, 所以在接收參數後需進行解碼 String value = URLDecoder.decode("param", "UTF-8"); * 參數在進行Http傳輸前需對參數進行編碼轉義操作, String value = URLEncoder.encode("param", "UTF-8"); * Java 1.8+ 原生支持 Base64, 1.7及以下需導包 安卓原生支持 Base64 */ public class RSA {
// 創建日誌記錄對象 private static final Logger LOGGER = LoggerFactory.getLogger(RSA.class);
// 聲明非對稱加密密鑰算法 public static final String RSA = "RSA"; // 聲明編碼集 public static final String CODE = "UTF-8"; // 祕鑰長度 public static final Integer KEY_SIZE = 2048; // 聲明簽名標準 public static final String SHA1WITHRSA = "SHA1WithRSA"; // 加密填充方式, 該屬性安卓有效, Java使用RSA加密後, 安卓使用該屬性解密, 因安卓和 Java RSA 協議不同 public static final String ECB_PKCS1_PADDING = "RSA/ECB/PKCS1Padding";
// 聲明祕鑰工廠對象 private static KeyFactory keyFactory = null; // 聲明公鑰對象 private static RSAPublicKey publicKey = null; // 聲明私鑰對象 private static RSAPrivateKey privateKey = null;
/** * 靜態代碼塊, 初始化祕鑰工廠對象 */ static { try { // 創建祕鑰工廠 keyFactory = KeyFactory.getInstance(RSA); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } }
/** * 使用公鑰字符獲取公鑰對象 * * @param publicKeyString 公鑰字符串 * @return 公鑰對象 */ public static RSAPublicKey getPublicKey(String publicKeyString) { // 把公鑰字符串數據加載到祕鑰對象中 X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyString)); try { // 獲取公鑰對象 publicKey = (RSAPublicKey) keyFactory.generatePublic(x509EncodedKeySpec); } catch (InvalidKeySpecException e) { e.printStackTrace(); } // 將結果返回 return publicKey; }
/** * 使用私鑰字符獲取私鑰對象 * * @param publicKeyString 私鑰字符串 * @return 私鑰對象 */ public static RSAPrivateKey getPrivateKey(String privateKeyString) { // 把私鑰字符串數據加載到祕鑰對象中 PKCS8EncodedKeySpec pKCS8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyString)); try { // 獲取私鑰對象 privateKey = (RSAPrivateKey) keyFactory.generatePrivate(pKCS8EncodedKeySpec); } catch (InvalidKeySpecException e) { e.printStackTrace(); } // 將結果返回 return privateKey; }
/** * 隨機生成密鑰對 * */ public static Map<String, String> genKeyPair() { // 創建返回值類型 Map<String, String> map = new HashMap<String, String>(); try { // KeyPairGenerator類用於生成公鑰和私鑰對,基於RSA算法生成對象 KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(RSA); // 初始化密鑰對生成器,設置祕鑰長度 keyPairGen.initialize(KEY_SIZE, new SecureRandom()); // 生成一個密鑰對,保存在keyPair中 KeyPair keyPair = keyPairGen.generateKeyPair();
// 得到公鑰 publicKey = (RSAPublicKey) keyPair.getPublic(); // 將公鑰轉換爲 String 類型 String pubKey = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());
// 得到私鑰 privateKey = (RSAPrivateKey) keyPair.getPrivate(); // 將私鑰轉換爲 String 類型 String priKey = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());
// 添加公鑰信息 map.put("publicKey", pubKey); // 添加私鑰信息 map.put("privateKey", priKey); } catch (Exception e) { e.printStackTrace(); } // 將結果返回 return map; }
/** * 公鑰加密 適用於明文過長 * * @param publicKeyString 公鑰 * @param plainTextData 明文數據 * @return 密文 * @throws Exception 加密過程中的異常信息 * */ public static String publicEncrypts(String publicKeyString, String plainTextData) throws Exception { // 聲明變量接收密文 String pubKey = ""; // 聲明起始下邊 Integer startIndex = 0; // 獲取明文長度 Integer strLength = plainTextData.length(); // 獲取單次可加密長度 Integer size = publicKeyString.length() / 8 - 11; // 遍歷參數 條件爲起始下邊小於明文長度 while(startIndex < plainTextData.length()) { // 獲取當前循環結束下標 Integer endIndex = (startIndex + size) > strLength ? strLength : startIndex + size; // 執行加密操作, 累加密文 pubKey += publicEncrypt(publicKeyString, plainTextData.substring(startIndex, endIndex)); // 起始位置增長 startIndex += size; } // 將結果返回 return pubKey; }
/** * 公鑰加密過程, 明文長度小於 (公鑰長度 / 8) - 11 * * @param publicKeyString 公鑰 * @param plainTextData 明文數據 * @return 密文 * @throws Exception 加密過程中的異常信息 * */ public static String publicEncrypt(String publicKeyString, String plainTextData) throws Exception { try { // 創建加解密對象, 安卓需使用 ECB_PKCS1_PADDING 不能使用 RSA Cipher cipher = Cipher.getInstance(RSA); // 使用公鑰字符串獲取公鑰對象 publicKey = getPublicKey(publicKeyString); // 初始化數據 cipher.init(Cipher.ENCRYPT_MODE, publicKey); // 執行加密操作獲取結果 byte[] output = cipher.doFinal(plainTextData.getBytes(CODE)); // 將加密後的結果轉換爲字符串 並返回 return Base64.getEncoder().encodeToString(output); } catch (Exception e) { e.printStackTrace(); throw e; } }
/** * 私鑰加密 適用於明文過長 * * @param privateKeyString 私鑰 * @param plainTextData 明文數據 * @return 密文 * @throws Exception 加密過程中的異常信息 * */ public static String privateEncrypts(String privateKeyString, String plainTextData) throws Exception { // 聲明變量接收密文 String priKey = ""; // 聲明起始下邊 Integer startIndex = 0; // 獲取明文長度 Integer strLength = plainTextData.length(); // 獲取單次可加密長度 Integer size = privateKeyString.length() / 8 - 11; // 遍歷參數 條件爲起始下邊小於明文長度 while(startIndex < plainTextData.length()) { // 獲取當前結束下標 Integer endIndex = (startIndex + size) > strLength ? strLength : startIndex + size; // 執行加密操作, 累加密文 priKey += privateEncrypt(privateKeyString, plainTextData.substring(startIndex, endIndex)); // 起始位置增長 startIndex += size; } // 將結果返回 return priKey; }
/** * 私鑰加密過程, 明文長度小於 (私鑰長度 / 8) - 11 * * @param privateKeyString 私鑰 * @param plainTextData 明文數據 * @return 密文 * @throws Exception 加密過程中的異常信息 * */ public static String privateEncrypt(String privateKeyString, String plainTextData) throws Exception { try { // 創建加解密對象, 安卓需使用 ECB_PKCS1_PADDING 不能使用 RSA Cipher cipher = Cipher.getInstance(RSA); // 使用私鑰字符串獲取私鑰對象 privateKey = getPrivateKey(privateKeyString); // 初始化數據 cipher.init(Cipher.ENCRYPT_MODE, privateKey); // 執行加密操作 byte[] output = cipher.doFinal(plainTextData.getBytes(CODE)); // 將加密後的結果轉換爲字符串 並返回 return Base64.getEncoder().encodeToString(output); } catch (Exception e) { e.printStackTrace(); throw e; } }
/** * 私鑰解密 適用於密文過長 * * @param privateKeyString 私鑰 * @param plainTextData 密文數據 * @return 明文 * @throws Exception 解密過程中的異常信息 * */ public static String privateDecrypts(String privateKeyString, String plainTextData) throws Exception { // 聲明變量接收明文 String plaintext = ""; // 聲明起始下邊 Integer startIndex = 0; // 獲取密文長度 Integer strLength = plainTextData.length(); // 遍歷參數 條件爲起始下邊小於密文長度 while(startIndex < plainTextData.length()) { // 獲取當前結束下標 Integer endIndex = (startIndex + 344) > strLength ? strLength : startIndex + 344; // 執行解密操作, 累加明文 plaintext += privateDecrypt(privateKeyString, plainTextData.substring(startIndex, endIndex)); // 起始位置增長 startIndex += 344; } // 將結果返回 return plaintext; }
/** * 私鑰解密過程 * * @param privateKey 私鑰 * @param cipherData 密文數據 * @return 明文 * @throws Exception 解密過程中的異常信息 * */ public static String privateDecrypt(String privateKeyString, String sign) throws Exception { try { // 創建加解密對象, 安卓需使用 ECB_PKCS1_PADDING 不能使用 RSA Cipher cipher = Cipher.getInstance(RSA); // 使用私鑰字符串獲取私鑰對象 privateKey = getPrivateKey(privateKeyString); // 初始化數據 cipher.init(Cipher.DECRYPT_MODE, privateKey); // 執行解密操作 byte[] output = cipher.doFinal(Base64.getDecoder().decode(sign)); // 轉換解密後結果編碼集 並返回 return new String(output, CODE); } catch (Exception e) { e.printStackTrace(); throw e; } }
/** * 公鑰解密 適用於密文過長 * * @param publicKeyString 公鑰 * @param plainTextData 密文數據 * @return 明文 * @throws Exception 解密過程中的異常信息 * */ public static String publicDecrypts(String publicKeyString, String sign) throws Exception { // 聲明變量接收明文 String plaintext = ""; // 聲明起始下邊 Integer startIndex = 0; // 獲取密文長度 Integer strLength = sign.length(); // 遍歷參數 條件爲起始下邊小於密文長度 while(startIndex < sign.length()) { // 獲取當前結束下標 Integer endIndex = (startIndex + 344) > strLength ? strLength : startIndex + 344; // 執行解密操作, 累加明文 plaintext += publicDecrypt(publicKeyString, sign.substring(startIndex, endIndex)); // 起始位置增長 startIndex += 344; } // 將結果返回 return plaintext; }
/** * 公鑰解密過程 * * @param publicKeyString 公鑰字符串 * @param cipherData 密文數據 * @return 明文 * @throws Exception 解密過程中的異常信息 * */ public static String publicDecrypt(String publicKeyString, String sign) throws Exception { try { // 創建加解密對象, 安卓需使用 ECB_PKCS1_PADDING 不能使用 RSA Cipher cipher = Cipher.getInstance(RSA); // 使用公鑰字符串獲取公鑰對象 publicKey = getPublicKey(publicKeyString); // 初始化數據 cipher.init(Cipher.DECRYPT_MODE, publicKey); // 執行解密操作 byte[] output = cipher.doFinal(Base64.getDecoder().decode(sign)); // 轉換解密後結果編碼集 並返回 return new String(output, CODE); } catch (Exception e) { e.printStackTrace(); throw e; } }
/** * RSA簽名 * * @param content 待簽名數據 * @return 簽名值 * @throws Exception 簽名過程中出現的異常 * */ public static String sign(String privateKeyString, String content) throws Exception { try { // 創建簽名對象 Signature signature = Signature.getInstance(SHA1WITHRSA); // 使用私鑰字符串獲取私鑰對象 privateKey = getPrivateKey(privateKeyString); // 初始化此對象的簽名 signature.initSign(privateKey); // 更新要簽名或驗證的數據 signature.update(content.getBytes(CODE)); return Base64.getEncoder().encodeToString(signature.sign()); } catch (Exception e) { e.printStackTrace(); throw e; } }
/** * RSA驗簽名檢查 * * @param content 待簽名數據 * @param sign 簽名值 * @return 布爾值 * @throws Exception 驗簽過程中出現的異常 * */ public static boolean verify(String publicKeyString, String content, String sign) throws Exception { try { // 創建簽名對象 Signature signature = Signature.getInstance(SHA1WITHRSA); // 使用公鑰字符串獲取公鑰對象 publicKey = getPublicKey(publicKeyString); // 初始化此對象的簽名 signature.initVerify(publicKey); // 更新要簽名或驗證的數據 signature.update(content.getBytes( )); // 校驗簽名結果 return signature.verify(Base64.getDecoder().decode(sign)); } catch (Exception e) { e.printStackTrace(); throw e; } }
/** * 根據給定的係數和專用指數構造一個RSA專用的公鑰對象。 * * @param modulus 係數。 * @param publicExponent 專用指數。 * @return RSA專用公鑰對象。 */ public static RSAPublicKey generateRSAPublicKey(byte[] modulus, byte[] publicExponent) { // 構造 RSAPublicKeySpec(RSA公鑰規範) 對象 RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(new BigInteger(modulus), new BigInteger(publicExponent)); try { // 獲取公鑰對象並返回 return (RSAPublicKey) keyFactory.generatePublic(publicKeySpec); } catch (InvalidKeySpecException ex) { LOGGER.error("RSAPublicKeySpec is unavailable.", ex); } catch (NullPointerException ex) { LOGGER.error("RSAUtils#KEY_FACTORY is null, can not generate KeyFactory instance.", ex); } // 構造失敗 return null; }
/** * 根據給定的係數和專用指數構造一個RSA專用的私鑰對象。 * * @param modulus 係數。 * @param privateExponent 專用指數。 * @return RSA專用私鑰對象。 */ public static RSAPrivateKey generateRSAPrivateKey(byte[] modulus, byte[] privateExponent) { // 構造 RSAPrivateKeySpec(RSA私鑰規範) 對象 RSAPrivateKeySpec privateKeySpec = new RSAPrivateKeySpec(new BigInteger(modulus), new BigInteger(privateExponent)); try { // 獲取私鑰對象並返回 return (RSAPrivateKey) keyFactory.generatePrivate(privateKeySpec); } catch (InvalidKeySpecException ex) { LOGGER.error("RSAPrivateKeySpec is unavailable.", ex); } catch (NullPointerException ex) { LOGGER.error("RSAUtils#KEY_FACTORY is null, can not generate KeyFactory instance.", ex); } // 構造失敗 return null; }
/** * 根據給定的16進制係數和專用指數字符串構造一個RSA專用的私鑰對象。 * * @param modulus 係數。 * @param privateExponent 專用指數。 * @return RSA專用私鑰對象。 */ public static RSAPrivateKey getRSAPrivateKey(String hexModulus, String hexPrivateExponent) { // 非空校驗 if(hexModulus.isEmpty() || hexPrivateExponent.isEmpty()) { // 判斷是否開啓 debug 級別日誌記錄 if(LOGGER.isDebugEnabled()) { // 打印日誌 LOGGER.debug("hexModulus and hexPrivateExponent cannot be empty. RSAPrivateKey value is null to return."); } // 終止方法 return null; } // 聲明變量 byte[] modulus = null; byte[] privateExponent = null; try { // 解碼, 調用 org.apache.commons.codec.binary.Hex.decodeHex()方法進行解碼 // 編碼, 調用 org.apache.commons.codec.binary.StringUtils.newStringUtf16()等方法進行編碼 modulus = Hex.decodeHex(hexModulus.toCharArray()); privateExponent = Hex.decodeHex(hexPrivateExponent.toCharArray()); } catch(DecoderException ex) { // 編碼轉換錯誤 LOGGER.error("hexModulus or hexPrivateExponent value is invalid. return null(RSAPrivateKey)."); } // 非空校驗 if(modulus != null && privateExponent != null) { // 調用 根據給定的係數和專用指數構造一個RSA專用的私鑰對象 方法獲取私鑰對象 return generateRSAPrivateKey(modulus, privateExponent); } // 構造失敗 return null; }
/** * 根據給定的16進制係數和專用指數字符串構造一個RSA專用的公鑰對象。 * * @param modulus 係數。 * @param publicExponent 專用指數。 * @return RSA專用公鑰對象。 */ public static RSAPublicKey getRSAPublidKey(String hexModulus, String hexPublicExponent) { // 非空校驗 if(hexModulus.isEmpty() || hexPublicExponent.isEmpty()) { // 判斷是否開啓 debug 級別日誌記錄 if(LOGGER.isDebugEnabled()) { // 打印日誌 LOGGER.debug("hexModulus and hexPublicExponent cannot be empty. return null(RSAPublicKey)."); } // 終止方法 return null; } // 聲明變量 byte[] modulus = null; byte[] publicExponent = null; try { // 解碼, 調用 org.apache.commons.codec.binary.Hex.decodeHex()方法進行解碼 // 編碼, 調用 org.apache.commons.codec.binary.StringUtils.newStringUtf16()等方法進行編碼 modulus = Hex.decodeHex(hexModulus.toCharArray()); publicExponent = Hex.decodeHex(hexPublicExponent.toCharArray()); } catch(DecoderException ex) { // 編碼轉換錯誤 LOGGER.error("hexModulus or hexPublicExponent value is invalid. return null(RSAPublicKey)."); } if(modulus != null && publicExponent != null) { // 調用 根據給定的係數和專用指數構造一個RSA專用的公鑰對象 方法獲取私鑰對象 return generateRSAPublicKey(modulus, publicExponent); } // 構造失敗 return null; }
} 關於加密填充方式:之前以爲上面這些操作就能實現rsa加解密,以爲萬事大吉了,呵呵,這事還沒完,悲劇還是發生了,Android這邊加密過的數據,服務器端死活解密不了,原來android系統的RSA實現是"RSA/None/NoPadding",而標準JDK實現是"RSA/None/PKCS1Padding" ,這造成了在android機上加密後無法在服務器上解密的原因,所以在實現的時候這個一定要注意。 實現分段加密:搞定了填充方式之後又自信的認爲萬事大吉了,可是意外還是發生了,RSA非對稱加密內容長度有限制,1024位key的最多隻能加密127位數據,否則就會報錯(javax.crypto.IllegalBlockSizeException: Data must not be longer than 117 bytes) , RSA 是常用的非對稱加密算法。最近使用時卻出現了“不正確的長度”的異常,研究發現是由於待加密的數據超長所致。RSA 算法規定:待加密的字節數不能超過密鑰的長度值除以 8 再減去 11(即:KeySize / 8 - 11),而加密後得到密文的字節數,正好是密鑰的長度值除以 8(即:KeySize / 8)。 |
Java RSA 加密
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.