web-dev-qa-db-ja.com

EVP_DecryptFinal_ex:Node.js使用時の復号化が正しくありません

次のノードjsを使用します。

var crypto = require('crypto');
var encrypt = function (input, password, callback) {
    var m = crypto.createHash('md5');
    m.update(password);
    var key = m.digest('hex');

    m = crypto.createHash('md5');
    m.update(password + key);
    var iv = m.digest('hex');
    console.log(iv);

    var data = new Buffer(input, 'utf8').toString('binary');

    var cipher = crypto.createCipheriv('aes-256-cbc', key, iv.slice(0,16));
    var encrypted = cipher.update(data, 'binary') + cipher.final('binary');
    var encoded = new Buffer(encrypted, 'binary').toString('base64');
    callback(encoded);
};

var decrypt = function (input, password, callback) {
    // Convert urlsafe base64 to normal base64
    input = input.replace(/\-/g, '+').replace(/_/g, '/');
    // Convert from base64 to binary string
    var edata = new Buffer(input, 'base64').toString('binary');

    // Create key from password
    var m = crypto.createHash('md5');
    m.update(password);
    var key = m.digest('hex');

    // Create iv from password and key
    m = crypto.createHash('md5');
    m.update(password + key);
    var iv = m.digest('hex');

    // Decipher encrypted data
    var decipher = crypto.createDecipheriv('aes-256-cbc', key, iv.slice(0,16));
    var decrypted = decipher.update(edata, 'binary') + decipher.final('binary');
    var plaintext = new Buffer(decrypted, 'binary').toString('utf8');

    callback(plaintext);
};

実行するために私はこれを実行しました:

encrypt("uWeShxRrCKyK4pcs", "secret", function (encoded) {
    console.log(encoded);
    decrypt(encoded, "secret", function (output) {
        console.log(output);
    });
});

暗号化は正常に機能しているようですが、復号化しようとすると、次のエラーが発生します。

エラー:エラー:06065064:デジタルエンベロープルーチン:EVP_DecryptFinal_ex:Decipheriv.Cipher.final(crypto.js:202:26)のエラー(ネイティブ)での不正な復号化

私は暗号化にかなり慣れていないので、なぜこのエラーが発生するのかよくわかりません。今のところ修正する必要があります。

9
Sonu Kapoor

2つの異なるエンコーディングを混同しました。見る

cipher.update(data[, input_encoding][, output_encoding])

そして

cipher.final([output_encoding])

そして今見て

_var encrypted = cipher.update(data, 'binary') + cipher.final('binary');
_

しかし、それは

_var encrypted = cipher.update(data, 'binary', 'binary') + cipher.final('binary');
_

問題は、cipher.update(data, 'binary')が、「バイナリ」文字列ではなく、16進数でエンコードされた文字列に自動的に文字列化するバッファを出力することです。


とにかく、このコードには非常に多くの間違いがあるので、最初からやり直して、非常に意見の高い既存のライブラリを使用する必要があります。

  • あなたは、セマンティックセキュリティに到達するために暗号文の前にランダムなIVを追加する必要があります

  • パスワードはエントロピーが低く、キーとして使用できません。 1回のMD5呼び出しで、その事実は変わりません。パスワードからの鍵の導出は遅いと思われるため、PBKDF2、bcrypt、scrypt、Argon2(セキュリティの強化)などの既知のスキームを使用し、反復回数/コスト係数を高くします。塩を忘れないでください。

  • 暗号化してからMACスキームで、HMAC-SHA256などのメッセージ認証コードを使用して暗号文を認証します。そうしないと、攻撃者が暗号文を操作する可能性があり、変更を検出することさえできなくなります。パディングOracle攻撃でデータを失うための最初のステップ。

7
Artjom B.

私はこれの理由を見つけました、暗号化と復号化のために異なるキーまたはivを使用しています。コンテンツを復号化するには、暗号化に使用したものと同じキーとivを使用する必要があります。唯一の回避策は、データの暗号化または区切り文字を使用して暗号化されたデータとiv&keyを連結するときに使用される配列にiv&keyを保存することです。

例1:

function encrypt(text) {
    let cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(key), iv);
    let encrypted = cipher.update(text);
    encrypted = Buffer.concat([encrypted, cipher.final()]);
    return encrypted.toString('hex') + ':' + iv.toString('hex') + '=' + 
    key.toString('hex');
    //returns encryptedData:iv=key
}

function decrypt(text) {
    let iv = Buffer.from((text.split(':')[1]).split('=')[0], 'hex')//will return iv;
    let enKey = Buffer.from(text.split('=')[1], 'hex')//will return key;
    let encryptedText = Buffer.from(text.split(':')[0], 'hex');//returns encrypted Data
    let decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(enKey), iv);
    let decrypted = decipher.update(encryptedText);
    decrypted = Buffer.concat([decrypted, decipher.final()]);
    return decrypted.toString();
    //returns decryptedData
}

例2:

function encrypt(text) {
    let cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(key), iv);
    let encrypted = cipher.update(text);
    encrypted = Buffer.concat([encrypted, cipher.final()]);
    return { 
        "encryptedData": encrypted.toString('hex'),
        "iv" : iv.toString('hex'),
        "key" : key.toString('hex');
    //returns an Array of key, iv & encryptedData
  }
}

function decrypt(text) {
    let iv = Buffer.from((text.iv, 'hex')//will return iv;
    let enKey = Buffer.from(text.key, 'hex')//will return key;
    let encryptedText = Buffer.from(text.encryptedData, 'hex');//returns encrypted Data
    let decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(enKey), iv);
    let decrypted = decipher.update(encryptedText);
    decrypted = Buffer.concat([decrypted, decipher.final()]);
    return decrypted.toString();
    //returns decryptedData
}