简体   繁体   中英

Encrypt RijndaelManaged 128 in unity c# and Decrypt in Node.JS

I'm looking for a way to encrypt a byte array in unity c# and decrypt on a node.js server.

I'm open to any implementation of either but I have currently gone with the below code which encrypts/decrypts fine in unity but I receive the error:

TypeError: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length

When decrypting a file encrypted in unity using RijndaelManaged 128

Find the encrypting and decrypting code below:

Unity C# Encrypt

private void GenerateEncryptionKey(string userID)
{
    //Generate the Salt, with any custom logic and using the user's ID
    StringBuilder salt = new StringBuilder();
    for (int i = 0; i < 8; i++)
    {
        salt.Append("," + userID.Length.ToString());
    }

    Rfc2898DeriveBytes pwdGen = new Rfc2898DeriveBytes (Encoding.UTF8.GetBytes(userID), Encoding.UTF8.GetBytes(salt.ToString()), 100);
    m_cryptoKey = pwdGen.GetBytes(KEY_SIZE / 8);
    m_cryptoIV = pwdGen.GetBytes(KEY_SIZE / 8);
}

public void Save(string path)
{
    string json = MiniJSON.Json.Serialize(m_saveData);

    using (RijndaelManaged crypto = new RijndaelManaged())
    {
        crypto.BlockSize = KEY_SIZE;
        crypto.Padding = PaddingMode.PKCS7;
        crypto.Key = m_cryptoKey;
        crypto.IV = m_cryptoIV;
        crypto.Mode = CipherMode.CBC;

        ICryptoTransform encryptor = crypto.CreateEncryptor(crypto.Key, crypto.IV);

        byte[] compressed = null;

        using (MemoryStream compMemStream = new MemoryStream())
        {
            using (StreamWriter writer = new StreamWriter(compMemStream, Encoding.UTF8))
            {
                writer.Write(json);
                writer.Close();

                compressed = compMemStream.ToArray();
            }
        }

        if (compressed != null)
        {
            using (MemoryStream encMemStream = new MemoryStream(compressed))
            {
                using (CryptoStream cryptoStream = new CryptoStream(encMemStream, encryptor, CryptoStreamMode.Write))
                {
                    using (FileStream fs = File.Create(GetSavePath(path)))
                    {
                        byte[] encrypted = encMemStream.ToArray();

                        fs.Write(encrypted, 0, encrypted.Length);
                        fs.Close();
                    }
                }
            }
        }
    }
}

ignore the compressed bit, I'll eventually be compressing the data for encryption but I have removed it in this example.

Node.JS Decrypt

var sUserID = "hello-me";
var sSalt = "";

for (var i = 0; i < 8; i++)
{
    sSalt += "," + sUserID.length;
}

var KEY_SIZE = 128;

crypto.pbkdf2(sUserID, sSalt, 100, KEY_SIZE / 4, function(cErr, cBuffer){
    var cKey = cBuffer.slice(0, cBuffer.length / 2);
    var cIV = cBuffer.slice(cBuffer.length / 2, cBuffer.length);

    fs.readFile("save.sav", function (cErr, cData){
        try
        {
            var cDecipher = crypto.createDecipheriv("AES-128-CBC", cKey, cIV);

            var sDecoded = cDecipher.update(cData, null, "utf8");
            sDecoded += cDecipher.final("utf8");
            console.log(sDecoded);
        }
        catch(e)
        {
            console.log(e.message);
            console.log(e.stack);
        }
    });
});

I believe the problem is something to do with padding! I am not using:

cryptoStream.FlushFinalBlock();

when saving the file in c# land because for some reason after doing that c# can't decrypt it anymore and it doesn't really have an effect on the ability of node to decrypt it either, but maybe I'm just missing something in the decryption of it with padding?

Any help is appreciated

One problem is that you're using PasswordDeriveBytes which according to this article is for PBKDF1, whereas Rfc2898DeriveBytes is for PBKDF2. You're using PBKDF2 in your node script.

Then you should check that your cKey and cIV values match between C# and node.

Okay well it seems that order of operation is very important when encrypting and decryption using RijndaelManaged.

Below is the code to encrypt and decrypt in Unity and works with the node.js code posted in the question.

public void Save(string path)
{
    string json = MiniJSON.Json.Serialize(m_saveData);

    using (RijndaelManaged crypto = new RijndaelManaged())
    {
        crypto.BlockSize = KEY_SIZE;
        crypto.Padding = PaddingMode.PKCS7;
        crypto.Key = m_cryptoKey;
        crypto.IV = m_cryptoIV;
        crypto.Mode = CipherMode.CBC;

        ICryptoTransform encryptor = crypto.CreateEncryptor(crypto.Key, crypto.IV);

        byte[] compressed = null;

        using (MemoryStream compMemStream = new MemoryStream())
        {
            using (StreamWriter writer = new StreamWriter(compMemStream, Encoding.UTF8))
            {
                writer.Write(json);
                writer.Close();

                //compressed = CLZF2.Compress(compMemStream.ToArray());
                compressed = compMemStream.ToArray();
            }
        }

        if (compressed != null)
        {
            using (MemoryStream encMemStream = new MemoryStream())
            {
                using (CryptoStream cryptoStream = new CryptoStream(encMemStream, encryptor, CryptoStreamMode.Write))
                {
                    cryptoStream.Write(compressed, 0, compressed.Length);
                    cryptoStream.FlushFinalBlock();

                    using (FileStream fs = File.Create(GetSavePath(path)))
                    {
                        encMemStream.WriteTo(fs);
                    }
                }
            }
        }
    }
}

public void Load(string path)
{
    path = GetSavePath(path);

    try
    {
        byte[] decrypted = null;

        using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
        {
            using (RijndaelManaged crypto = new RijndaelManaged())
            {
                crypto.BlockSize = KEY_SIZE;
                crypto.Padding = PaddingMode.PKCS7;
                crypto.Key = m_cryptoKey;
                crypto.IV = m_cryptoIV;
                crypto.Mode = CipherMode.CBC;

                // Create a decrytor to perform the stream transform.
                ICryptoTransform decryptor = crypto.CreateDecryptor(crypto.Key, crypto.IV);

                using (CryptoStream cryptoStream = new CryptoStream(fs, decryptor, CryptoStreamMode.Read))
                {
                    using (MemoryStream decMemStream = new MemoryStream())
                    {
                        var buffer = new byte[512];
                        var bytesRead = 0;

                        while ((bytesRead = cryptoStream.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            decMemStream.Write(buffer, 0, bytesRead);
                        }

                        //decrypted = CLZF2.Decompress(decMemStream.ToArray());
                        decrypted = decMemStream.ToArray();
                    }
                }
            }
        }

        if (decrypted != null)
        {
            using (MemoryStream jsonMemoryStream = new MemoryStream(decrypted))
            {
                using (StreamReader reader = new StreamReader(jsonMemoryStream))
                {
                    string json = reader.ReadToEnd();

                    Dictionary<string, object> saveData = MiniJSON.Json.Deserialize(json) as Dictionary<string, object>;

                    if (saveData != null)
                    {
                        m_saveData = saveData;
                    }
                    else
                    {
                        Debug.LogWarning("Trying to load invalid JSON file at path: " + path);
                    }
                }
            }
        }
    }
    catch (FileNotFoundException e)
    {
        Debug.LogWarning("No save file found at path: " + path);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM