Php and iOS Encryption RNCryptor - php

I have been trying to encrypt data from php and send it to iOS (I use the RNCryptor library).
I have read through the github Data-Format
and developed this algorithm on php.
function encrypt($data, $key)
{
$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 = pbkdf2('SHA1', $key, $keySalt , 10000, 32, true);
$_hmacKey = pbkdf2('SHA1', $key, $hmacSalt, 10000, 32, true);
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $_key, $data, MCRYPT_MODE_CBC, $iv);
$data = base64_encode(chr(2).chr(0).$keySalt.$hmacSalt.$iv.$ciphertext.hash_hmac('SHA256',$ciphertext,$_hmacKey, true));
return $data;
}
However, when the Data is sent to the php. I received such error in the iOS debugging tool:
Error Domain=net.robnapier.RNCryptManager Code=-4300 "The operation coulnd't be completed. (net.robnapier.RNCryptManager error - 4300.)";
I've look at many website. I've also followed the github website step-by-step, but still unable to find the right answer.

Related

Encryption and Decryption for Triple DES in PHP Example in ECB Mode

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

Preparing for removal of Mcrypt in PHP 7.2

So as time moves on mcrypt will go in PHP 7.2.
Of course there is an alternative: openssl.
I find it difficult to switch from mcrypt to openssl, using AES 256 CBC and preserving IVs. I am sort of new to cryptography, so I don't really know everything, but I understand the basics.
Let's say I have the following code
function encrypt($masterPassword, $data)
{
$keySize = mcrypt_get_key_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$ivSize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($ivSize, MCRYPT_DEV_URANDOM);
$key = mb_substr(hash('SHA256', $masterPassword), 0, $keySize);
$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $data, MCRYPT_MODE_CBC, $iv);
return base64_encode($iv . $encrypted);
}
function decrypt($masterPassword, $base64)
{
$keySize = mcrypt_get_key_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$ivSize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$key = mb_substr(hash('SHA256', $masterPassword), 0, $keySize);
$data = base64_decode($base64);
$iv = substr($data, 0, $ivSize);
$encrypted = substr($data, $ivSize, strlen($data));
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted, MCRYPT_MODE_CBC, $iv);
return trim($decrypted);
}
How can I "convert" this code to use openssl insted of mcrypt?
You can't convert it, because Rijndael-256 is not AES-256, and the OpenSSL extension doesn't ship with Rijndael-256 support.
AES-256 is Rijndael-128 with a 256-bit (32-byte) key.
Unfortunately, you'll have to re-encrypt all of your data.
Edit: Also, the scheme you're currently using has some problems:
It lacks authentication (HMACs are the easiest way to do it in PHP)
It lacks proper padding (mcrypt pads with zero bytes; you need something like PKCS#5 padding instead), which is required for block mode encryption to be safe.
It's not byte-safe (you're using mb_substr())
The good news is that OpenSSL will do PKCS#5 padding for you automatically, but you should go even further and use a solid encryption library like defuse/php-encryption.

Decryption on PHP have problems after 16 digits

