I want to use AES to encrypt a password in Objective-C, and then decrypt it in PHP, but I have two problems.
I encrypt the password, but it's an NSData object, so I encode it with base64, but when I decode in PHP, the result is nil. So I can't decrypt it.
I can encrypt and decrypt the password in Objective-C, so it is the PHP that is the problem, but when I encrypt with AES and then encode with base64, the results are not the same.
Here is my code:
PHP:
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$key = "a16byteslongkey!";
$plaintext = "iphone";
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $plaintext, MCRYPT_MODE_ECB, $iv);
$ciphertext = base64_encode($ciphertext);
echo "ciphertext: ".$ciphertext."<br/>";
$ciphertext = base64_decode($ciphertext);
$plaintext = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $ciphertext, MCRYPT_MODE_ECB, $iv);
echo "plaintext: ".$plaintext."<br/>";
output:
ciphertext: SXNepKfh0IrlDDdkq4EdmQ==
plaintext: iphone
Objective-C: (Get the full source code here: https://gist.github.com/838614)
NSString *key = #"a16byteslongkey!";
NSString *plaintext = #"iphone";
NSString *ciphertext = [plaintext AES256EncryptWithKey: key];
NSLog(#"ciphertext: %#", ciphertext);
plaintext = [ciphertext AES256DecryptWithKey: key];
NSLog(#"plaintext: %#", plaintext);
output:
ciphertext: D19l3gsgXJlrLl7B2oCT6g==
plaintext: iphone
i replace kCCKeySizeAES256 with kCCKeySizeAES128, and replace "kCCOptionPKCS7Padding" with "kCCOptionPKCS7Padding | kCCOptionECBMode",
i have slove the problem.
don't change the code from https://gist.github.com/838614
the key should be 32 byte.
the results of encryt are not the same, but they'll be the same if you decrypt.
objective-c:
NSString *key = #"a16byteslongkey!a16byteslongkey!";
NSString *plaintext = #"iphone";
NSString *ciphertext = [plaintext AES256EncryptWithKey: key];
NSLog(#"ciphertext: %#", ciphertext);
plaintext = [ciphertext AES256DecryptWithKey: key];
NSLog(#"plaintext: %#", plaintext);
output:
ciphertext: I3chV+E2XUHeLCcJAhBaJQ==
plaintext: iphone
php:
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$key = 'a16byteslongkey!a16byteslongkey!';
$plaintext = "iphone";
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $plaintext, MCRYPT_MODE_ECB);
$base64encoded_ciphertext = base64_encode($ciphertext);
echo "ciphertext: ".$base64encoded_ciphertext."<br/>";
$plaintext = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($base64encoded_ciphertext), MCRYPT_MODE_ECB);
echo "plaintext: ".$plaintext."<br/>";
$base64encoded_ciphertext = "I3chV+E2XUHeLCcJAhBaJQ==";
$plaintext = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($base64encoded_ciphertext), MCRYPT_MODE_ECB);
echo "plaintext: ".trim($plaintext);
output:
ciphertext: kUr+YsYtb3Uy34li/GPcjg==
plaintext: iphone
plaintext: iphone
fixed use some thing like
$iv2 = '';
for ($i = 0; $i < 16; $i++) {
$iv2 .= "\0";
}
mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($text), MCRYPT_MODE_CBC, $iv2);
don't change the code from https://gist.github.com/838614
the key should be 32 byte
the results of encryt and decrypt are not the same
I have tried to test with this string:
$plaintex = "fskfladsadsadfsfs dfskl;dfs a jadfsa ds'a' j afdjdfsaadfs' jdfas af 'ksfegfffffffffffffffffffffsdfsfgfsfdsdfddfsg"
and the results are different. Anybody here know what the reason could be?
I suspect that it's the padding routine. I've approached this by making sure that the text to be encrypted is padded to 32 char with spaces, and before the result is returned from teh decryption routine, trim off the extra spaces.
There are official padding algorithms, but I found they didn't work. If you encrypt a string that is a multiple of 32 characters long, then even if the padding routine is wrong it will ignore it.
Related
Can you please give me a full example of how to create an encryption and decryption in PHP language? I use hexa for the data and the key. I search through google and find that there is one website that match my expectation which is here.
Take this for example:
Data: 225551100012FFFF
Key: DC1C1F2B180F85D8D522A75D2354ED149A5B81F198387B51
When I decrypt, I got 389da227862957c4
Thank you in advance!
Have found my answer from this website http://www.isapp.it/en/menu-en/31-tips-a-tricks/php/118-php-how-to-encrypt-text-in-triple-des-ecb.html
But because i want to encrypt and decrypt it using hexa, i modify the code a bit to this
function cryptECB($crypt, $key) {
//Omit hex2bin and bin2hex if plain text is used
$crypt = hex2bin($crypt);
$key = hex2bin($key);
$iv_size = mcrypt_get_iv_size(MCRYPT_3DES, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$cryptText = mcrypt_encrypt(MCRYPT_3DES, $key, $crypt, MCRYPT_MODE_ECB, $iv);
return bin2hex($cryptText);
}
function decryptECB($encrypted, $key) {
//Omit hex2bin and bin2hex if plain text is used
$encrypted = hex2bin($encrypted);
$key = hex2bin($key);
$iv_size = mcrypt_get_iv_size(MCRYPT_3DES, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$stringText = mcrypt_decrypt(MCRYPT_3DES, $key, $encrypted, MCRYPT_MODE_ECB, $iv);
return bin2hex($stringText);
}
$secretKey = "MYSECRETKEY";
$plaintext = 'Plain Text Will Be here';
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$ivDecode = base64_encode(mcrypt_create_iv($iv_size, MCRYPT_RAND));
$encrypted = trim(mcrypt_encrypt(MCRYPT_RIJNDAEL_128,
substr(sha1($secretKey), 0, 32),
$plaintext,
MCRYPT_MODE_CBC,
$iv), "\0..\32");
$encrypted = $iv . $encrypted;
$ciphertext_base64 = base64_encode($encrypted);
#echo $ciphertext_base64 . "\n";
$decrypted = trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128,
substr(sha1($secretKey), 0, 32),
base64_decode($ciphertext_base64),
MCRYPT_MODE_CBC,
base64_decode($ivDecode)), "\0..\32");
echo $decrypted;
when I run above code I got this output.
»_w>ø9â„6ÅkžPlain Text Will Be here
I can't edit $decrypted string because I can't access it. I just can edit $encrypted only. So how can remove extra special characters(»_w>ø9â„6Åkž) from out put by editing $encrypted string. I want to send encrypted text using JSON to the different server to decrypt it.
It is not possible to split the iv and encrypted data prior to Base64 decoding, first Base64 decode and then split them.
MCRYPT_RIJNDAEL_128 which is also AES has a block size of 128-bits or 16-bytes. The iv must be that size. Instead of including base64_decode($iv) as a parameter actually create a 16-byte iv. Base64 decoding the iv will not work if it is is not Base64 encoded, it isn't in this case.
The key should be 128, 192 or 256 bits (16, 24 or 32 bytes), exactly the correct size for interoperability, do not rely on padding by the encryption algorithms.
Similarly, for the input to be encrypted and the key prepare it in a separate statement so that debugging is easier.
Do not trim the output, the mcrypt_decrypt is correct. Padding may add an additional block, that is required.
Do not Base64 decode the result of the decryption, the plaintext was not Base64 encoded. – zaph just now edit
"text like this ïÕ[pI¤;Køv" probably occurs when attempting to print data as a string, not all binary bytes have a print representation and many have special characters as their print representation in the 0x80-0xff range.
Here is the concept, not tested, I have not used php in 20 years so fix any errors:
$secretKey = "1234567890123456"; # Note the length is 16-bytes, a full key
$plaintext = 'XXXXXXXXX';
echo $plaintext . "\n";
# --- ENCRYPTION ---
$key = substr(sha1($secretKey), 0, 32)
$iv = mcrypt_create_iv(16, MCRYPT_RAND);
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128,
$key,
$plaintext,
MCRYPT_MODE_CBC,
$iv);
# prepend the IV for it to be available for decryption
$ciphertext = $iv . $ciphertext;
$ciphertext_base64 = base64_encode($ciphertext);
echo $ciphertext_base64 . "\n";
# --- DECRYPTION ---
$key = substr(sha1($secretKey), 0, 32)
$cipher_text_iv = base64_decode($ciphertext_base64)
# split the iv and encrypted text
$iv = substr($cipher_text_iv, 0, 16)
$ciphertext = substr($cipher_text_iv, 16)
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128,
$key,
$ciphertext,
MCRYPT_MODE_CBC,
$iv);
echo $decrypted;
I have the following encrypt code:
function encryptData($value){
$key = "7685647tfyr65413285746352413sgfh";
$text = $value;
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_ECB, $iv);
return $crypttext;
}
echo 'Encrpt: ' . encryptData('This is just a test');
The output is: Encrpt: yUB�F3�*ľ�G-�ۅd�8�f�_�X/O
I'm going to place this into a mySQL database but was unsure if it would accept those types of weird characters?
Am i doing this correctly?
Yes, you are doing it correctly, however the output is a binary value. To be save, it's good practice to encode it to a 'regular' string via
$encrypted_base64 = base64_encode($crypttext);
Just remember to do the opposite before decoding;
$crypttext = base64_decode($encrypted_base64);
I have a problem ... and It kills me
Below is my code:
PHP
1-I did not use any kind of pre-padding and generated the IV with 16 bytes.
2-I forced the pbkdf2 function to return 8 bytes for key & hmacKey
$key = "myKey";
$plainText = "iphone";
$iv = mcrypt_create_iv(16, MCRYPT_RAND);
$keySalt = '12345678';
$hmacSalt = '12345678';
$_key = pbkdf2('SHA1', $key, $keySalt, 10000, 8, true);
$_hmacKey = pbkdf2('SHA1', $key, $hmacSalt, 10000, 8, true);
$cipherText = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $_key, $plainText, MCRYPT_MODE_CBC, $iv);
$dataWithoutHMAC = chr(2).chr(1).$keySalt.$hmacSalt.$iv.$cipherText;
3-first try I passed the cipher text to the hash_hmac
$data = base64_encode($dataWithoutHMAC.hash_hmac('SHA256',$cipherText,$_hmacKey, true));
and I got "et.robnapier.RNCryptManager error -4301" while decrypting in iOS. "which mean too small buffer as I think"
4-second try I passed the header (chr(2).chr(1).$keySalt.$hmacSalt.$iv) + cipher text to the hash_hmac (but I got the same result for the first try [-4301])
$data = base64_encode($dataWithoutHMAC.hash_hmac('SHA256',$dataWithoutHMAC,$_hmacKey, true));
In iOS
decryptionError = nil;
NSData *fromPHPData = [#"AgExMjM0NTY3ODEyMzQ1Njc4WvrmgsFy6IoWNmm2hYL9N2jNVxU13Eo15cRyQRakRZ9WsjZ2CY/B5y4YkmG9uGdB2vHFpmpjsnm3O4d59Ex7Nw==" base64DecodedData];
NSData *fromPHPDecryptedData = [RNDecryptor decryptData:fromPHPData withPassword:#"myKey" error:&decryptionError];
NSLog(#"decryptionError %#", decryptionError);
NSString *fromPHPEcryptedStr = [fromPHPDecryptedData base64EncodedString];
NSLog(#"data : %#", fromPHPEcryptedStr);
the error I got in iOS is : "The operation couldn’t be completed. (net.robnapier.RNCryptManager error -4301.)"
Using PHP 5.4 (mcrypt), RNCryptor 2, iOS 6.
PHP function creates base64 with all headers as referenced from https://github.com/rnapier/RNCryptor/wiki/Data-Format.
PHP decrypt function which can decrypt base64 string from both RNEncryptor and the PHP Encrypt function below return data as expected.
When using RNDecryptor with base64 from PHP Encrypt function below, no data is returned as shown in XCode output below.
PHP Function:
function encrypt($data, $key)
{
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$salt = '12345678';
$_key = $this->pbkdf2('SHA1', $key, $salt, 10000, 32, true);
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $_key, $data, MCRYPT_MODE_CBC, $iv);
$hmac = $this->pbkdf2('SHA1', $key, $salt, 10000, 32, true);
$data = mb_convert_encoding(chr(1).chr(0).$salt.$salt.$iv.$ciphertext.$hmac, "BASE64", "UTF-8");
return $data;
}
PHP Function Call:
encrypt('My Data', 'mykey');
iOS:
NSError * error;
NSData *decryptedData = [RNDecryptor decryptData:[NSString base64DataFromString:#"AQBpcGhvbmU2MmlwaG9uZTYyrYk2rJnaoywktnx6TZ4X3YKgYuEHCL1EHv+/MqIvQMq5BmZOyMJr QSRs9P4uxShsOJOg67VYniUGhHbFNTSl1Q=="]
withPassword:#"mykey"
error:&error];
NSLog(#"data = %#, %#", decryptedData, error);
XCode output:
data = <>, (null)
This is done when I comment out HMAC verification in RNDecryptor -finish, once these section is uncommented I receive a HMAC Mismatch error
data = (null), Error Domain=net.robnapier.RNCryptManager Code=1 "HMAC Mismatch" UserInfo=0x1e564280 {NSLocalizedDescription=HMAC Mismatch}
if (self.hasHMAC) {
NSMutableData *HMACData = [NSMutableData dataWithLength:self.HMACLength];
CCHmacFinal(&_HMACContext, [HMACData mutableBytes]);
if (![HMACData isEqualToData:self.inData]) {
[self cleanupAndNotifyWithError:[NSError errorWithDomain:kRNCryptorErrorDomain
code:kRNCryptorHMACMismatch
userInfo:[NSDictionary dictionaryWithObject:#"HMAC Mismatch"
forKey:NSLocalizedDescriptionKey]]];
return;
}
}
mb_convert_encoding() will do base64 conversion, but it will output chunked base64.
The PHP base64 decoder will accept both chunked and unchunked, but iOS...?
Perhaps you need to just encode:
$data = base64_encode(chr(1).chr(0).$salt.$salt.$iv.$ciphertext.$hmac);
You may want to check out iOS/PHP kCCDecodeError for another implementation.
Finally, from the RNCryptor Wiki Data Format, I see (together with a link to the PHP implementation on Stack Overflow)
HMAC is generated using the ciphertext and the HMACKey (above) and the SHA-256 PRF.
...but the HMAC you append seems to me to actually be the HMACKey, not the HMAC...?
The problem was due to both an incorrect HMAC (was passing the HMAC Key) and PHP encryption needing PKCS7 Padding on the data to be encrypted (not the IV too).
Final PHP function...
function AES256Encrypt($data, $key)
{
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$pad = $block - (strlen($data) % $block);
$data .= str_repeat(chr($pad), $pad);
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$keySalt = '12345678';
$hmacSalt = '12345678';
$_key = $this->pbkdf2('SHA1', $key, $keySalt, 10000, 32, true);
$_hmacKey = $this->pbkdf2('SHA1', $key, $hmacSalt, 10000, 32, true);
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $_key, $data, MCRYPT_MODE_CBC, $iv);
$data = base64_encode(chr(1).chr(0).$keySalt.$hmacSalt.$iv.$ciphertext.hash_hmac('SHA256',$ciphertext,$_hmacKey, true));
return $data;
}