有些公司對接口的安全要求比較高,傳參數的時候,不會明文的傳輸,先對接口加密,返回的數據也加密返回。
目前比較常見的加密方式是AES/CBC/pkcs7padding。html
在AES加密時,通常使用了「AES/ECB/NoPadding」或「AES/ECB/PKCS5padding」 或 「AES/ECB/PKCS5padding」 的模式
使用AES加密的ECB模式,顯式指定加密算法爲:CBC或CFB模式,可帶上PKCS5Padding填充。AES密鑰長度最少是128位,推薦使用256位
AES-ECB模式加密在加密和解密是須要一個初始化向量(Initialization Vector, IV),在每次加密以前或者解密以後,使用初始化向量與明文或密文異或。java
分組密碼有五種工做體制:python
AES算法是典型的【對稱加密算法】,所謂對稱加密,就是加密和解密的祕鑰是同樣的算法
通常咱們作接口自動化測試的時候,接口都是java寫的,因此先得了解下java的加密方式。知道java對應的加密方式了,才能用python找到對應的解藥!json
/** * * @author ngh * AES128 算法 * * CBC 模式 * * PKCS7Padding 填充模式 * * CBC模式須要添加一個參數iv * * 介於java 不支持PKCS7Padding,只支持PKCS5Padding 可是PKCS7Padding 和 PKCS5Padding 沒有什麼區別 * 要實如今java端用PKCS7Padding填充,須要用到bouncycastle組件來實現 */ public class AES { // 算法名稱 final String KEY_ALGORITHM = "AES"; // 加解密算法/模式/填充方式 final String algorithmStr = "AES/CBC/PKCS7Padding"; // private Key key; private Cipher cipher; boolean isInited = false; byte[] iv = { 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, 0x30, 0x36, 0x30, 0x37, 0x30, 0x38 }; public void init(byte[] keyBytes) { // 若是密鑰不足16位,那麼就補足. 這個if 中的內容很重要 int base = 16; if (keyBytes.length % base != 0) { int groups = keyBytes.length / base + (keyBytes.length % base != 0 ? 1 : 0); byte[] temp = new byte[groups * base]; Arrays.fill(temp, (byte) 0); System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length); keyBytes = temp; } // 初始化 Security.addProvider(new BouncyCastleProvider()); // 轉化成JAVA的密鑰格式 key = new SecretKeySpec(keyBytes, KEY_ALGORITHM); try { // 初始化cipher cipher = Cipher.getInstance(algorithmStr, "BC"); } catch (NoSuchAlgorithmException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchPaddingException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchProviderException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 加密方法 * * @param content * 要加密的字符串 * @param keyBytes * 加密密鑰 * @return */ public byte[] encrypt(byte[] content, byte[] keyBytes) { byte[] encryptedText = null; init(keyBytes); System.out.println("IV:" + new String(iv)); try { cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv)); encryptedText = cipher.doFinal(content); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return encryptedText; } /** * 解密方法 * * @param encryptedData * 要解密的字符串 * @param keyBytes * 解密密鑰 * @return */ public byte[] decrypt(byte[] encryptedData, byte[] keyBytes) { byte[] encryptedText = null; init(keyBytes); System.out.println("IV:" + new String(iv)); try { cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv)); encryptedText = cipher.doFinal(encryptedData); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return encryptedText; } }
測試安全
ublic class Test { public static void main(String[] args) { AES aes = new AES(); // 加解密 密鑰 byte[] keybytes = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 }; String content = "1"; // 加密字符串 System.out.println("加密前的:" + content); System.out.println("加密密鑰:" + new String(keybytes)); // 加密方法 byte[] enc = aes.encrypt(content.getBytes(), keybytes); System.out.println("加密後的內容:" + new String(Hex.encode(enc))); // 解密方法 byte[] dec = aes.decrypt(enc, keybytes); System.out.println("解密後的內容:" + new String(dec)); }
測試結果dom
測試結果: 加密前的:1 加密密鑰:12345678 IV:0102030405060708 加密後的內容:b59227d86200d7fedfb8418a59a8eea9 IV:0102030405060708 解密後的內容:1
從上面的這一段JAVA代碼中,咱們須要知道的關鍵信息是,加密方式:AES/CBC/PKCS7Padding
iv偏移量 byte[] iv = { 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, 0x30, 0x36, 0x30, 0x37, 0x30, 0x38 }
0x30
就是16進制的0, 因此iv = b'0102030405060708', iv通常是16位
祕鑰key雖然上面測試的key是12345678,可是key通常是16位,因此它上面有個if判斷不足16位的時候,用\0去填充
那麼key應該是:12345678\0\0\0\0\0\0\0\0ide
python代碼 AES/CBC/pkcs7padding 加解密函數
from cryptography.hazmat.primitives import padding from cryptography.hazmat.primitives.ciphers import algorithms from Crypto.Cipher import AES from binascii import b2a_hex, a2b_hex import json ''' AES/CBC/PKCS7Padding 加密解密 環境需求: pip3 install pycryptodome ''' class PrpCrypt(object): def __init__(self, key='0000000000000000'): self.key = key.encode('utf-8') self.mode = AES.MODE_CBC self.iv = b'0102030405060708' # block_size 128位 # 加密函數,若是text不足16位就用空格補足爲16位, # 若是大於16可是不是16的倍數,那就補足爲16的倍數。 def encrypt(self, text): cryptor = AES.new(self.key, self.mode, self.iv) text = text.encode('utf-8') # 這裏密鑰key 長度必須爲16(AES-128),24(AES-192),或者32 (AES-256)Bytes 長度 # 目前AES-128 足夠目前使用 text=self.pkcs7_padding(text) self.ciphertext = cryptor.encrypt(text) # 由於AES加密時候獲得的字符串不必定是ascii字符集的,輸出到終端或者保存時候可能存在問題 # 因此這裏統一把加密後的字符串轉化爲16進制字符串 return b2a_hex(self.ciphertext).decode().upper() @staticmethod def pkcs7_padding(data): if not isinstance(data, bytes): data = data.encode() padder = padding.PKCS7(algorithms.AES.block_size).padder() padded_data = padder.update(data) + padder.finalize() return padded_data @staticmethod def pkcs7_unpadding(padded_data): unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder() data = unpadder.update(padded_data) try: uppadded_data = data + unpadder.finalize() except ValueError: raise Exception('無效的加密信息!') else: return uppadded_data # 解密後,去掉補足的空格用strip() 去掉 def decrypt(self, text): # 偏移量'iv' cryptor = AES.new(self.key, self.mode, self.iv) plain_text = cryptor.decrypt(a2b_hex(text)) # return plain_text.rstrip('\0') return bytes.decode(plain_text).rstrip("\x01").\ rstrip("\x02").rstrip("\x03").rstrip("\x04").rstrip("\x05").\ rstrip("\x06").rstrip("\x07").rstrip("\x08").rstrip("\x09").\ rstrip("\x0a").rstrip("\x0b").rstrip("\x0c").rstrip("\x0d").\ rstrip("\x0e").rstrip("\x0f").rstrip("\x10") def dict_json(self, d): '''python字典轉json字符串, 去掉一些空格''' j = json.dumps(d).replace('": ', '":').replace(', "', ',"').replace(", {", ",{") return j # 加解密 if __name__ == '__main__': import json pc = PrpCrypt('12345678\0\0\0\0\0\0\0\0') # 初始化密鑰 a = "1" print("加密前:%s" % a) b = pc.encrypt(a) print("解密後:%s" % b) print("大寫變小寫:%s" % b.lower())
最後運行結果測試
加密前:1 解密後:B59227D86200D7FEDFB8418A59A8EEA9 大寫變小寫:b59227d86200d7fedfb8418a59a8eea9
參考相關的博客:http://www.javashuo.com/article/p-ucqgrgbe-hv.html
https://blog.csdn.net/weixin_43107613/article/details/87875359