Skip to main content
 首页 » 编程设计

android之解密适用于 IV,即 String.getBytes(),但 SecureRandom.generateSeed() 失败

2024年11月01日38lhb25

我正在弄清楚如何使用 AES 进行跨平台(Android 和 Python)加密和解密,如果我使用基于 String 的 IV,我似乎可以成功传输数据.但是,如果我立即切换到使用 SecureRandom.generateSeed() 生成的字节,它就会出错。 key 是预先共享的。

工作的 Android 代码(删除了 try/catch block 以保持简短):

String SecretKey = "0123456789abcdef"; 
String iv = "fedcba9876543210"; 
 
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes()); 
SecretKeySpec keyspec = new SecretKeySpec(SecretKey.getBytes(), "AES"); 
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
 
//Initialize the cipher 
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);  
 
String message = "What's up?"; 
byte[] encrypted = cipher.doFinal(message.getBytes()); 
 
//Send the data 
outputStream.write(encrypted); 

有一个小的传输 header ,让客户端知道传入消息的大小,但我认为它不相关,所以我将其遗漏了。 接收此消息的 Python 代码如下所示:

#Predefined: 
unpad = lambda s : s[0:-ord(s[-1])] 
 
  encrypted = cs.recv(messagesize) # Receive the encrypted message 
  iv = encrypted[:16] 
 
  key = AES.new('0123456789abcdef', AES.MODE_CBC,IV=iv) 
  padded_msg  = key.decrypt(encrypted[16:]) 
  decrypted = unpad(padded_msg) #Remove padding 
 
  print "read [%s]" % decrypted 

结果如下:

read [What's up] 

如果我更改 Java 代码中的两行:

SecureRandom rnd = new SecureRandom(); 
IvParameterSpec ivspec = new IvParameterSpec(rnd.generateSeed(16)); 

Python 输出变为:

read [?=H��m��lڈ�1ls] 

我想知道 SecureRandom 有什么变化?我读到默认情况下 String.getBytes() 返回平台默认编码(对于 Android 4.0),所以我想知道我是否必须在 Python 端对使用 SecureRandom 生成的 IV 进行一些操作。 .?

请您参考如下方法:

接收者需要被告知 IV 是什么。查看 this Wikipedia entry 中关于 CBC 的部分:你可以看到一个加密的 n-block 消息由 n+1 个 block 组成,额外的 block 是 IV。没有用于传输它的标准协议(protocol),但我见过的每个代码都是通过在消息前面加上 IV 来实现的,这确实是很自然的事情,并且由于 CBC 的纠错特性,即使代码得到它略有错误。例如,您可以在野外找到使用常量 IV 但在纯文本前面加上随机 block 的代码,这基本上以不同的方式执行相同的操作。有时您甚至会在书中找到这种代码,例如 David Hook 的第 2 章中的 InlineIvCBCExample.java 否则非常好 book .

我推荐使用以下方法进行 AES/CBC/PKCS7Padding:

byte[] plaintext = ...; 
byte[] key = ...; 
 
// get iv 
SecureRandom rnd = new SecureRandom(); 
byte[] iv = rnd.getBytes(16); 
IvParameterSpec ivSpec = new IvParameterSpec(iv);    
 
// encrypt 
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); 
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec); 
byte[] ciphertext = cipher.doFinal(plaintext); 
 
// copy to result 
byte[] result = new byte[iv.length + ciphertext.length]; 
System.arraycopy(iv, 0, result, 0, iv.length); 
System.arraycopy(ciphertext, 0 , result, iv.length, ciphertext.length);