I am doing AES encryption on ios End, and i the base64 encode that string and send over to php end. On the php end, i have following code:
<?php
$key = 'a16byteslongkey!';
$data = base64_decode('LsCH4nvvGPKN67v94Ig9BweQgOk9rtDdK7ZugeJkTS8=');
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
$iv = substr($data, 0, $iv_size);
function aes256_cbc_decrypt($key, $data, $iv) {
if(32 !== strlen($key)) $key = hash('SHA256', $key, true);
if(16 !== strlen($iv)) $iv = hash('MD5', $iv, true);
$data = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_ECB, $iv);
$padding = ord($data[strlen($data) - 1]);
return substr($data, 0, -$padding);
}
$result = aes256_cbc_decrypt($key,$data,$iv);
var_dump($result);
?>
But when i run this code i get this "anil.mnd#gmail.cA���u�" . I should have got anil.mnd#gmail.com. I get only first 16 characters correct.
I am new to encryption so not have much idea what is wrong.
I am new to encryption so not have much idea what is wrong.
If you're new and want something that "just works", use defuse/php-encryption instead of trying to write it yourself.
If you're up for the challenge, however, keep reading:
Your code is unreadable. Let's add some whitespace.
$key = 'a16byteslongkey!';
$data = base64_decode('LsCH4nvvGPKN67v94Ig9BweQgOk9rtDdK7ZugeJkTS8=');
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
$iv = substr($data, 0, $iv_size);
function aes256_cbc_decrypt($key, $data, $iv)
{
if (32 !== strlen($key)) {
$key = hash('SHA256', $key, true);
}
if (16 !== strlen($iv)) {
$iv = hash('MD5', $iv, true);
}
$data = mcrypt_decrypt(
MCRYPT_RIJNDAEL_128,
$key,
$data,
MCRYPT_MODE_ECB,
$iv
);
$padding = ord($data[strlen($data) - 1]);
return substr($data, 0, -$padding);
}
$result = aes256_cbc_decrypt($key,$data,$iv);
var_dump($result);
Specific problems:
You're using MCRYPT_MODE_ECB for a function named aes256_cbc (have you seen the penguin?)
When I switch that out, I get invalid data.
Your encryption method is also probably broken, since changing your IV to "\x00\x00"... makes it decrypt.
Specific recommendations:
Please, please, PLEASE consider using well-studied cryptography code instead of writing it yourself.
strlen() and substr() are brittle. See: function overloading.
Use a real key derivation function, not a hash function.
Your IV (and keys, for that matter) should be generated from a cryptographically secure pseudo-random number generator, such as random_bytes().
Use authenticated encryption.

How to Encrypt-Decrypt .pdf, .docx files in php?

I am trying to encrypt/decrypt files in PHP. So far I am successful with .txt files but when it comes to .pdf and .doc or .docx my code fails, i.e. it gives absurd results. Can anyone suggest modification/alternative in my code? Thanks in advance!
Here's the encryption function
function encryptData($value)
{
$key = "Mary has one cat";
$text = $value;
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_ECB, $iv);
return $crypttext;
}
Here's the decryption function
function decryptData($value)
{
$key = "Mary has one cat";
$crypttext = $value;
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$decrypttext = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $crypttext, MCRYPT_MODE_ECB, $iv);
return trim($decrypttext);
}
I used this blog to help me encrypt/decrypt pdf files on my local machine using openssl_encrypt because mcrypt is deprecated in php7.
First, you get the file contents of the pdf:
$msg = file_get_contents('example.pdf');
Then I called the encryption function written in the blog post:
$msg_encrypted = my_encrypt($msg, $key);
Then I open the file I want to write to and write the new encrypted msg:
$file = fopen('example.pdf', 'wb');
fwrite($file, $msg_encrypted);
fclose($file);
For reference, in case that blog goes down, here are the encryption and decryption functions from the blog:
$key = 'bRuD5WYw5wd0rdHR9yLlM6wt2vteuiniQBqE70nAuhU=';
function my_encrypt($data, $key) {
// Remove the base64 encoding from our key
$encryption_key = base64_decode($key);
// Generate an initialization vector
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
// Encrypt the data using AES 256 encryption in CBC mode using our encryption key and initialization vector.
$encrypted = openssl_encrypt($data, 'aes-256-cbc', $encryption_key, 0, $iv);
// The $iv is just as important as the key for decrypting, so save it with our encrypted data using a unique separator (::)
return base64_encode($encrypted . '::' . $iv);
}
function my_decrypt($data, $key) {
// Remove the base64 encoding from our key
$encryption_key = base64_decode($key);
// To decrypt, split the encrypted data from our IV - our unique separator used was "::"
list($encrypted_data, $iv) = explode('::', base64_decode($data), 2);
return openssl_decrypt($encrypted_data, 'aes-256-cbc', $encryption_key, 0, $iv);
}

PHP AES256 Encryption => RNCryptor RNDecryptor returns blank / HMAC Mismatch

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

Categories