RSA加密算法是一種非對稱的加密算法,主要包括以下知識點:
1、密鑰對的獲取:主要涉及到兩個參數:秘鑰格式、秘鑰長度
(1)常見秘鑰格式:PKCS8(java適用)、PKCS1(其他),如果不是java平台生成的秘鑰,有可能報以下錯誤:
java.security.InvalidKeyException: invalid key format //秘鑰格式不合法
(2)秘鑰長度:1024、2048(越長越安全,效率越低),詳細介紹可以參考 :https://blog.csdn.net/luoluo_onion/article/details/78354799
注意:分段加解密時:1024的秘鑰每次加密最大長度為117(128-11),長度為2048秘鑰每次加解密長度為256,如果加密時設置的長度不匹配,可能會報以下錯誤:
javax.crypto.BadPaddingException : Decryption error //解碼失敗
秘鑰獲取的代碼實現:java.security.KeyPairGenerator提供了生成秘鑰的API,只需要調用即可。
public static Map<String, Object> genKeyPair() throws Exception { KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM); keyPairGen.initialize(1024);//2048 KeyPair keyPair = keyPairGen.generateKeyPair(); RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); Map<String, Object> keyMap = new HashMap<String, Object>(2); keyMap.put(PUBLIC_KEY, publicKey); keyMap.put(PRIVATE_KEY, privateKey); return keyMap; }
public static String getPrivateKey(Map<String, Object> keyMap) throws Exception { Key key = (Key) keyMap.get(PRIVATE_KEY); return Base64.encodeBase64String(key.getEncoded()); }
3、加密實現(公鑰加密)
(1)獲取Cipher需要的java.security.Key類型公鑰;
(2)調用getInstance()方法獲取cipher實例,參數為填充方式(加密和解密的填充方式要統一,否則解密失敗!)
(3)使用公鑰初始化cipher實例,同時設置加解密類型
(4)對數據進行分段加密,得到字節數組。
private static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception { byte[] keyBytes = Base64.decodeBase64(publicKey); X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); Key publicK = keyFactory.generatePublic(x509KeySpec); // 對數據加密 Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); 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 > 256) { cache = cipher.doFinal(data, offSet, 256); } else { cache = cipher.doFinal(data, offSet, inputLen - offSet); } out.write(cache, 0, cache.length); i++; offSet = i * 256; } byte[] encryptedData = out.toByteArray(); out.close(); return encryptedData; }
4、解密實現
(1)獲取Cipher需要的java.security.Key類型私鑰;
(2)調用getInstance()方法獲取cipher實例,參數為填充方式(加密和解密的填充方式要統一,否則解密失敗!)
(3)使用私鑰初始化cipher實例,同時設置類型為解密類型
(4)分段解密,得到字節數組。
private static byte[] decryptByPrivateKey(byte[] data, String privateKey) throws Exception { // 得到私鑰 byte[] keyBytes = Base64.decodeBase64(privateKey.getBytes()); PKCS8EncodedKeySpec pKCS8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key key = keyFactory.generatePrivate(pKCS8EncodedKeySpec); // 解密數據 Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.DECRYPT_MODE, key); // SealedObject obj = new SealedObject(data, cipher); int inputLen = data.length; System.out.println(data); System.out.println(inputLen); 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(data, offSet, MAX_DECRYPT_BLOCK); } else { cache = cipher.doFinal(data, offSet, inputLen - offSet); } out.write(cache, 0, cache.length); i++; offSet = i * MAX_DECRYPT_BLOCK; } byte[] decryptedData = out.toByteArray(); out.close(); //byte[] messageBytes = (byte[]) obj.getObject(cipher); return decryptedData; }
5由於本人是首次接觸RSA,並且對加密算法一片空白,所以過程中幾乎把能遇見的錯誤碰了個遍,總結以下幾個常見錯誤,引以為戒:
(1)java.security.InvalidKeyException: invalid key format
秘鑰格式不合法,或者是秘鑰被修改了。
(2)java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : DerInputStream.getLength(): lengthTag=111, too big.
秘鑰字符串主體前后有空格,或者是換行,或者有多余字段,如(-----BEGIN RSA PRIVATE KEY-----)(秘鑰格式不正確)
(3)java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : algid parse error, not a sequence
原因可能是沒有使用 PKCS8(java適用)
(4)javax.crypto.BadPaddingException : Decryption error
解密時報這個錯誤,很可能是填充格式不匹配,請確認加密時使用的填充格式,解密時保持統一即可,我這里使用功能的是RSA/ECB/PKCS1Padding
(5)javax.crypto.BadPaddingException: Message is larger than modulus
解密時出現這個錯誤(這個錯誤我是羞於啟齒的),可能是因為要解密的字符串中混入了什么奇怪的東西,你可以在解密前打印下要解密的字符串,再打印下轉為base64后的字節數組長度(正常應該為128的倍數),否則應該是手殘多加了什么東西呢。
備注:以上內容是個人見解,疏漏之處,望各位不吝賜教!