4

This will hardly get any answer but let's try anyway. Also I might just have done some really basic thing wrong... but anyway:

I have to use some OpenSSL functions for AES/RSA encrypt/decrypt with PHP (just telling this so if you know some headace-saving alternatives, you know...) with Mono. Since OpenSSL.NET first doesn't work with Mono and second it doesn't receive support any longer, I've been forced to port the functions I needed directly from C OpenSSL to a DLL and call these functions from C#, which is a pain by itself.

As you'd may notice, the encrypt/decrypt functions are directly stolen AHEM taken from the wiki.

The .h file is:

#pragma once

#include <comdef.h>
#include <comutil.h>
#include <tchar.h>

#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/applink.c>

#define EXPORT __declspec(dllexport)

namespace OpenSSL
{
    extern "C" { EXPORT int NumericTest(int a, int b); }
    extern "C" { EXPORT BSTR StringTest(); }
    extern "C" { EXPORT BSTR StringTestReturn(char *toReturn); }

    extern "C" { EXPORT int Encrypt_AES_256_CBC(unsigned char *plaintext,
                                                int plaintext_len,
                                                unsigned char *key,
                                                unsigned char *iv,
                                                unsigned char *ciphertext); }

    extern "C" { EXPORT int Decrypt_AES_256_CBC(unsigned char *ciphertext,
                                                int ciphertext_len,
                                                unsigned char *key,
                                                unsigned char *iv,
                                                unsigned char *plaintext); }

    extern "C" { EXPORT void HandleErrors(); }
}

The .cpp file is:

#include "OpenSSL.h"

namespace OpenSSL
{
    void HandleErrors()
    {
        ERR_print_errors_fp(stderr);
        abort();
    }

    void InitOpenSSL()
    {
        ERR_load_crypto_strings();
        OpenSSL_add_all_algorithms();
        OPENSSL_config(NULL);
    }

    int NumericTest(int a, int b)
    {
        return a + b;
    }

    BSTR StringTest()
    {
        return ::SysAllocString(L"StringTest successful!");
    }

    BSTR StringTestReturn(char *toReturn)
    {
        _bstr_t bstrt(toReturn);
        bstrt += " (StringTestReturn)";

        strcpy_s(toReturn, 256, "StringTestReturn");
        return bstrt;
    }

    int Encrypt_AES_256_CBC(unsigned char *plaintext, int plaintext_len, unsigned char *key, unsigned char *iv, unsigned char *ciphertext)
    {
        InitOpenSSL();

        EVP_CIPHER_CTX *ctx;

        int len;

        int ciphertext_len;

        /* Create and initialise the context */
        if (!(ctx = EVP_CIPHER_CTX_new())) HandleErrors();

        /* Initialise the encryption operation. IMPORTANT - ensure you use a key
        * and IV size appropriate for your cipher
        * In this example we are using 256 bit AES (i.e. a 256 bit key). The
        * IV size for *most* modes is the same as the block size. For AES this
        * is 128 bits */
        if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv))
            HandleErrors();

        /* Provide the message to be encrypted, and obtain the encrypted output.
        * EVP_EncryptUpdate can be called multiple times if necessary
        */
        if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
            HandleErrors();
        ciphertext_len = len;

        /* Finalise the encryption. Further ciphertext bytes may be written at
        * this stage.
        */
        if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) HandleErrors();
        ciphertext_len += len;

        /* Clean up */
        EVP_CIPHER_CTX_free(ctx);

        return ciphertext_len;
    }

    int Decrypt_AES_256_CBC(unsigned char *ciphertext, int ciphertext_len, unsigned char *key,
                unsigned char *iv, unsigned char *plaintext)
    {
        InitOpenSSL();

        EVP_CIPHER_CTX *ctx;

        int len;

        int plaintext_len;

        /* Create and initialise the context */
        if (!(ctx = EVP_CIPHER_CTX_new())) HandleErrors();

        /* Initialise the decryption operation. IMPORTANT - ensure you use a key
        * and IV size appropriate for your cipher
        * In this example we are using 256 bit AES (i.e. a 256 bit key). The
        * IV size for *most* modes is the same as the block size. For AES this
        * is 128 bits */
        if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv))
            HandleErrors();

        /* Provide the message to be decrypted, and obtain the plaintext output.
        * EVP_DecryptUpdate can be called multiple times if necessary
        */
        if (1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
            HandleErrors();
        plaintext_len = len;

        /* Finalise the decryption. Further plaintext bytes may be written at
        * this stage.
        */
        if (1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) HandleErrors();
        plaintext_len += len;

        /* Clean up */
        EVP_CIPHER_CTX_free(ctx);

        return plaintext_len;
    }
}

Note how these functions had been tested and works perfectly fine on a C++ console application

Last but not least, the C# code I've been using for testing purposes:

using System;
using System.Text;
using System.Runtime.InteropServices;


namespace OpenSSLTests
{
    [StructLayout(LayoutKind.Sequential)]
    class OpenSSL
    {
        const string OPENSSL_PATH = "(here i putted my dll file path (of course in the program i putted the real path, but not here))";

        [DllImport(OPENSSL_PATH, CallingConvention = CallingConvention.Cdecl)]
        public static extern int NumericTest(int a, int b);

        [DllImport(OPENSSL_PATH, CallingConvention = CallingConvention.Cdecl)]
        [return: MarshalAs(UnmanagedType.BStr)]
        public static extern string StringTest();

