Pergunta

I'm figuring out how to do cross platform (Android & Python) encryption&decryption using AES, and I seem to succeed transferring data if I use a String based IV. But immediately if I switch to using bytes generated with SecureRandom.generateSeed(), it goes awry. The secret keys are preshared.

Working Android code (try/catch blocks removed to keep it short):

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

There is a small transfer header that let's the client know the size of the incoming message, but I thought it's not relevant and I left that out. The Python code receiving this message looks like:

#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

Result looks like:

read [What's up]

And if I change two lines in Java code:

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

Python output becomes:

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

I wonder what changes with the SecureRandom? I read that by default the String.getBytes() return platform default encoding (for Android 4.0), so I wonder if I have to do some manipulation on Python end to the IV that was generated with SecureRandom..?

Foi útil?

Solução

The recipient needs to be told what the IV is. Look at the section on CBC in this Wikipedia entry: you can see that an encrypted n-block message consists of n+1 blocks, the additional block being the IV. There is no standard protocol for transmitting it, but every code i've seen does this by prepending the IV to the message, which really is the natural thing to do and thanks to the error-correcting properties of CBC works even when the code gets it slightly wrong. For example, you can find code in the wild that uses a constant IV but prepends the plain text with a random block, which basically does the same thing in a different way. Sometimes you'll find that kind of code even in books, like the InlineIvCBCExample.java in chapter 2 of David Hook's otherwise very good book.

I recommed the following way of doing 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);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top