The situation I'm trying to solve: in my Cocoa app, I need to encrypt a string with a symmetric cipher, POST it to PHP, and have that script decode the data. The process needs to work in reverse for returning an answer (PHP encodes, Cocoa decodes).
I'm missing something because even though I can get both the key and initialization vector (iv) to be the same in both PHP and Cocoa, the decoding never works when one app sends its encoded data to the other. Both work just fine encoding/decoding their own data (verified to make sure there wasn't some PEBKAC issue at hand). I have a suspicion that there's a padding issue someplace, I just don't see it.
My cocoa app encodes using SSCrypto (which is just a handy-dandy wrapper around OpenSSL functions). The cipher is Blowfish, mode is CBC. (forgive the memory leaks, code has been stripped to the bare essentials)
NSData *secretText = [@"secretTextToEncode" dataUsingEncoding:NSUTF8StringEncoding];
NSData *symmetricKey = [@"ThisIsMyKey" dataUsingEncoding:NSUTF8StringEncoding];
unsigned char *input = (unsigned char *)[secretText bytes];
unsigned char *outbuf;
int outlen, templen, inlen;
inlen = [secretText length];
unsigned char evp_key[EVP_MAX_KEY_LENGTH] = {"\0"};
int cipherMaxIVLength = EVP_MAX_IV_LENGTH;
EVP_CIPHER_CTX cCtx;
const EVP_CIPHER *cipher = EVP_bf_cbc();
cipherMaxIVLength = EVP_CIPHER_iv_length( cipher );
unsigned char iv[cipherMaxIVLength];
EVP_BytesToKey(cipher, EVP_md5(), NULL, [symmetricKey bytes], [symmetricKey length], 1, evp_key, iv);
NSData *initVector = [NSData dataWithBytes:iv length:cipherMaxIVLength];
EVP_CIPHER_CTX_init(&cCtx);
if (!EVP_EncryptInit_ex(&cCtx, cipher, NULL, evp_key, iv)) {
EVP_CIPHER_CTX_cleanup(&cCtx);
return nil;
}
int ctx_CipherKeyLength = EVP_CIPHER_CTX_key_length( &cCtx );
EVP_CIPHER_CTX_set_key_length(&cCtx, ctx_CipherKeyLength);
outbuf = (unsigned char *)calloc(inlen + EVP_CIPHER_CTX_block_size(&cCtx), sizeof(unsigned char));
if (!EVP_EncryptUpdate(&cCtx, outbuf, &outlen, input, inlen)){
EVP_CIPHER_CTX_cleanup(&cCtx);
return nil;
}
if (!EVP_EncryptFinal(&cCtx, outbuf + outlen, &templen)){
EVP_CIPHER_CTX_cleanup(&cCtx);
return nil;
}
outlen += templen;
EVP_CIPHER_CTX_cleanup(&cCtx);
NSData *cipherText = [NSData dataWithBytes:outbuf length:outlen];
NSString *base64String = [cipherText encodeBase64WithNewlines:NO];
NSString *iv = [initVector encodeBase64WithNewlines:NO];
base64String and iv are then POSTed to PHP that attempts to decode it:
<?php
import_request_variables( "p", "p_" );
if( $p_data != "" && $p_iv != "" )
{
$encodedData = base64_decode( $p_data, true );
$iv = base64_decode( $p_iv, true );
$td = mcrypt_module_open( MCRYPT_BLOWFISH, '', MCRYPT_MODE_CBC, '' );
$keySize = mcrypt_enc_get_key_size( $td );
$key = substr( md5( "ThisIsMyKey" ), 0, $keySize );
$decodedData = mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $encodedData, MCRYPT_MODE_CBC, $iv );
mcrypt_module_close( $td );
echo "decoded: " . $decodedData;
}
?>
decodedData is always gibberish.
I've tried reversing the process, sending the encoded output from PHP to Cocoa but EVP_DecryptFinal() fails, which is what leads me to believe there's a NULL padding issue somewhere. I've read and re-read the PHP and OpenSSL docs but it's all blurring together now and I'm out of ideas to try.