title: AES Encryption in Google App Engine (Python) and Decryption on iOS (Objective-C)
body: <p>I'm trying to encrypt some data from python (Google App Engine) and then decrypt it on iOS.</p>
  
  <p>There are several issues surrounding this based on the fact that there are so many options with AES Encryption and the different formats available in Python and Objective-C. </p>
  
  <p>Because of the limited availability of the PyCrypto libraries on Google App Engine and the AES code on the iOS/Objective-C side requiring PKCS7Padding, I decided to use slowAES on the python side. </p>
  
  <p>I'm also using a 16-bit key, CBC Mode, and PKCS7Padding.</p>
  
  <p>Given that, this is my encrypt function and helper variables/function:</p>
  
  <pre><code>def str2nums(s):
      return map(ord, s)
  
  key = "hjt4mndfy234n5fs"
  moo = aes.AESModeOfOperation()
  iv = [12, 34, 96, 15] * 4
  CBC_mode = moo.modeOfOperation['CBC']
  nkey = str2nums(key)
  
  
  def encrypt(plaintext):
    funcName = inspect.stack()[0][3]
    logging.debug("Enter " + funcName)
    logging.debug("Input string: " + plaintext)
    m, s, encData = moo.encrypt(plaintext, CBC_mode, nkey, len(nkey), iv)
    fmt = len(encData)*'B'
    dataAsStr = ""
    for j in encData:
          dataAsStr = dataAsStr + str(j) + ","
    logging.debug("Output encrypted data:[" + dataAsStr + "]")
    encoded = base64.b64encode(struct.pack(fmt, *encData))
    logging.debug("Output encrypted string: " + encoded)
    decoded = struct.unpack(fmt, base64.b64decode(encoded))
    decrypted = moo.decrypt(decoded, s, CBC_mode, nkey, len(nkey), iv)
    logging.debug("Output decrypted back: " + decrypted)
    return encoded
  </code></pre>
  
  <p>Note that on the Python side, I am encrypting, packing, and then base64 encoding the data. Before returning this encrypted data, I'm also doing a test run at decrypting it just to show that in the logs and it does indeed work.</p>
  
  <p>On the iOS side, I'm using the following AES NSData addition:</p>
  
  <pre><code>- (NSData *)AES128DecryptWithKey:(NSString *)key {
      // 'key' should be 16 bytes for AES128, will be null-padded otherwise
      char keyPtr[kCCKeySizeAES128+1]; // room for terminator (unused)
      bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
  
      // fetch key data
      [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSASCIIStringEncoding];
  
      NSUInteger dataLength = [self length];
  
      //See the doc: For block ciphers, the output size will always be less than or 
      //equal to the input size plus the size of one block.
      //That's why we need to add the size of one block here
      size_t bufferSize = dataLength + kCCBlockSizeAES128;
      void *buffer = malloc(bufferSize);
  
      size_t numBytesDecrypted = 0;
      CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                                            keyPtr, kCCKeySizeAES128,
                                            NULL /* initialization vector (optional) */,
                                            [self bytes], dataLength, /* input */
                                            buffer, bufferSize, /* output */
                                            &amp;numBytesDecrypted);
  
      if (cryptStatus == kCCSuccess) {
          //the returned NSData takes ownership of the buffer and will free it on deallocation
          return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
      }
  
      free(buffer); //free the buffer;
      return nil;
  }
  </code></pre>
  
  <p>And I'm making use of this when I pull down the base64/packed/encrypted data like so:</p>
  
  <pre><code>NSMutableString * result = [[NSMutableString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
  if (LOG) { NSLog(@"Base64 encoded / Encrypted string: %@", result); }
  
  NSData* encryptedData = [NSData decodeBase64ForString:result]; // base64 decode
  
  if (LOG) { NSLog(@"Encrypted string: %@", encryptedData); }
  
  NSData* decryptedData = [encryptedData AES128DecryptWithKey:@"hjt4mndfy234n5fs"]; // AES Decrypt
  </code></pre>
  
  <p>The problem is, I can't seem to get the data to decrypt correctly on the client (iOS) side even though it decrypts just fine on the server (in python). </p>
  
  <p>During the decryption, the cryptStatus always ends up : <strong>kCCAlignmentError.</strong> Which I don't quite understand.</p>
  
  <p>I've also messed with AES 256 but I need a 32bit key I think and that doesn't seem to be an option for slowAES in CBC mode (at least according to the examples?). </p>
  
  <p>Logging of the Server (Notice the actual unencrypted data is merely an empty set [] . That's a JSON representation of such to return to the client.</p>
  
  <pre><code>2012-01-04 08:48:13.962
  Enter encrypt
  D 2012-01-04 08:48:13.962
  Input string: []
  D 2012-01-04 08:48:13.967
  Output encrypted data:[4,254,226,26,101,240,22,113,44,54,209,203,233,64,208,255,]
  D 2012-01-04 08:48:13.967
  Output encrypted string: BP7iGmXwFnEsNtHL6UDQ/w==
  D 2012-01-04 08:48:13.971
  Output decrypted back: []
  </code></pre>
  
  <p>Logging of the client (iOS):</p>
  
  <pre><code>2012-01-04 12:45:13.891 Base64 encoded / Encrypted string: BP7iGmXwFnEsNtHL6UDQ/w==
  2012-01-04 12:45:13.892 Encrypted string: &lt;04fee21a 65f01671 2c36d1cb e940d0ff&gt;
  2012-01-04 12:45:29.126 Decrypted string: 
  </code></pre>
  
  <p>So my questions are:</p>
  
  <ol>
  <li>What does it mean by an "Alignment Error"? Or what am I doing wrong that it doesn't want to decrypt on the client?</li>
  <li>Do I need to worry about unpacking the data at all considering it looks like it matches up just fine? And if so, how would I go about an unpack() function in C or Objective-C?</li>
  <li>Is there just plain a better way to do AES encryption between Google App Engine and iOS?</li>
  </ol>
  
  <p>EDIT: I should also note that besides the answer of using the same Initialization Vector, I found a discrepancy between the python and iOS code for the padding. The encrypt/decrypt worked fine for small amounts of data but then I ran into failure to decrypt with larger ones. I fixed this by changing the iOS side to NOT use PKCS7Padding and put 0 there instead.</p>