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.)"
Related
I'm currently implementing encryption on my API. I have to implement this the same way it is encrypted and decrypted in a specific script written in PHP.
This works all fine when I output the result in, let's say, base64. The PHP decrypt method runs against it and works just fine. The problem is that I need to output not in base64 but in binary due to certain requirements, but when I encrypt from the Nodejs side into binary the result is different than when I encrypt in the PHP into binary when it should be the same.
PHP Encryption:
function vd_encrypt($plaintext, $password) {
$method = "AES-256-CBC";
$key = hash('sha256', $password, true);
$iv = openssl_random_pseudo_bytes(16);
$ciphertext = openssl_encrypt($plaintext, $method, $key, OPENSSL_RAW_DATA, $iv);
$hash = hash_hmac('sha256', $ciphertext, $key, true);
return $iv . $hash . $ciphertext;
}
Javascript Encryption:
import crypto from 'crypto';
export function encrypt (plain_text: string): string {
const encryptionMethod = 'AES-256-CBC';
const iv = crypto.randomBytes(IV_LENGTH);
const key = crypto.createHash("sha256").update(secret).digest();
var encryptor = crypto.createCipheriv(encryptionMethod, key, iv);
const result = iv + encryptor.update(plain_text, 'utf8', 'binary') + encryptor.final('binary');
return result;
}
I've updated your code slightly to accept an iv parameter. You can generate this in the same way as before (e.g. openssl_random_pseudo_bytes).
For the purposes of demonstration I'll use a fixed IV so we can show the same result.
PHP
function vd_encrypt($plaintext, $password, $iv) {
$method = "AES-256-CBC";
$key = hash('sha256', $password, true);
$ciphertext = openssl_encrypt($plaintext, $method, $key, OPENSSL_RAW_DATA, $iv);
$hash = hash_hmac('sha256', $ciphertext, $key, true);
return $iv . $hash . $ciphertext;
}
// Replace with below code in production
// $iv = openssl_random_pseudo_bytes(16);
$iv = base64_decode("eQMrc61Gt8qRejRjhJOkVw==");
$result = vd_encrypt("He was a man take him for all in all, I shall not look upon his like again", "password", $iv);
echo "Result (base64): " . base64_encode($result) . "\n";
Node.js
import crypto from 'crypto';
export function encrypt (plaintext: string, password: string, iv: string): string {
const encryptionMethod = 'AES-256-CBC';
const key = crypto.createHash("sha256").update(password).digest();
const encryptor = crypto.createCipheriv(encryptionMethod, key, iv);
const encryptedData = Buffer.concat([encryptor.update(plaintext, 'utf8'), encryptor.final()]);
const hash = crypto.createHmac("sha256", key).update(encryptedData).digest();
return Buffer.concat([iv, hash, encryptedData]);
}
// Replace with below code in production
//const iv = crypto.randomBytes(16);
const iv = Buffer.from("eQMrc61Gt8qRejRjhJOkVw==", "base64");
const result = encrypt("He was a man take him for all in all, I shall not look upon his like again", "password", iv);
console.log("Result (base64):", result.toString("base64"));
In this case the results will be like so:
PHP:
Result (base64): eQMrc61Gt8qRejRjhJOkVxsqZTqUjSUnaL46yZDLGGK5+o7WKLyIiG4UKj0ST93Wi7UlaAyTFIjpIs0C893SFsnHeuVshG+6EJF99GrLSUCMFJG3J1pJnmxF4Pu8ZCbN7Ounp0BjhJKIpu9yQn6uEYylJLXWpzNw+aCwsnIV1h0=
Node.js:
Result (base64): eQMrc61Gt8qRejRjhJOkVxsqZTqUjSUnaL46yZDLGGK5+o7WKLyIiG4UKj0ST93Wi7UlaAyTFIjpIs0C893SFsnHeuVshG+6EJF99GrLSUCMFJG3J1pJnmxF4Pu8ZCbN7Ounp0BjhJKIpu9yQn6uEYylJLXWpzNw+aCwsnIV1h0=
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.
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.
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;
}
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.