C#Dll导入Go Dll字符串值差异-CString返回值

i wrote in Go a dll to decrypt / encrypt data with public / private key encoding.

The first problem I encountered was sharing strings between the dll and the c# code.

I solved this by using the C library in Go.

func myfunc( password *C.char, passwordLength C.int )  *C.char{
   result = C.GoStringN(password, passwordLength)
   return C.CString(result)
}

More or less Something like that.

In C# the counterpart is

Marshal.PtrToStringAuto(value);

My problem is now that the strings do not match in go and c#. One function is returning an encrypted value and when i log it with

fmt.println(...)

It does not match the return value I'm logging in c#

Console.writeln(...)

I tried different functions:

Marshal.PtrToStringAuto(...)
Marshal.PtrToStringAnsi(...)
Marshal.PtrToStringUTF8(...)
Marshal.PtrToStringUni(...)

After using the C Library I had problems with the strings in go too. Because of that I added length and moved from C.GoString to C.GoStringN that helped while debugging the code in Go.

I really don't know what I'm doing wrong. Has someone an idea what I could try?

Output Go 1

Output Go 2

I solved a part of the problem. The output of the encryption has a defined size. So in Go im able to read the values now properly. What options i have in C# to read the values properly? Marshal.PtrToStringUTF8 seems to works best from the Marshal functions. But it does not equals the value from Go.

Here is an example with AES encryption within a go dll; the strings are matching for me:

Go code (compiled with CGO_ENABLED=1 GOARCH=386 GOOS=windows go build -buildmode=c-shared -o test.dll AES.go):

package main

import (
    "C"
    "crypto/aes"
    "crypto/cipher"
    "crypto/rand"
    "encoding/base64"
    "errors"
    "io"
)

var cipherKey = []byte("Zq4t7w!z%C&F)J@N")

func main() {}

//export Encrypt
func Encrypt(cMessage *C.char) *C.char {
    message := C.GoString(cMessage)
    plainText := []byte(message)

    block, err := aes.NewCipher(cipherKey)
    if err != nil {
        panic(err)
    }

    cipherText := make([]byte, aes.BlockSize+len(plainText))
    iv := cipherText[:aes.BlockSize]
    if _, err = io.ReadFull(rand.Reader, iv); err != nil {
        panic(err)
    }

    stream := cipher.NewCFBEncrypter(block, iv)
    stream.XORKeyStream(cipherText[aes.BlockSize:], plainText)

    //returns to base64 encoded string
    encmess := base64.URLEncoding.EncodeToString(cipherText)
    return C.CString(encmess)
}

//export Decrypt
func Decrypt(cEncryptedMessage *C.char) *C.char {
    encryptedMessage := C.GoString(cEncryptedMessage)
    cipherText, err := base64.URLEncoding.DecodeString(encryptedMessage)
    if err != nil {
        panic(err)
    }

    block, err := aes.NewCipher(cipherKey)
    if err != nil {
        panic(err)
    }

    if len(cipherText) < aes.BlockSize {
        err = errors.New("Ciphertext block size is too short!")
        panic(err)
    }

    iv := cipherText[:aes.BlockSize]
    cipherText = cipherText[aes.BlockSize:]

    stream := cipher.NewCFBDecrypter(block, iv)
    stream.XORKeyStream(cipherText, cipherText)

    decodedmess := string(cipherText)
    return C.CString(decodedmess)
}

C# code:

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            var encrypted = GoFunctions.Encrypt(System.Text.Encoding.UTF8.GetBytes("hello"));
            var decrypted = GoFunctions.Decrypt(System.Text.Encoding.UTF8.GetBytes(Marshal.PtrToStringAnsi(encrypted)));
            label1.Text = Marshal.PtrToStringAnsi(encrypted);
            label2.Text = Marshal.PtrToStringAnsi(decrypted);
        }

        static class GoFunctions
        {
            [DllImport(@"C:\Repos\Go_AES\test.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
            public static extern IntPtr Encrypt(byte[] message);

            [DllImport(@"C:\Repos\Go_AES\test.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
            public static extern IntPtr Decrypt(byte[] encrypted_message);
        }
    }
}

Thank you for helping me.

Your solution didn't work for me, but it helped me a lot.

//export EncryptForDb
func EncryptForDb(value *C.char) unsafe.Pointer {
   encodedString := Cipher.PrivatKeyEncryptValue(Cipher.PrivatKey, C.GoString(value))
   return C.CBytes([]byte(encodedString))
}

I'm returning an unsafe Pointer to the result ByteArray. On the C# side im using this code now.

byte[] ICipherManager.EncryptForDb(string value)
{
    byte[] encryptedBytes = new byte[1024];

    IntPtr pointer = EncryptForDb(value);
    Marshal.Copy(pointer, encryptedBytes, 0, encryptedBytes.Length);

    return encryptedBytes;
}

When im using C.CString the raw value is sliced. Go and C# Strings does contain a length property. CStrings are terminated by ram and i couldn't find a solution to read or write a whole encrypted string with it. So i decided to use []byte instead.