iOS/Node.js安全查询:加密和解密

4

我目前在两个平台上都使用AES128,代码来源于这个回答
注意:我稍微改变了代码,不使用IV是因为我认为它对我的应用程序来说过于复杂。

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

我的问题:我这样硬编码盐可以吗?我正在尝试加密和解密密码(变量密码和NSString密码可能会被硬编码到某个东西中)。我在网上读到需要将盐与密码一起保存在我的数据库中。如果不能硬编码盐,那么我应该如何将其从iOS发送到node.js并保持盐的一致性?我的iOS请求是否应该像这样?

{
key:"someKeyGeneratedOnTheSpotWithRandomSalt",
password:"somePasswordGeneratedFromKey"
}

在我的后端中,通过从数据库中提取这些字段来检查密码?

{
key:"someKeyGeneratedWhenTheUserFirstSignedUp",
password:"somePasswordGeneratedFromTheOrginalKeyWhenUserFirstSignedUp"
}

然后使用从两种情况生成的密钥和密码解密两个密码?

或者硬编码盐值,例如用户名,这样每个用户的密钥都是相同的,这样可以吗?

基本上我对我的加密模型是否正确感到困惑。

感谢任何帮助。


你真的不应该在应用程序中硬编码密码或编码密钥。这并没有提供实际的安全性,只是混淆手段而已。你可以在应用程序中硬编码一个RSA公钥,并从中与服务器建立一个短暂的秘密密钥。如果你使用TLS与证书固定功能会更好。 - Artjom B.
1个回答

1
通常会使用随机盐并将其添加到加密数据前面。还通常会在PBKDF2迭代计数之前添加版本号以帮助未来。最后,跳过iv会降低第一个块的保护性,您可以考虑使用身份验证哈希。
这类似于RNCryptor所做的事情。有关加密消息详细信息,请参见RNCryptor-Spec-v3.md
注: 我不理解盐的CC_SHA256,这是不必要的。 NSData *outputMessage = [NSMutableData dataWithBytes:decrypted.mutableBytes length:bytesDecrypted]; 是不必要的,只需设置decrypted的长度 decrypted.length = bytesDecrypted; 并使用decrypted替换outputMessage

所以我应该在用户注册时将加密密码的密钥存储在数据库中。然后每当用户登录时,我会使用另一个随机盐重新加密它,发送新的密钥和新密码,并与旧密钥和旧密码进行比较。这样正确吗?我可能会稍后决定添加一个iv,我只是想快速入门。 - user2977578
我还没有真正检查过数据库部分,我会看一下的。如果你要“快速起步”,请确保在加密数据中添加版本号,否则你将被锁定。考虑一下是否真的会有后续更新。 - zaph
你的意思是类似于 key = "1.0" + key; 这样吗? - user2977578
不,加密数据之前不要添加任何内容,直接进行Base64编码。 - zaph
你能帮我找出为什么我的iOS和Node在加密数据方面不同吗?它们都有相同的密钥、哈希和解密值,但加密数据却不同,但我很确定两者都使用了AES 128。IOS:vfOzya0yV9G5hLHeSh3R1g= 和 NODE:8fd7cd38dd715f9269f9eb40ecb43ab9。 - user2977578
我也尝试在节点中解密iOS,但没有成功。错误信息为:数字信封例程:EVP_DecryptFinal_ex:错误的最终块长度。代码如下: decrypt('vfOzya0yV9G5hLHeSh3R1g=='); - user2977578

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接