        [DllImport(OPENSSL_PATH, EntryPoint = "StringTestReturn", CallingConvention = CallingConvention.Cdecl)]
        [return: MarshalAs(UnmanagedType.BStr)]
        public static extern string StringTestReturn(StringBuilder toReturn);

        [DllImport(OPENSSL_PATH, CallingConvention = CallingConvention.Cdecl)]
        public static extern int Encrypt_AES_256_CBC(StringBuilder plainText,
                                                    int plaintext_len,
                                                    StringBuilder key,
                                                    StringBuilder iv,
                                                    StringBuilder ciphertext);

        [DllImport(OPENSSL_PATH, CallingConvention = CallingConvention.Cdecl)]
        public static extern int Decrypt_AES_256_CBC(StringBuilder cipherText,
                                                    int ciphertext_len,
                                                    StringBuilder key,
                                                    StringBuilder iv,
                                                    StringBuilder plaintext);

        static void Main(string[] args)
        {

            Console.WriteLine("the numeric test result is: " + NumericTest(1, 2));
            Console.WriteLine();

            Console.WriteLine("the string test result is: " + StringTest());
            Console.WriteLine();

            StringBuilder test = new StringBuilder(256);
            test.Append("test");
            Console.WriteLine("test is: " + test);
            Console.WriteLine();




            Console.WriteLine("Calling StringTestReturn...");
            string newTest = StringTestReturn(test);
            Console.WriteLine("test is now: " + test);
            Console.WriteLine("newTest is: " + newTest);
            Console.WriteLine();

            StringBuilder plainText = new StringBuilder(1024);
            plainText.Append("this is a really crashy test plz halp");

            StringBuilder key = new StringBuilder(1024);
            StringBuilder IV = new StringBuilder(1024);

            key.Append("randomkey12345asdafsEWFAWEFAERGERUGHERUIGHAEIRUGHOAEGHSD");
            IV.Append("ivtoUse1248235fdghapeorughèaerjèaeribyvgnèaervgjer0vriono");

            StringBuilder ciphredText = new StringBuilder(1024);

            int plainTextLength = Encrypt_AES_256_CBC(plainText, plainText.Length, key, IV, ciphredText);

            if (plainTextLength != -1)
            {
                Console.WriteLine("encrypted text length: " + plainTextLength);
                Console.WriteLine("the encrypted text content is: " + ciphredText);
            }
            else
            {
                Console.WriteLine("error encrypting\n");
            }

            StringBuilder decryptedText = new StringBuilder(1024);

            int decryptedTextLength = -1;
            try
            {
                decryptedTextLength = Decrypt_AES_256_CBC(ciphredText, ciphredText.Length, key, IV, decryptedText);
            }
            catch (Exception e)
            {
                Console.WriteLine("error during decryption");
            }

            if (decryptedTextLength != -1)
            {
                decryptedText[decryptedTextLength] = '\0';
                Console.WriteLine("decrypted text length: " + decryptedTextLength);

                try
                {
                    //here it crashes without even giving you the courtesy of printing anything. It just closes
                    Console.WriteLine("decrypted text is:     " + decryptedText);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.ToString());
                }
            }
            else
            {
                Console.WriteLine("error after decryption");
            }


            Console.ReadLine();
        }
    }
}

As you'd might expect, it shouldn't work. Not exactly. Or better: it doesn't work but the strange thing is the way it doesn't: unlike any other program i ever wrote or heard, when the program runs Visual Studio just opens a console window but just for an istant and then it self shuts down without even throwing an error. You know, like when you learn C# and write hello world but forget to add Console.ReadLine() at the end or system("PAUSE") in c++ so it closes in a second? The same thing.

However if i run it by pressing ctrl+F5 i get the console and the output will be:

the numeric test result is: 3

the string test result is: StringTest successful!

test is: test

Calling StringTestReturn...
test is now: StringTestReturn
newTest is: test (StringTestReturn)

encrypted text length: 48
the encrypted text content is: 9ïÅèSðdC1¦æÌ?
OPENSSL_Uplink(00007FFFFB388000,08): no OPENSSL_Applink
Premere un tasto per continuare . . . (press a key to finish in italian)

So the error is supposed to be that OPENSSL_Uplink(00007FFFFB388000,08): no OPENSSL_Applink and it throws it while i try to write the decrypted text.

According to the documentation of the website, it happens if you forget to include applink.c to the code but as you can see it's right there in the .h file.

So how should i get rid of it? Including applink.c doesn't work.

I will sell my soul to you if you can get me out of this eternal hell.

Regards.

Asduffo
  • 93
  • 1
  • 6
  • *"... but as DLL causes OPENSSL_Uplink: no OPENSSL_Applink"* - This is sometimes due to mixing and matching C/C++ runtimes. You should probably go back to the early steps in your project, and rebuild everything using the same C/C++ runtimes. Be sure Mono and other Windows-on-Linux projects are using the same version of the runtime. Also, avoid the distro's copy of OpenSSL-on-Windows, like Cygwin. Build OpenSSL yourself to ensure you get the right C/C++ runtime (this leads to a number of WTF? problems). – jww Aug 13 '16 at 20:33
  • will give it a try. It might take 1 billion years through, since i'm super bad at installing stuff such as OpenSSL even with step by step tutorials XD – Asduffo Aug 17 '16 at 13:10

1 Answers1

0

Just include the applink.c in the main.c and don't include it in any another file!

Timo
  • 1