开发者

aes256 results differ in C# (Windows) and C++ (Ubuntu) implementation

Here are the codes for aes256 with cbc and pkcs7 padding (and a password) encryption on C (Windows and C++ (Ubuntu using libcrypto++). The encryption result is not the same. Why?

C#:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Security.Cryptography;


public static class AESEncryption
{
    public static string Encrypt(byte[] PlainTextBytes, byte[] KeyBytes, string InitialVector)
        {
            try
            {
                byte[开发者_如何学JAVA] InitialVectorBytes = Encoding.UTF8.GetBytes(InitialVector);
                RijndaelManaged SymmetricKey = new RijndaelManaged();
                SymmetricKey.Mode = CipherMode.CBC;
               // SymmetricKey.Padding = PaddingMode.PKCS7;
                ICryptoTransform Encryptor = SymmetricKey.CreateEncryptor(KeyBytes, InitialVectorBytes);
                MemoryStream MemStream = new MemoryStream();
                CryptoStream CryptoStream = new CryptoStream(MemStream, Encryptor, CryptoStreamMode.Write);
                CryptoStream.Write(PlainTextBytes, 0, PlainTextBytes.Length);
                CryptoStream.FlushFinalBlock();
                byte[] CipherTextBytes = MemStream.ToArray();
                MemStream.Close();
                CryptoStream.Close();
                //return ByteToHexConversion(CipherTextBytes);

                return Convert.ToBase64String(CipherTextBytes);
            }
            catch (Exception a)
            {
                throw a;
            }
        }
    }
namespace aes
{ class Program
    {

        static void Main(string[] args)
        {

            string FinalValue = AESEncryption.Encrypt( Encoding.ASCII.GetBytes("My Text"),  Encoding.ASCII.GetBytes("My Password"), "0000000000000000");

            Console.WriteLine(FinalValue);

        }
}

}

C++:

#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
#include <cassert>
#include <stdlib.h>
#include <openssl/evp.h>
#include <sstream>
#include "base64.h"

int main()
{


std::string result;
std::stringstream out;

    // ctx holds the state of the encryption algorithm so that it doesn't
    // reset back to its initial state while encrypting more than 1 block.
    EVP_CIPHER_CTX ctx;
    EVP_CIPHER_CTX_init(&ctx);


    std::string keyy="My Password";// in char key[] My Password is written in bytes
    unsigned char key[] = {0x00, 0x00, 0x00, 0x00, 0x00,
                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x4d,0x79, 0x20, 0x50, 0x61, 0x73, 0x73, 0x77,
                   0x6f, 0x72, 0x64};
    unsigned char iv[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   assert(sizeof(key) == 32);  // AES256 key size
    assert(sizeof(iv) ==  16);   // IV is always the AES block size

    // If data isn't a multiple of 16, the default behavior is to pad with
    // n bytes of value n, where n is the number of padding bytes required
    // to make data a multiple of the block size.  This is PKCS7 padding.
    // The output then will be a multiple of the block size.
    std::string plain("My Text");
    std::vector<unsigned char> encrypted;
    size_t max_output_len  = plain.length() + (plain.length() % 16) + 16;
    encrypted.resize(max_output_len);

    // Enc is 1 to encrypt, 0 to decrypt, or -1 (see documentation).
    EVP_CipherInit_ex(&ctx, EVP_aes_256_cbc(), NULL, key, iv, 1);

    // EVP_CipherUpdate can encrypt all your data at once, or you can do
    // small chunks at a time.
    int actual_size = 0;
    EVP_CipherUpdate(&ctx,
             &encrypted[0], &actual_size,
             reinterpret_cast<unsigned char *>(&plain[0]), plain.size());

    // EVP_CipherFinal_ex is what applies the padding.  If your data is
    // a multiple of the block size, you'll get an extra AES block filled
    // with nothing but padding.
    int final_size;
    EVP_CipherFinal_ex(&ctx, &encrypted[actual_size], &final_size);
    actual_size += final_size;

    encrypted.resize(actual_size);

    for( size_t index = 0; index < encrypted.size(); ++index )
    {
        std::cout << std::hex << std::setw(2) << std::setfill('0') <<
            static_cast<unsigned int>(encrypted[index]);
         //std:: cout<< "val: "<< static_cast<unsigned int>(encrypted[index]) << std::endl;

        out<< std::hex << std::setw(2) << std::setfill('0') << static_cast<unsigned int>(encrypted[index]);
    }
    result = out.str();
    std::cout <<"\n"<< result<< "\n";

    EVP_CIPHER_CTX_cleanup(&ctx);


    //
    std::cout<<"decript..\n";


    return 0;
}


your IV in c# is a string containing '0' and not '\0' and your IV in c++ does contain '\0' the ascii value of '0' and '\0' are different.

replace the following line

string FinalValue = AESEncryption.Encrypt( Encoding.ASCII.GetBytes("My Text"),  Encoding.ASCII.GetBytes("My Password"), "0000000000000000");

with

string FinalValue = AESEncryption.Encrypt( Encoding.ASCII.GetBytes("My Text"),  Encoding.ASCII.GetBytes("My Password"), "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");

that should do the trick I think.

20110111

try replacing Encoding.ASCII.GetBytes("My Password") with new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d,0x79, 0x20, 0x50, 0x61, 0x73, 0x73, 0x77,0x6f, 0x72, 0x64} in your c# code should yell different result


Expanding on dvhh's answer:

You probably should not use a zero IV anyhow. An IV can be useful to avoid identical plaintexts (or plaintexts with identical prefixes) from being recognizably identical to an attacker. It's OK for the IV itself to be plaintext; so you could just randomly generate the IV and prepend it to the output to permit decryption.

To get a secure random IV in .NET, so something like:

byte[] initialVectorBytes = new byte[16];
using(var rng = new RNGCryptoServiceProvider())
    rng.GetBytes(initialVectorBytes);
//...
using(var memStream = new MemoryStream()) {
    memStream.Write(IV,0,16); //to permit decryption later. 
    //...
}

BTW, the usual practice in C# is to use a lowercase letter at the start of a variable or parameter name - you'll make your code more readable to others if you follow suit.


I came across this same issue when I was working on encryption between PHP and a .NET web service.

I created a sample project on github to show a working example of Rijndael encryption between PHP and NET.: https://github.com/dchymko/.NET--PHP-encryption


If you want to use IV as 16 bytes array of value 0x00, then change the C# code

byte[] InitialVectorBytes = Encoding.UTF8.GetBytes(InitialVector);

to

byte[] InitialVectorBytes = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

You can also change the function to ommit the 3rd parameter

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