Encrypt iOS and Decrypt Node.js AES

I have searched high and low for a solution which and encrypt on Node.js server and Objective-C client, and vise versa using AES (or other if appropriate)

I am relatively new to cryptography, and understanding why my encrypted text is different in each language is beyond my knowledge.

  • Hide secret key in public repository
  • Issue with pouch.transformation during replication
  • Javascript digital signatures
  • AES equivalent in Ruby openssl?
  • Simplest way to obfuscate and unobfuscate a string in JavaScript
  • Encrypt with CryptoJs and Decrypt with Phalcon
  • This is what I have so far:

    Node.js crypto methods Using this CryptoJS Library – node-cryptojs-aes

    var node_cryptojs = require("node-cryptojs-aes");
    var CryptoJS = node_cryptojs.CryptoJS;
    
        var textToEncrypt = 'Hello';
    var key_clear = 'a16byteslongkey!';
    
    //encrypted + decrypted
    
    var encrypted = CryptoJS.AES.encrypt(clearText, key_clear, { iv: null });
    var decrypted = CryptoJS.AES.decrypt(encrypted, key_clear, { iv: null });
    
    //Outputs   
        console.log("encrypted: " + encrypted);     //encrypted: U2FsdGVkX1/ILXOjqIw2Vvz6DzRh1LMHgEQhDm3OunY=
    console.log("decrypted: " + decrypted.toString(CryptoJS.enc.Utf8));   // decrypted: Hello
    

    Objective-C crypto methods Using AESCrypt library

    NSString* textToEncrypt = @"Hello";
    
    // encrypt
    NSString* encryptedText = [AESCrypt encrypt:textToEncrypt password:@"a16byteslongkey!"];
    
    // decrypt
    NSString* decryptedText = [AESCrypt decrypt:encryptedText password:@"a16byteslongkey!"];
    
    // output
    NSLog(@"Text to encrypt: %@", textToEncrypt);    // Text to encrypt: Hello
    NSLog(@"Encrypted text: %@", encryptedText);     // Encrypted text: wY80MJyxRRJdE+eKw6kaIA==
    NSLog(@"Decrypted text: %@", decryptedText);     // Decrypted text: Hello
    

    I’ve been scratching my head for ages and tried everything I can think of. Can show underlying crypto methods from the libraries if required. There is SHAR256 hash applied to the key in AESCrypt library but I have removed this, and think there is some missmatch with the string encoding.

  • Chrome extension - encrypting data to be stored in chrome storage
  • Issue with pouch.transformation during replication
  • Resolved: Javascript DES encryption/PHP decryption
  • Password encryption at client side
  • JS: Decryption not possible after storing in database (SJCL)
  • “Downloading” a computed value form JavaScript
  • 2 Solutions collect form web for “Encrypt iOS and Decrypt Node.js AES”

    1. Are you sure the same key is being used in both libraries? You say you took out the SHA-256 part in AESCrypt, how is the library using the password parameter now? The AES algorithm can only use keys of 16, 24, or 32 bytes in length. Your password is 16 bytes long, but did you change the corresponding parameter to 128 (instead of 256) in the encrypt function?
      Do you know how CryptoJS is using the key parameter? Are you sure it’s being used directly, or might there be some processing (for example, hashing) before it’s passed to the underlying primitive AES encryption function?

    2. What mode of encryption is the CryptoJS library using? Its documentation doesn’t say. Given that it asks for an IV, it’s probably CBC, but you would have to look at the source to know for sure.
      AESCrypt’s documentation claims to use CBC mode, but you don’t give it an IV anywhere. That must mean that it generates it own somewhere, or always uses a fixed one. (Which half defeats the purpose of CBC mode, but that’s another story). So you need to figure out what the IV actually is.

    TL;DR: unless you make sure that the same key and key length, the same mode, and the same IV are used across both libraries, then you will have different cipher text.

    I’m posting this here because there are bound to be others trying to interop Node.js and iOS. Everyone seems to get hung up on keeping everything in the correct structures, buffers, strings etc. I know I did. So here is a step-by-step process to creating a key, creating an iv, encrypting, decrypting and placing in base64 for easy transfer:

    JavaScript (Node.js using the CryptoJS module)

        // Generate key from password and salt using SHA256 to hash and PDKDF2 to harden
        var password = "1234567890123456";
        var salt = "gettingsaltyfoo!";
        var hash = CryptoJS.SHA256(salt);
        var key = CryptoJS.PBKDF2(password, hash, { keySize: 256/32, iterations: 1000 });
        console.log("Hash :",hash.toString(CryptoJS.enc.Base64));
        console.log("Key :",key.toString(CryptoJS.enc.Base64));
    
        // Generate a random IV
        var iv = CryptoJS.lib.WordArray.random(128/8);
        console.log("IV :",iv.toString(CryptoJS.enc.Base64));
    
        // Encrypt message into base64
        var message = "Hello World!";
        var encrypted = CryptoJS.AES.encrypt(message, key, { iv: iv });
        var encrypted64 = encrypted.ciphertext.toString(CryptoJS.enc.Base64);
        console.log("Encrypted :",encrypted64);
    
        // Decrypt base64 into message again
        var cipherParams = CryptoJS.lib.CipherParams.create({ ciphertext: CryptoJS.enc.Base64.parse(encrypted64) });
        var decrypted = CryptoJS.AES.decrypt(cipherParams, key, { iv: iv }).toString(CryptoJS.enc.Utf8);
        console.log("Decrypted :",decrypted);
    

    iOS SDK using CommonCrypto

        // Generate key from password and salt using SHA256 to hash and PDKDF2 to harden
        NSString* password = @"1234567890123456";
        NSString* salt = @"gettingsaltyfoo!";
        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]);
    
        // Generate a random IV (or use the base64 version from node.js)
        NSString* iv64 = @"ludWXFqwWeLOkmhutxiwHw==";       // Taken from node.js CryptoJS IV output
        NSData* iv = [[NSData alloc] initWithBase64EncodedString:iv64 options:0];
        NSLog(@"IV : %@",[iv base64EncodedStringWithOptions:0]);
    
        // Encrypt message into base64
        NSData* message = [@"Hello World!" dataUsingEncoding:NSUTF8StringEncoding];
        NSMutableData* encrypted = [NSMutableData dataWithLength:message.length + kCCBlockSizeAES128];
        size_t bytesEncrypted = 0;
        CCCrypt(kCCEncrypt,
                kCCAlgorithmAES128,
                kCCOptionPKCS7Padding,
                key.bytes,
                key.length,
                iv.bytes,
                message.bytes, message.length,
                encrypted.mutableBytes, encrypted.length, &bytesEncrypted);
        NSString* encrypted64 = [[NSMutableData dataWithBytes:encrypted.mutableBytes length:bytesEncrypted] base64EncodedStringWithOptions:0];
        NSLog(@"Encrypted : %@",encrypted64);
    
        // Decrypt base 64 into message again
        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,
                iv.bytes,
                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);
    

    The output should be the same

        /*
            Hash : AEIHWLT/cTUfXdYJ+oai6sZ4tXlc4QQcYTbI9If/Moc=
            Key : WdHhJ19dSBURBA25HZSpbCJ4KnNEEgwzqjgyTBqa3eg=
            IV : ludWXFqwWeLOkmhutxiwHw==
            Encrypted : D3JpubesPMgQTiXbaoxAIw==
            Decrypted : Hello World!
        */
    

    Hopefully this saves someone else the time I’ve wasted 🙂