參考博客:https://blog.csdn.net/weixin_42068117/article/details/80084034
工作中開發人員用的是Java,但是寫mock用的是Python,所以Java的加密解密算法轉Python遇到了不少坑。下面以AES算法為例說明一下。
Java加密:
1 /** 2 * aes加密-128位 3 * 4 */ 5 public static String AesEncrypt(String content ,String key){ 6 if (StringUtils.isEmpty(key) || key.length() != 16) { 7 throw new RuntimeException("密鑰長度為16位"); 8 } 9 try { 10 String iv = key; 11 Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); 12 int blockSize = cipher.getBlockSize(); 13 byte[] dataBytes = content.getBytes("utf-8"); 14 int plaintextLength = dataBytes.length; 15 if (plaintextLength % blockSize != 0) { 16 plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize)); 17 } 18 byte[] plaintext = new byte[plaintextLength]; 19 System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length); 20 SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES"); 21 IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes()); 22 cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec); 23 byte[] encrypted = cipher.doFinal(plaintext); 24 return byte2Hex(encrypted); 25 26 } catch (Exception e) { 27 throw new RuntimeException("aes加密發生錯誤", e); 28 } 29 }
Java解密:
1 // ==Aes加解密================================================================== 2 /** 3 * aes解密-128位 4 */ 5 public static String AesDecrypt(String encryptContent, String password) { 6 if (StringUtils.isEmpty(password) || password.length() != 16) { 7 throw new RuntimeException("密鑰長度為16位"); 8 } 9 try { 10 String key = password; 11 String iv = password; 12 byte[] encrypted1 = hex2Bytes(encryptContent); 13 Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); 14 SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES"); 15 IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes()); 16 cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec); 17 byte[] original = cipher.doFinal(encrypted1); 18 return new String(original,"UTF-8").trim(); 19 } catch (Exception e) { 20 e.printStackTrace(); 21 throw new RuntimeException("aes解密發生錯誤", e); 22 } 23 }
對應Python加密:
1 def aes_encrypt(data, key): 2 """aes加密函數,如果data不是16的倍數【加密文本data必須為16的倍數!】,那就補足為16的倍數 3 :param key: 4 :param data: 5 """ 6 cipher = AES.new(key, AES.MODE_CBC, key) # 設置AES加密模式 此處設置為CBC模式 7 block_size = AES.block_size 8 # 判斷data是不是16的倍數,如果不是用b'\0'補足 9 if len(data) % block_size != 0: 10 add = block_size - (len(data) % block_size) 11 else: 12 add = 0 13 data += b'\0' * add 14 encrypted = cipher.encrypt(data) # aes加密 15 result = binascii.b2a_hex(encrypted) # b2a_hex encode 將二進制轉換成16進制 16 return result
Python解密:
1 def aes_decode(data, key): 2 """aes解密 3 :param key: 4 :param data: 5 """ 6 cipher = AES.new(key, AES.MODE_CBC, key) 7 result2 = binascii.a2b_hex(data) # 十六進制還原成二進制 8 decrypted = cipher.decrypt(result2) 9 return decrypted.rstrip(b'\0') # 解密完成后將加密時添加的多余字符'\0'刪除
從Java加密的代碼可以看出使用的是AES的CBC、128位模式,對應的Python加密也需要相同的模式,這里模式一定不要搞錯,否則加密的結果就不一樣了。
加密的時候遇到一個比較坑的問題,就是Python加密時用的方法AES.new有三個參數,第一個參數是密鑰,第二個是模式,但是第三個參數就有點蒙了,之前在網上搜的結果是需要設置一個16位的字符,可以設置為b'0000000000000000'。但是加密后結果不對,這個參數不可以隨便設置,最后又回過頭來看了看Java的代碼,發現第三個參數和第一個參數設置的值是一樣的。。。
還有個地方需要注意一下,就是Java算法中最后將字符串轉換成16進制了,所以轉換成Python的時候也需要轉換成16進制。
解密的時候算法同上理。
最后像這種Java轉Python的算法,一定要看清楚Java的每一步算法的處理方法,否則就坑大了。
以上