Quindi ho avuto qualche problema con questo. Sto cercando di creare la mia libreria AES 128 da utilizzare con uno dei miei programmi. I test della biblioteca fuori e lavora a C++ (bene per la funzione Encrypt .. Io non sono implementati gli altri) La funzione 'Encrypt' è come questo:Come faccio a restituire un array di byte da C++ a C#
NUOVO CODICE
void Aes128Class::EncryptBlock(BYTE* outBlock, const BYTE* inBlock, const BYTE* cipherBlock)
{
BYTE temp[16] = {0x00};
Galois::XorBlock(temp, inBlock);
Galois::XorBlock(temp, cipherBlock);
BYTE expandedKey[176] = {0x00};
memcpy(expandedKey, Key, 16);
Galois::expand_key(expandedKey);
Galois::XorBlock(temp, expandedKey);
for(int i=16; i<160; i+=16)
{
Galois::DoRound(temp, &expandedKey[i]);
}
Galois::SubBytes(temp);
Galois::ShiftRows(temp);
Galois::XorBlock(temp, &expandedKey[160]);
memcpy(outBlock, temp, 16);
}
void Aes128Class::EncryptData(BYTE* outBlock, size_t& outlen, const BYTE* inBlock, size_t length)
{
float blockSize = (float)(length/16);
blockSize = ceilf(blockSize);
int newLength = (int)(blockSize*16);
BYTE* temp = (BYTE*)malloc(newLength);
BYTE* padd = (BYTE*)malloc(newLength);
memset(temp, 0, newLength);
memcpy(padd, inBlock, length);
EncryptBlock(temp, padd, IV);
for (int i=1; i<blockSize; i++)
{
EncryptBlock(&temp[i*16], &padd[i*16], &temp[(i-1)*16]);
}
outlen = newLength;
memcpy(outBlock, temp, newLength);
}
L'idea è che se lo plainText
non è in un incremento di blocchi di 16 byte allora lo impongo. Quindi questo rende un array di byte di dimensioni variabili. Funziona nei miei test C++
, ma quando lo chiamo in C#
ottengo alcuni errori diversi ... Ci vorrà un minuto per descriverlo.
[DllImport("CppAes128.dll", CallingConvention = CallingConvention.ThisCall,
EntryPoint = "[email protected]@@[email protected]")]
static extern void EncryptData(IntPtr pClass, ref IntPtr outblock, [Out]int OutLength, byte[] inBlock, int length);
Quando chiamo questo ottengo puntatori validi sia al array
, e l'outlength. Il modo in cui ora sembra causare una violazione di accesso, ma posso far funzionare quella struttura se cambio [Out]int OutLength
in ref IntPtr
. È interessante notare che se faccio ref int
o ref uint
funziona ancora ". Quindi se lo faccio provo a leggere il intptr
e poi ottengo una violazione di accesso. Sto compilando questo come un x86 project
in .NET 4.0
(dato che ho letto da qualche parte che il 3,5 avuto alcuni bug con accesso ...)
Ecco quello che ho provato in C#
. E 'un po' confuso come ho giocato con lui per ore (mi dispiace):
public byte[] EncryptData(byte[] plainText, int length)
{
byte[] enc = null;
int len = 0;
IntPtr pArray = IntPtr.Zero;
EncryptData(theClass, ref pArray, len, plainText, length);
Console.WriteLine(len);
//enc = new byte[len];
//Marshal.Copy(pArray, enc, 0, len);
//Marshal.Release(pArray);
//try
//{
// int elementSize = Marshal.SizeOf(typeof(IntPtr));
// //IntPtr unmanagedArray = Marshal.AllocHGlobal(10 * elementSize);
// Console.WriteLine("Reading unmanaged memory:");
// // Print the 10 elements of the C-style unmanagedArray
// for (int i = 0; i < 10; i++)
// {
// Console.WriteLine("{0:X2}:", Marshal.ReadByte(pArray, i));
// }
// Marshal.FreeHGlobal(pArray);
//}
//catch (Exception ex)
//{
// Console.WriteLine("{0}\n{1}", ex.Source, ex.Message);
// Console.WriteLine("Win32({0})", Marshal.GetLastWin32Error());
//}
//Marshal.Release(pArray);
return enc;
}
L'unica volta che questo ha funzionato è quando ho appena fatto un array statico-size e non ho usato ref
o marshal
copia o nulla .. credo che la mia firma era qualcosa di simile
static extern void EncryptData(IntPtr pClass, byte[] outBlock, byte[] inBlock, int length);
che quasi ha funzionato, ma il problema era che quando ho fatto un ciclo foreach
su quel array
era sempre la dimensione che ho messo .. frustrante per non dire meno.
Quindi cosa sto sbagliando? come posso farlo funzionare? Sono così frustrato con esso. Grazie
Oh e FYI, questo è così non posso più dipendere dallo cryptlib
. Sto cercando di ricompilare un progetto diverso, che utilizza cryptlib
, come libreria statica e non condivisa, il che causa alcuni problemi con le mie opzioni compilate ed è troppo grande di una seccatura per tornare indietro.
modificato per mostrare più codice
Questo è il test che uso. Ho trovato una pagina web che ha mostrato un sacco di test, quindi questo sono io che sto implementando questo.
void VerifyEncrypt16(const BYTE* expected, const BYTE* key, const BYTE* iv, const BYTE* plainText)
{
BYTE actual[16] = {0x00};
Aes128Class aes;
aes.SetKey(key, 16);
aes.SetIV(iv, 16);
size_t len = 0;
aes.EncryptData(actual, len, plainText, 16);
_ASSERT(CompareTwoArrays(expected, actual));
}
void VerifyEncrypt16String(const char* expected, const char* key, const char* iv, const char* plainText)
{
BYTE e[16];
BYTE k[16];
BYTE i[16];
BYTE p[16];
ByteUtil::StringToHex(expected, e);
ByteUtil::StringToHex(key, k);
ByteUtil::StringToHex(iv, i);
ByteUtil::StringToHex(plainText, p);
VerifyEncrypt16(e, k, i, p);
}
void CheckEncrypt16(void)
{
_RPT0(_CRT_WARN, "Checking Encryption of a 16 byte number IV set to 0\n");
//AESVS GFSbox test data for CBC
VerifyEncrypt16String("0336763e966d92595a567cc9ce537f5e","00000000000000000000000000000000","00000000000000000000000000000000","f34481ec3cc627bacd5dc3fb08f273e6");
VerifyEncrypt16String("a9a1631bf4996954ebc093957b234589","00000000000000000000000000000000","00000000000000000000000000000000","9798c4640bad75c7c3227db910174e72");
VerifyEncrypt16String("ff4f8391a6a40ca5b25d23bedd44a597","00000000000000000000000000000000","00000000000000000000000000000000","96ab5c2ff612d9dfaae8c31f30c42168");
VerifyEncrypt16String("dc43be40be0e53712f7e2bf5ca707209","00000000000000000000000000000000","00000000000000000000000000000000","6a118a874519e64e9963798a503f1d35");
VerifyEncrypt16String("92beedab1895a94faa69b632e5cc47ce","00000000000000000000000000000000","00000000000000000000000000000000","cb9fceec81286ca3e989bd979b0cb284");
VerifyEncrypt16String("459264f4798f6a78bacb89c15ed3d601","00000000000000000000000000000000","00000000000000000000000000000000","b26aeb1874e47ca8358ff22378f09144");
VerifyEncrypt16String("08a4e2efec8a8e3312ca7460b9040bbf","00000000000000000000000000000000","00000000000000000000000000000000","58c8e00b2631686d54eab84b91f0aca1");
//AESVS KeySbox test data for CBC
VerifyEncrypt16String("6d251e6944b051e04eaa6fb4dbf78465","10a58869d74be5a374cf867cfb473859","00000000000000000000000000000000","00000000000000000000000000000000");
//A TON OF MORE TESTS! etc etc etc VerifyEncrypt16String("5c005e72c1418c44f569f2ea33ba54f3","00000000000000000000000000000000","00000000000000000000000000000000","fffffffffffffffffffffffffffffffe");
VerifyEncrypt16String("3f5b8cc9ea855a0afa7347d23e8d664e","00000000000000000000000000000000","00000000000000000000000000000000","ffffffffffffffffffffffffffffffff");
}
Dai un'occhiata a questo [risposta] (http://stackoverflow.com/a/13123962/175157) . In poche parole, C# non può sapere quanta memoria hai assegnato nel codice C++. – Alex
Non sono sicuro di quale sia il tuo obiettivo finale, ma vorrei solo sottolineare che C# include alcune ottime classi di crittografia nel [Namespace System.Security.Cryptography] (http://msdn.microsoft.com/en-us /library/system.security.cryptography.aspx) che funzionano alla grande. –
Un punto di partenza necessario per l'interoperabilità con il codice C++ è che tu * inizi * con codice C++ che può essere tranquillamente chiamato da un altro codice C++. Non ci sei ancora, stai perdendo la memoria male e un chiamante C++ non avrebbe alcuna ipotesi sulla dimensione del buffer richiesta. Questi problemi non migliorano quando si effettua la chiamata da C#. –