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

时间:2013-11-25 15:19:45

标签: android python encryption aes cbc-mode

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

使用Android代码(删除try / catch块以保持简短):

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);

有一个小的转移标题,让客户知道传入消息的大小,但我认为这是不相关的,我把它留了出来。 接收此消息的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进行一些操作。?

1 个答案:

答案 0 :(得分:2)

需要告知收件人IV是什么。查看this Wikipedia entry中关于CBC的部分:您可以看到加密的n块消息由n + 1个块组成,附加块是IV。传输它没有标准的协议,但是我看到的每个代码都是通过在信息中添加IV来实现的,这实际上是很自然的事情,并且由于CBC的纠错属性,即使代码获得有点不对劲。例如,您可以在野外找到使用常量IV的代码,但是使用随机块预先添加纯文本,这基本上以不同的方式执行相同的操作。有时你甚至会在书本中找到那种代码,比如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);