2013-01-31 15 views
6

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"); 
} 
+2

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

+1

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. –

+1

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#. –

risposta

1

Nel caso in cui si stia ancora cercando la risposta, questo esempio fornisce un punto di partenza.

Fondamentalmente, avviare l'allocazione del blocco di memoria nella chiamata di funzione nativa, quindi richiamare la richiamata su gestito in cui si passa (per ref) l'array e le relative dimensioni salvate dall'elenco originale dei parametri di input.

In questo modo si alloca il blocco di memoria per il codice gestito nel codice gestito e lo si modifica con il contenuto da nativo.

http://msdn.microsoft.com/en-us/library/ektebyzx.aspx

Un metodo alternativo sarebbe bello trovare, confrontare le prestazioni sarebbe un bonus :)

1

Francamente, ho trovato il modo più semplice per farlo è quello di avvolgere il non gestito C++ chiamata con una chiamata gestita C++. Nel C++ gestito, è possibile copiare i dati in modo diretto C++ (bloccando la struttura dati) e passarli nuovamente a C#

Problemi correlati