How to decrypt a PHP script in Objective C / iOS - php

I've checked all the related Stack Overflow questions. Also checked the links in that answers but didn't got any usable solution.
Here is my php script and I've nothing to do with this script (as I can't change the script).
function encrypt($message,$secretKey) {
return base64_encode(
mcrypt_encrypt(
MCRYPT_RIJNDAEL_256,
$secretKey,
$message,
MCRYPT_MODE_ECB
)
);
}
I'm unable to decrypt it in Objective C. I've used a number of Categories like Strong Encryption for Cocoa / Cocoa Touch etc, also I followed this question How do I do base64 encoding on iOS?
Here is the objective C codes that I used for decryption (found in cocoa-aes Category NSData+AES.h)
- (NSData *)AESDecryptWithPassphrase:(NSString *)pass
{
NSMutableData *ret = [NSMutableData dataWithCapacity:[self length]];
unsigned long rk[RKLENGTH(KEYBITS)];
unsigned char key[KEYLENGTH(KEYBITS)];
const char *password = [pass UTF8String];
for (int i = 0; i < sizeof(key); i++)
key[i] = password != 0 ? *password++ : 0;
int nrounds = rijndaelSetupDecrypt(rk, key, KEYBITS);
unsigned char *srcBytes = (unsigned char *)[self bytes];
int index = 0;
while (index < [self length])
{
unsigned char plaintext[16];
unsigned char ciphertext[16];
int j;
for (j = 0; j < sizeof(ciphertext); j++)
{
if (index >= [self length])
break;
ciphertext[j] = srcBytes[index++];
}
rijndaelDecrypt(rk, nrounds, ciphertext, plaintext);
[ret appendBytes:plaintext length:sizeof(plaintext)];
NSString* s = [[NSString alloc] initWithBytes:plaintext length:sizeof(plaintext) encoding:NSASCIIStringEncoding];
NSLog(#"%#",s);
}
return ret;
}
Also I tried this decoder
- (NSData*) aesDecryptWithKey:(NSString *)key initialVector:(NSString*)iv
{
int keyLength = [key length];
if(keyLength != kCCKeySizeAES128)
{
DebugLog(#"key length is not 128/192/256-bits long");
///return nil;
}
char keyBytes[keyLength+1];
bzero(keyBytes, sizeof(keyBytes));
[key getCString:keyBytes maxLength:sizeof(keyBytes) encoding:NSUTF8StringEncoding];
size_t numBytesDecrypted = 0;
size_t decryptedLength = [self length] + kCCBlockSizeAES128;
char* decryptedBytes = malloc(decryptedLength);
CCCryptorStatus result = CCCrypt(kCCDecrypt,
kCCAlgorithmAES128 ,
(iv == nil ? kCCOptionECBMode | kCCOptionPKCS7Padding : kCCOptionPKCS7Padding),
keyBytes,
keyLength,
iv,
[self bytes],
[self length],
decryptedBytes,
decryptedLength,
&numBytesDecrypted);
if(result == kCCSuccess){
NSData* d=[NSData dataWithBytesNoCopy:decryptedBytes length:numBytesDecrypted];
NSLog(#"%#",[NSString stringWithUTF8String:[d bytes]]);
return d;
}
free(decryptedBytes);
return nil;
}

From the looks of it, that php function does two things.
mcrypt using MCRYPT_RIJNDAEL_256
base64 encodes the output of (1)
That would by why simply using base64 doesn't work. I'm going to guess from the name that MCRYPT_RIJNDAEL_256 is just AES 256.
Hope that helps.
Edit:
The code you added above looks ok. You just have to base64 decode the data first.
The php script does this:
aes encrypt
base64 encode
So you want to do this in your cocoa app:
base64 decode
aes decrypt
If you're having trouble, you might want to play around and see if you can get cocoa to do the same thing as the php script: encrypt and base64 encode the data. If you can get the output of your encryption function to be the same as the output of the php encryption function, you're in a good place to get it decrypting.

Related

OpenSSL RSA decryption failed

I'm trying to encrypt data with RSA in a C program, that I send with POST method on my server and then decrypt it in PHP. For both program I use OpenSSL to encrypt / decrypt the data.
I have the error "error:0407109F:rsa routines:RSA_padding_check_PKCS1_type_2:pkcs decoding error" during the decryption in PHP and no data is return by the openssl_private_decrypt function.
Here is my C program :
RSA *createRSA(unsigned char *key, int public)
{
RSA *rsa = RSA_new();
BIO *keybio;
keybio = BIO_new_mem_buf(key, -1);
if (keybio == NULL)
return 0;
if (public)
rsa = PEM_read_bio_RSA_PUBKEY(keybio, &rsa, NULL, NULL);
else
rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa,NULL, NULL);
return rsa;
}
char *rsa_encode(char *json)
{
char *data;
RSA *rsa;
int encrypted_length = 0;
char pub[] = "MY_PUBLIC_KEY"; //RSA public key of size 8192
rsa = createRSA((unsigned char *)pub, 1);
if (!(data = malloc(RSA_size(rsa))))
exit(0);
encrypted_length = RSA_public_encrypt((int)strlen(json), (unsigned char *)json, (unsigned char *)data, rsa, RSA_PKCS1_PADDING);
RSA_free(rsa);
if (encrypted_length > 0)
return data;
return NULL;
}
char *bin2hex(const unsigned char *key, int size)
{
char *hex;
int i = 0;
if (!(hex = malloc((size * 2 + 1) * sizeof(char))))
handleError();
while (i < size)
{
sprintf((char *)(hex + (i * 2)),"%02x", key[i]);
i++;
}
hex[i * 2] = '\0';
return hex;
}
Here is my PHP program :
$hexa = $_POST["data"];
$data = hexToStr($hexa);
$privateKey = openssl_get_privatekey("file://private.pem");
$decrypted = "";
$ret = openssl_private_decrypt($data, $decrypted, $privateKey, OPENSSL_PKCS1_PADDING);
echo openssl_error_string() . "\n";
I don't understand where is the error, for both program I use OPENSSL_PKCS1_PADDING so why OpenSSL error tell me that the error is on my padding ?
I finally found why decryption didn't work.
I was using this line to encode in hexa in C :
encoded = bin2hex(data, (int)strlen((char *)data);
data (the encrypted data) is unsigned char * and can contain null bytes. But I was using strlen which stop on first null byte. So my encrypted data was truncated when it was sended on server.
So the solution is to use the length of data that is given in return value of len = RSA_public_encrypt((int)strlen((char *)src), src, encrypted, rsa, RSA_PKCS1_PADDING); instead of (int)strlen((char *)data);
Thanks you all for your help !

AESCrypt encryption/decryption from PHP to iOS

I need to convert the encryption/decryption method follows in our server side, please check the below code
-------------------------------------------------------------------------------------------------------------------------------------
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$key = "a16byteslongkey!";
$plaintext = "iphone";
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $plaintext, MCRYPT_MODE_CBC, $iv);
$ciphertext = base64_encode($ciphertext);
echo "ciphertext: ".$ciphertext."<br/>";
$ciphertextinput = base64_decode($ciphertext);
$cipherdetext = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $ciphertextinput, MCRYPT_MODE_CBC, $iv);
echo "decryptText:".$cipherdetext."<br/>";
I tried with AES 256 encryption but it gives different results,
Encryption
- (NSData *)AES256EncryptWithKey:(NSString *)key {
// 'key' should be 32 bytes for AES256, will be null-padded otherwise
char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
// fetch key data
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
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 numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES256,
NULL /* initialization vector (optional) */,
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
free(buffer); //free the buffer;
return nil;}
Decryption
- (NSData *)AES256DecryptWithKey:(NSString *)key {
// 'key' should be 32 bytes for AES256, will be null-padded otherwise
char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
// fetch key data
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
char ivPtr = "";
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, kCCKeySizeAES256,
NULL /* initialization vector (optional) */,
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&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;}
OriginalText: iphone
ciphertext: SXNepKfh0IrlDDdkq4EdmQ==
Whats wrong here, Please give some suggestion for decrypt encrypted text from PHP. How to get exact "iphone" word from base 64 encode string SXNepKfh0IrlDDdkq4EdmQ==
Also, Please let me know how to use ECB Mode in AES encryption.
Thanks!
The problem:
`mcrypt uses non-standard null data padding, the iOS code is using PKCS#7 padding.
The fix, three choices:
Use PKCS#7 padding, for mcrypt add PKCS#7 padding the prior to encryption and remove it after decryption. (recommended)
or
Use null padding, for Common Crypto do not specify kCCOptionPKCS7Padding, add null padding prior to encryption and remove after decryption. (not recommended)
or
It is best not to use mcrypt, it is abandonware, has not been updated in years and does not support standard PKCS#7 (née PKCS#5) padding, only non-standard null padding that can't even be used with binary data. mcrypt has many outstanding bugs dating back to 2003. The mcrypt-extension is deprecated will be removed in PHP 7.2. Instead consider using defuse or RNCryptor, they provide a complete solution and are being maintained and is correct.

PHP encrypting and objective-c decrypting

I have to generate encrypted key on my php server and send it to the ipad app to decrypt it.
What I did in the php server side :
$iv = mcrypt_create_iv(32);
$privatEencryptKey = "1111";
$data = "2222";
$encryptedData = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $privateEncryptKey, base64_encode($data), MCRYPT_MODE_CBC, $iv);
$decryptedData = base64_decode(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $privateEncryptkey, $encryptedData, MCRYPT_MODE_CBC, $iv));
echo base64_encode($encryptedData); //output = WT7LorzZ1EQo2BeWxawW3Q==
echo $decryptedData; // output = 2222
echo base64_encode($iv); // output = fZTj4BxWSdCYQW/scUHvx9QoiTNXmxNrGWb/n7eFkR4=
and in the xcode I import the sercurity.framwork and I added 3rd party for base64 (encoding & decoding) and I use the (CommonCryptor.h) also and here is my code :
+ (NSData *)doCipher:(NSData *)dataIn
iv:(NSData *)iv
key:(NSData *)symmetricKey
context:(CCOperation)encryptOrDecrypt{
CCCryptorStatus ccStatus = kCCSuccess;
size_t cryptBytes = 0; // Number of bytes moved to buffer.
NSMutableData *dataOut = [NSMutableData dataWithLength:dataIn.length + kCCBlockSizeAES128];
ccStatus = CCCrypt( encryptOrDecrypt,
kCCAlgorithmAES128,
0,
symmetricKey.bytes,
kCCKeySizeAES128,
iv.bytes,
dataIn.bytes,
dataIn.length,
dataOut.mutableBytes,
dataOut.length,
&cryptBytes);
if (ccStatus != kCCSuccess) {
NSLog(#"CCCrypt status: %d", ccStatus);
}
dataOut.length = cryptBytes;
return dataOut;
}
+ (void) testCipher{
NSData *dataIn = [[#"WT7LorzZ1EQo2BeWxawW3Q==" base64DecodedString] dataUsingEncoding:NSUTF8StringEncoding];
NSData *key = [#"1111" dataUsingEncoding:NSUTF8StringEncoding];
NSData *iv = [[#"fZTj4BxWSdCYQW/scUHvx9QoiTNXmxNrGWb/n7eFkR4=" base64DecodedString] dataUsingEncoding:NSUTF8StringEncoding];
NSData *dataOut = [Utils doCipher:dataIn iv:iv key:key context:kCCDecrypt];
NSString* strOut = [[[NSString alloc] initWithData:dataOut
encoding:NSUTF8StringEncoding] base64DecodedString];
NSLog(#"%#", strOut);
}
I got the nil for strOut..... :(
Any help please......
You should use 16-byte Key and IV for AES-128. Mcrypt_encrypt otherwise pads this with zero.
Most likely you should manually add PKCS#5 padding to the input since the mcrypt_encrypt pads data with zero, which is not common practice.

Why does this AES encryption on an iPad and decryption in PHP fail?

I have an iPad application that transmits encrypted information to a PHP-based website, but I'm having difficulties in properly decrypting this information. I use the following code for the PHP-side decryption:
//Decryption function
function mc_decrypt($decrypt, $key, $iv)
{
$decoded = base64_decode($decrypt);
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
mcrypt_generic_init($td, $key, $iv);
$decrypted = mdecrypt_generic($td, $decoded);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return trim($decrypted);
}
and this Objective-C code in my iPad application:
#import <CommonCrypto/CommonCryptor.h>
#implementation NSData (AES256)
- (NSData *)AES256EncryptWithKey:(NSString *)key {
// 'key' should be 32 bytes for AES256, will be null-padded otherwise
char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
// fetch key data
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
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 numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES256,
NULL /* initialization vector (optional) */,
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
free(buffer); //free the buffer;
return nil;
}
- (NSData *)AES256DecryptWithKey:(NSString *)key {
// 'key' should be 32 bytes for AES256, will be null-padded otherwise
char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
// fetch key data
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
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, kCCKeySizeAES256,
NULL /* initialization vector (optional) */,
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&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;
}
#end
Why am I seeing data corruption when trying to decrypt data encoded on the iPad and decrypted on the PHP side?
Check the key that you're using. In PHP the MCRYPT_RIJNDAEL_128 _256 etc constants do not represent the key strength, but rather the block size being used.
To get 128-bit encryption with PHP you want to use a key that is 16 bytes long. For 256 you need 32 bytes and so on and so forth.
Both you PHP and C code look correct to me. Make sure the key is being used correctly in both cases.
As another thought. It looks like you're using PKCS#7 padding in C. I don't believe PHP is designed to work with that padding by default. If memory serves me, I believe the Mcrypt functions use null padding.
Lastly check your initialization vector in PHP. I noticed you're not using one in your C code which means you should not be accepting an $iv variable in PHP. That should be passed as NULL into the mcrypt functions (but this is highly discouraged).
What you should do instead is randomize an IV in your C code (where the data is being encrypted) and then send the IV along with the encrypted data. You can detect the size of the IV for the algorithm being used and split it off the front of the encrypted data to then use to populate your PHP side of things. This further secures your encryption for you.
please refere to this discussion for better usage AES encrypt/decrypt
AES with CommonCrypto uses too much memory - Objective-C

RC4 encryption - CommonCrypto (Objective-C) vs PHP

I have a XML file on a server encrypted with the RC4 algorithm (http://rc4crypt.devhome.org)
function encrypt ($pwd, $data, $ispwdHex = 0)
{
if ($ispwdHex)
$pwd = #pack('H*', $pwd); // valid input, please!
$key[] = '';
$box[] = '';
$cipher = '';
$pwd_length = strlen($pwd);
$data_length = strlen($data);
for ($i = 0; $i < 256; $i++)
{
$key[$i] = ord($pwd[$i % $pwd_length]);
$box[$i] = $i;
}
for ($j = $i = 0; $i < 256; $i++)
{
$j = ($j + $box[$i] + $key[$i]) % 256;
$tmp = $box[$i];
$box[$i] = $box[$j];
$box[$j] = $tmp;
}
for ($a = $j = $i = 0; $i < $data_length; $i++)
{
$a = ($a + 1) % 256;
$j = ($j + $box[$a]) % 256;
$tmp = $box[$a];
$box[$a] = $box[$j];
$box[$j] = $tmp;
$k = $box[(($box[$a] + $box[$j]) % 256)];
$cipher .= chr(ord($data[$i]) ^ $k);
}
return $cipher;
}
Here is the objective-C code I use to decrypt :
NSData *dataToDecrypt = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.url.com/fileCrypted.xml"]] returningResponse:nil error:nil];
const void *vplainText;
size_t plainTextBufferSize;
plainTextBufferSize = [dataToDecrypt length];
vplainText = [dataToDecrypt bytes];
CCCryptorStatus ccStatus;
uint8_t *bufferPtr = NULL;
size_t bufferPtrSize = 0;
size_t movedBytes = 0;
bufferPtrSize = (plainTextBufferSize + kCCBlockSize3DES) & ~(kCCBlockSize3DES - 1);
bufferPtr = malloc( bufferPtrSize * sizeof(uint8_t));
memset((void *)bufferPtr, 0x0, bufferPtrSize);
NSString *key = #"mykey";
//NSString *initVec = #"init Vec";
const void *vkey = (const void *) [key UTF8String];
const void *vinitVec = (const void *) [initVec UTF8String];
size_t keyLength = [[key dataUsingEncoding:NSUTF8StringEncoding] length];
ccStatus = CCCrypt(kCCDecrypt,
kCCAlgorithmRC4,
0,
vkey,
kCCKeySizeDES,
nil,
vplainText,
plainTextBufferSize,
(void *)bufferPtr,
bufferPtrSize,
&movedBytes);
if (ccStatus == kCCSuccess) NSLog(#"SUCCESS");
/*else*/ if (ccStatus == kCCParamError) return #"PARAM ERROR";
else if (ccStatus == kCCBufferTooSmall) return #"BUFFER TOO SMALL";
else if (ccStatus == kCCMemoryFailure) return #"MEMORY FAILURE";
else if (ccStatus == kCCAlignmentError) return #"ALIGNMENT";
else if (ccStatus == kCCDecodeError) return #"DECODE ERROR";
else if (ccStatus == kCCUnimplemented) return #"UNIMPLEMENTED";
NSString *result = [[ NSString alloc ] initWithData: [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)movedBytes] encoding:NSASCIIEncoding];
Logs output : SUCCESS but my result is not good (I tested many encoding but ASCII seems to be the good, cf. PHP function ord ...)
Are my both RC4 implementation standard ?
Edit: removed IV in Objective-C code
Edit2: Objective-C KeyLength = password data length, removed option
If you don't know what you're doing in cryptography, then "rolling-your-own" is a sure fire receipe for disaster. You don't have to believe me, read John Viega on The Cult of Schneier about general programmers who try to "roll their own" cryptography. Paraphrased: Don't do it.
Since this is a file, and I'm guessing a reasonable small file, you can use more standard and higher level libraries using standards such as SSL (OpenSSL and others) or OpenPGP (GPG, etc.) to do the necessary encryption / decryption. I believe there is library or module support for SSL in both Objective-C (or the iPhone environment) and PHP.
In particular about RC4, it is a stream cipher that is deceptively simple to write, but incredibility easy to mess up in its implementation details. See The Misuse of RC4 in Microsoft Word and Excel, and Can you recommend RC4 128-bit encrypted software? for an well known historic example, and a recommendation of security / cryptography expert (former CTO and co-founder of PGP Corp.).
Added:
The keysize in the php script is based on the raw length of the password data. It also does not use any PKCS7 Padding, so I believe that field should be zero (0) (CCryptor doesn't support padding of stream ciphers, and the php version certainly doesn't use it). In your code CCCrypt used a keysize of 8 bytes (64* bits), whereas I believe you want it to be the length (in bytes) of the password (binary data).
There is no MAC or hash of the data so the functions won't be able to determine valid from invalid decodings.
I think this will get you closer to compatibility with this insecure implementation of RC4 (RC4crypt in PHP).
DES uses 56-bits of key from 64-bits of key input.
Building on #mctylr, Avoid rolling your own whenever possible. If you need RC4, use a pre-built/tested library: OpenSSL RC4. You can replace your entire code with about 3 lines.

Categories