AES-256在iOS和Node.js中使用相同的密码

时间:2015-10-13 07:30:19

标签: javascript objective-c node.js encryption aes

所以我有我的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;
}

我有我的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';

function encrypt(text){
  var cipher = crypto.createCipher(algorithm,key.toString(CryptoJS.enc.Base64));
  var crypted = cipher.update(text,'utf8','hex');
  crypted += cipher.final('hex');
  console.log(crypted);
  console.log(hash.toString(CryptoJS.enc.Base64));
  console.log(key.toString(CryptoJS.enc.Base64));
  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');
  console.log(dec);
  return dec;
}

问题:不幸的是,虽然我有相同的哈希,密钥和最终解密的值(意味着它们可以独立工作),但我获得了不同的加密值。所以在一个代码中,如果我采用加密值并尝试在另一个代码中解密它,我会收到错误。当我从iOS转到节点时,我收到此错误:

  

错误:数字包​​络例程:EVP_DecryptFinal_ex:错误的最终块长度CODE:decrypt(&#39; vfOzya0yV9G5hLHeSh3R1g ==&#39;);

此外,我为字符串&#34; Hello World&#34;:

获取了这些不同的加密值
IOS: vfOzya0yV9G5hLHeSh3R1g==
NODE: 13b51a6785f47d8601c3a612d41b9a8b

如何解决此问题,以便我可以互操作我的iOS和Node.js,以及将来的Android。我知道我的哈希算法适合生成SHA256和PBDKF2,因为我得到相同的哈希和密钥。这意味着在加密我的密码时,我的实现对于AES128是错误的。很可能是我的iOS代码。请让我知道我的错误在哪里。

1 个答案:

答案 0 :(得分:1)

您不需要使用CryptoJS,因为node.js&#39; crypto module提供了您工作所需的一切。 CryptoJS具有与node.js不同的二进制表示。原生缓冲区,因此在使用两者时会出现问题。

问题:

  • 您正在使用crypto.createCipher(),它将以OpenSSL兼容格式自行从密码派生密钥。您想使用crypto.createCipheriv()
  • 您没有在Objective-C中传递IV,默认为零填充IV。您需要在node.js中通过初始化零填充缓冲区来执行相同操作。
  • 您在node.js中提供Base64编码格式的密钥,但您必须提供字节(缓冲区)。
  • 由于密钥大小为256位,您实际上使用的是AES-256而不是AES-128。尽管指定了128位,但CommonCrypto代码似乎会自动更改为256位,但node.js要求您明确指定256位。此外,&#34; aes128&#34;或&#34; aes256&#34;将在node.js中默认为ECB模式,但CommonCrypto默认为CBC模式,因此您需要明确指定。

完整的工作代码:

var crypto = require('crypto');
var password = "1234567890123456";
var salt = "gettingsaltyfoo!";

var sha256 = crypto.createHash("sha256");
sha256.update(salt);
var hash = sha256.digest();

var key = crypto.pbkdf2Sync(password, hash, 1000, 32, "sha1");

var iv = new Buffer(16);
iv.fill(0);

var algorithm = 'aes-256-cbc';

function encrypt(text){
  var cipher = crypto.createCipheriv(algorithm, key, iv);
  var crypted = cipher.update(text,'utf8','base64');
  crypted += cipher.final('base64');
  return crypted;
}

function decrypt(text){
  var decipher = crypto.createDecipheriv(algorithm, key, iv);
  var dec = decipher.update(text,'base64','utf8');
  dec += decipher.final('utf8');
  return dec;
}

console.log(encrypt("Hello World"));

输出:

vfOzya0yV9G5hLHeSh3R1g==

其他考虑因素:

  • 您需要为您执行的每次加密生成随机IV。如果您不这样做,那么如果您每次都使用相同的密钥,则攻击者可能会看到您多次加密相同的邮件而未实际解密。由于您从密码派生密钥,因此您可以通过生成随机盐并从PBKDF2派生384位(48字节)来更好地完成此操作。将前32个字节用于密钥,其余部分用于IV。

  • 您需要对密文进行身份验证。如果你没有,那么攻击者可能会在你的系统上安装padding oracle攻击。您可以通过在密文上运行HMAC并随之发送生成的标记来轻松完成此操作。然后,您可以通过在收到的密文上再次运行HMAC来验证解密之前的标签,以便检查操作。
    或者您可以使用像GCM这样的身份验证模式。