AES256 encryption get different result between node.js and objective-c - ios

1.Node.js
var crypto = require('crypto');
var key = "my password";
var text = "text to encrypt";
var cipher = crypto.createCipher('aes-256-cbc',key);
var crypted =cipher.update(text,'utf8','base64');
crypted+=cipher.final('base64');
Result: ZeYCYOrR/w7qSAZVYht8+Q==
2.Objective-C
{
NSString *key = #"my password";
NSString *text = #"text to encrypt";
NSData *plain = [secret dataUsingEncoding:NSUTF8StringEncoding];
NSData *cipher = [plain AES256EncryptWithKey:key];
NSLog(#"%#\n", [cipher base64Encoding] );
}
Result: raFGdTWYvSPWpkgtF9LJIg==
[AES256EncryptWithKey:] is HERE

The problem is that node.js crypto.createCipher internally uses a key derivation function EVP_BytesToKey() to generate the AES key and iv from the key = "my password". Thus the actual AES keys are different for node.js and Common Crypto.
The answer is to use crypto.createCipheriv(algorithm, key, iv) in place of crypto.createCipher(algorithm, password).
From the node.js documentation:
In line with OpenSSL's recommendation to use pbkdf2 instead of EVP_BytesToKey it is recommended you derive a key and iv yourself with crypto.pbkdf2 and to then use createCipheriv() to create the cipher stream.

Related

How to decrypt a very long string of AES 256 (CBC) encryption in Flutter?

I want to decrypt a very long base64 string of an encrypted JSON file, in which I'm provided with key and iv. And I'm not using the default PKCS7 padding instead I'm keeping the padding null in the encryption part. AES 256 encryption is of CBC mode.
With the help of "encrypt" dart package. I've tried decoding it, but the speed was very, very slow. But the solution I tried was natively configured respectively on Android & iOS, but It wasn't working on that long base64 encrypted string.
//This is some native code I wrote for iOS (Objective-C)
(NSString *)decrypt:(NSString *)text {
NSString *result =
[text stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSData *decodedBase64Data = [[NSData alloc]
initWithBase64EncodedString:result options:0]; //Decode Base64
NSData *decodedAESData = [decodedBase64Data
AES256DecryptedDataWithKey:rd_key iv:rd_iv]; //Decode AES 256
result = [[NSString alloc] initWithData:decodedAESData
encoding:NSUTF8StringEncoding];
return result;
}
Is there any faster or a native solution to this problem?
as I'm not able to decrypt the whole base64 encrypted string.
Base64 encypted String = "...2Btn9j6DjFOFgN2gUgVWv8!2FE94hmTZqge6wFMWCMBwRKu!
2BhaUSbip!2Brxa0tF5JqzVukbz4cbWZE6NGNgrA5BAeNHh3RVFBuoOG!2F!2Fiw!2BmsUob4W5ab9
P0lnHhPU9hk2qlfbj1ydS6m9OYYFwl!2F1QXAJ2I6lVZ81HYzeupP42!2BxqIx3zyURHZwQsCuOZ!
2BYits!2FqBC!2BSvAfiCUCeEOSqaAaLjbxXNAaIrtHRh3B9boAPESTxTKzUSC!2Bhpa72xk7
Dby8cl0MlflPAc9P2ihO3SD5GNVm9HWIjMga41OUvw1xMsgqqe5G5YnsNOom57cnLkj
JJhxsnfgnqaKxpAK0fdsov3f6!2FRDAVrSQledg4PWpQpNw9PCmIpzvk6!2wCbHck8
OlJm!2Br7NI9FNTfhJ2YceXohNkAZPkWW!2F2j9egRHNhbZNxGZoFnVE!2FgP1Zyqd6R
8Ma3b!2BhqCCWT4ZoSXXals26jVtkCMU5NA!2FWfxi6LbbsukyS9ViNj3gP8eCUH1!2Fbq
nLWXRfN03yqDcjM9tTFvSjorbqvf0sI0Go7crwJGaX7ODB4C!2FWcZ8plUTmqTZI!2BNUE
QKlxvGd24Vf!2B!2Br5ZvzvcM3Ew3CEw4JM1IZb30mqFEgFAt8dv08fj!2FRsQYt8PfK!2
r0ucWIAXVNz0ZySX6lgSza0ZHMSot!2BpUzt0Ru8vUuALL1g5xoEgh1l4yyjs2Dmxz4mJ4
0PNxdvye1FIjY!...";
Decrypted data should be like this!

AES encryption on iOS with base64 key from CryptoJS

I'm generating and exporting a key with CryptoJS:
const password = crypto.lib.WordArray.random(128 / 8);
const salt = crypto.lib.WordArray.random(128 / 8);
const encryptionKey = crypto.PBKDF2(password, salt, {keySize: 128 / 32});
return encryptionKey.toString();
Now I'm trying to encrypt some data with the key on iOS:
const char *s = [encryptionKey cStringUsingEncoding:NSASCIIStringEncoding];
NSData *keyData= [NSData dataWithBytes:s length:strlen(s)];
NSMutableData *ivData = [NSMutableData dataWithLength:kCCBlockSizeAES128];
SecRandomCopyBytes(kSecRandomDefault, kCCBlockSizeAES128, ivData.mutableBytes);
NSData *iv = [NSData dataWithData:ivData];
size_t outLength;
NSMutableData *cipherData = [NSMutableData dataWithLength:dataString.length + kCCBlockSizeAES128];
CCCrypt(kCCEncrypt, // operation
kCCAlgorithmAES128, // Algorithm
kCCOptionPKCS7Padding, // options
keyData.bytes, // key
keyData.length, // keylength
iv.bytes,// iv
jsonData.bytes, // dataIn
jsonData.length, // dataInLength,
cipherData.mutableBytes, // dataOut
cipherData.length, // dataOutAvailable
&outLength); // dataOutMoved
cipherData.length = outLength;
NSString *cipherText = [cipherData base64EncodedStringWithOptions:NSUTF8StringEncoding];
NSString *ivText = [iv base64EncodedStringWithOptions:NSUTF8StringEncoding];
return [ivText stringByAppendingString:cipherText]
This all works so far. Trying to decrypt the data with CryptoJS however fails:
const iv = crypto.enc.Base64.parse(message.substr(0, 24));
const encrypted = crypto.enc.Base64.parse(message.substring(24));
const decrypted = crypto.AES.decrypt(encrypted, encryptionKey, {
iv: iv,
padding: crypto.pad.Pkcs7,
mode: crypto.mode.CBC
});
console.log(decrypted.toString(crypto.enc.Utf8))
The problem seems to be in the passing of the key from CryptoJS to iOS. What is the correct format to pass to CCCrypt?
The option to base64EncodedStringWithOptions is incorrect and will add line ending characters to the Base64 encoded iv and encrypted data.
You do not want line endings options, by default, no line endings are inserted. Just specify 0:
NSString *cipherText = [cipherData base64EncodedStringWithOptions:0];
NSString *ivText = [iv base64EncodedStringWithOptions:0];
The option Note that NSUTF8StringEncoding is not an encoding option for the method base64EncodedStringWithOptions. The options are:
NSDataBase64Encoding64CharacterLineLength
NSDataBase64Encoding76CharacterLineLength
NSDataBase64EncodingEndLineWithCarriageReturn
NSDataBase64EncodingEndLineWithLineFeed`
which are all line separator options.
My original code contained three errors.
The resulting strings need to be encoded using no parameter as suggested by zaph:
NSString *cipherText = [cipherData base64EncodedStringWithOptions:0];
NSString *ivText = [iv base64EncodedStringWithOptions:0];
To correctly convert the encryption key to NSData, I use the method provided here and call it like this:
NSData *keyData= [self dataFromHexString:encryptionKey];
The decrypt function of CryptoJS requires an object like this:
const encrypted = crypto.enc.Base64.parse(message.substring(24));
const params = {
ciphertext: encrypted,
salt: ''
};
const decrypted = crypto.AES.decrypt(params, crypto.enc.Hex.parse(this.encryptionKey.toString()), {
iv: iv,
padding: crypto.pad.Pkcs7,
mode: crypto.mode.CBC
});
return decrypted.toString(crypto.enc.Utf8);
Thanks for your help!

AES Encryption / Decryption Xcode

I am facing error in AES encrypt and decrypt
I am using these CODE
And using in my viewcontroller like this // read show error comment in code
Please fix how to use it?
#import "CryptLib.h"
#import "NSData+Base64.h"
-(NSData *)encryption:(NSString*)myString
{
NSString * secret = #"This the sample text has to be encrypted"; // this is the text that you want to encrypt.
NSString * key = #"shared secret"; //secret key for encryption. To make encryption stronger, we will not use this key directly. We'll first hash the key next step and then use it.
key = [[CryptLib alloc] sha256:key length:32]; //this is very important, 32 bytes = 256 bit
NSString * iv = [[[[CryptLib alloc] generateRandomIV:11] base64EncodingWithLineLength:0] substringToIndex:16]; //Here we are generating random initialization vector (iv). Length of this vector = 16 bytes = 128 bits //SHOWS ERROR HERE
// Now that we have input text, hashed key and random IV, we are all set for encryption:
NSData * encryptedData = [[CryptLib alloc] encrypt:[secret dataUsingEncoding:NSUTF8StringEncoding] key:key iv:iv];
NSLog(#"encrypted data:: %#",[encryptedData base64EncodingWithLineLength:0]); //print the encrypted text //SHOWS ERROR HERE
// Encryption = [plainText + secretKey + randomIV] = Cyphertext
return encryptedData;
}

Encrypt and Decrypt iOS/Node.js Security Inquiry

I'm currently using AES128 on both platforms and my code from this answer
Note: I changed the code a bit to deviate from using an IV because I thought it was overkill for the purpose of my application.
node.js:
var CryptoJS = require("crypto-js");
var crypto = require('crypto');
var password = "1234567890123456";
var salt = "gettingsaltyfoo!";
var hash = CryptoJS.SHA256(salt);
var key = CryptoJS.PBKDF2(password, hash, { keySize: 256/32, iterations: 1000 });
var algorithm = 'aes128';
console.log(key.toString(CryptoJS.enc.Base64));
function encrypt(text){
var cipher = crypto.createCipher(algorithm,key.toString(CryptoJS.enc.Base64));
var crypted = cipher.update(text,'utf8','hex');
crypted += cipher.final('hex');
return crypted;
}
function decrypt(text){
var decipher = crypto.createDecipher(algorithm,key.toString(CryptoJS.enc.Base64));
var dec = decipher.update(text,'hex','utf8');
dec += decipher.final('utf8');
return dec;
}
iOS:
#import <CommonCrypto/CommonCrypto.h>
NSString* password = #"1234567890123456";
NSString* salt = #"gettingsaltyfoo!";
-(NSString *)decrypt:(NSString*)encrypted64{
NSMutableData* hash = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
NSMutableData* key = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
CC_SHA256(salt.UTF8String, (CC_LONG)strlen(salt.UTF8String), hash.mutableBytes);
CCKeyDerivationPBKDF(kCCPBKDF2, password.UTF8String, strlen(password.UTF8String), hash.bytes, hash.length, kCCPRFHmacAlgSHA1, 1000, key.mutableBytes, key.length);
NSLog(#"Hash : %#",[hash base64EncodedStringWithOptions:0]);
NSLog(#"Key : %#",[key base64EncodedStringWithOptions:0]);
NSData* encryptedWithout64 = [[NSData alloc] initWithBase64EncodedString:encrypted64 options:0];
NSMutableData* decrypted = [NSMutableData dataWithLength:encryptedWithout64.length + kCCBlockSizeAES128];
size_t bytesDecrypted = 0;
CCCrypt(kCCDecrypt,
kCCAlgorithmAES128,
kCCOptionPKCS7Padding,
key.bytes,
key.length,
NULL,
encryptedWithout64.bytes, encryptedWithout64.length,
decrypted.mutableBytes, decrypted.length, &bytesDecrypted);
NSData* outputMessage = [NSMutableData dataWithBytes:decrypted.mutableBytes length:bytesDecrypted];
NSString* outputString = [[NSString alloc] initWithData:outputMessage encoding:NSUTF8StringEncoding];
NSLog(#"Decrypted : %#",outputString);
return outputString;
}
-(NSString *)encrypt:(NSString *)toEncrypt{
NSMutableData* hash = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
NSMutableData* key = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
CC_SHA256(salt.UTF8String, (CC_LONG)strlen(salt.UTF8String), hash.mutableBytes);
CCKeyDerivationPBKDF(kCCPBKDF2, password.UTF8String, strlen(password.UTF8String), hash.bytes, hash.length, kCCPRFHmacAlgSHA1, 1000, key.mutableBytes, key.length);
NSData* message = [toEncrypt dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData* encrypted = [NSMutableData dataWithLength:message.length + kCCBlockSizeAES128];
size_t bytesEncrypted = 0;
CCCrypt(kCCEncrypt,
kCCAlgorithmAES128,
kCCOptionPKCS7Padding,
key.bytes,
key.length,
NULL,
message.bytes, message.length,
encrypted.mutableBytes, encrypted.length, &bytesEncrypted);
NSString* encrypted64 = [[NSMutableData dataWithBytes:encrypted.mutableBytes length:bytesEncrypted] base64EncodedStringWithOptions:0];
NSLog(#"Encrypted : %#",encrypted64);
return encrypted64;
}
MY QUESTION: Is it okay if I hardcode the salt like this? I'm trying to encrypt and decrypt the password (the var password and NSString password will probably be hardcoded into something). I've read online that I need to keep my salt with my password in my db. If it's not okay if I hardcode my salt, how do I send it from iOS to node.js and be consistent with the salt? Should my iOS request look like this?
{
key:"someKeyGeneratedOnTheSpotWithRandomSalt",
password:"somePasswordGeneratedFromKey"
}
and in my backend check the password by pulling these fields from the database?
{
key:"someKeyGeneratedWhenTheUserFirstSignedUp",
password:"somePasswordGeneratedFromTheOrginalKeyWhenUserFirstSignedUp"
}
And then decrypt both passwords using the key and password generated from both scenarios?
OR is it okay to have a hardcoded salt, say the username, so that way the key is always the same per user?
Basically I'm confused on whether or not I have the right idea for my encryption model.
Thanks for any assistance.
Typically a random salt is used and prepended to the encrypted data. It is also common to all prepend the PBKDF2 iteration count along with a version number helps for future-proofing. Finally, skipping an iv reduces the protection of the first block and you might consider an authentication hash.
This is similar to what RNCryptor does. See RNCryptor-Spec-v3.md for a detail of a encrypted message.
Notes:
I don't understand CC_SHA256 of the salt, that shouldn't be necessary.
NSData* outputMessage = [NSMutableData dataWithBytes:decrypted.mutableBytes
length:bytesDecrypted];
is unnecessary, just set the length of decrypted
decrypted.length = bytesDecrypted;
and use decrypted in place of outputMessage.

Objective C Google Maps API public key function

At the moment I'm having an issue with producing a public key for a given Google Maps Web Services API query.
The documentation stipulates that the signature must be produced with a modified base-64 HMAC-SHA1 hash, on the path and query part of the URL.
However using this function, and testing it with this tool shows that it isn't working correctly.
+ (NSString *) hmac:(NSString *)data withKey:(NSString *)key{
const char *cKey = [key cStringUsingEncoding:NSUTF8StringEncoding];
const char *cData = [data cStringUsingEncoding:NSUTF8StringEncoding];
unsigned char cHMAC[CC_SHA1_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
NSData *HMAC = [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
NSString *hash = [HMAC base64EncodedStringWithOptions:0];
hash = [hash stringByReplacingOccurrencesOfString:#"+" withString:#"-"];
hash = [hash stringByReplacingOccurrencesOfString:#"/" withString:#"_"];
return hash;
}
I am calling this function where data has been precent encoded;
[urlString stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
Where am I going wrong? Any help greatly appreciated.
You can use this for your url signing : https://github.com/youssman/GMUrlSigner
I am interested in your feedback and comments ;-)
Google has a sample Objective C code - is there any particular reason that you are not using them and some of the google functions in your code? In particular the equivalence of CKey is not plain 7 bit asCII (as coded in your sample) but Base64 (rfc4648Base64WebsafeStringEncoding). For the unfamiliar, Base64 is typically uses in mime Mail to allow attachments to go through plain text email years ago (prior to HTML Mail) problem is if google thinks that your key is base 64 and it is not, the decoding will change your key - example base64 abcedfg = iy in ascii. ... so google is not getting the correct decoding key from your URL.
There are other differences but I guess most of them are just format.

Resources