Encrypt on client side using javascript WebCrypto and decrypt on backend using go:

javascript code:

async function aesGcmEncrypt(plaintext, password) {
    const pwUtf8 = new TextEncoder().encode(password);                                 // encode password as UTF-8
    const pwHash = await crypto.subtle.digest('SHA-256', pwUtf8);                      // hash the password

    const iv = crypto.getRandomValues(new Uint8Array(12));                             // get 96-bit random iv

    const alg = { name: 'AES-GCM', iv: iv };                                           // specify algorithm to use

    const key = await crypto.subtle.importKey('raw', pwHash, alg, false, ['encrypt']); // generate key from pw

    const ptUint8 = new TextEncoder().encode(plaintext);                               // encode plaintext as UTF-8
    const ctBuffer = await crypto.subtle.encrypt(alg, key, ptUint8);                   // encrypt plaintext using key

    const ctArray = Array.from(new Uint8Array(ctBuffer));                              // ciphertext as byte array
    const ctStr = ctArray.map(byte => String.fromCharCode(byte)).join('');             // ciphertext as string
    const ctBase64 = btoa(ctStr);                                                      // encode ciphertext as base64

    const ivHex = Array.from(iv).map(b => ('00' + b.toString(16)).slice(-2)).join(''); // iv as hex string

    return ivHex+ctBase64;                                                             // return iv+ciphertext
}


async function aesGcmDecrypt(ciphertext, password) {
    const pwUtf8 = new TextEncoder().encode(password);                                 // encode password as UTF-8
    const pwHash = await crypto.subtle.digest('SHA-256', pwUtf8);                      // hash the password

    const iv = ciphertext.slice(0,24).match(/.{2}/g).map(byte => parseInt(byte, 16));  // get iv from ciphertext

    const alg = { name: 'AES-GCM', iv: new Uint8Array(iv) };                           // specify algorithm to use

    const key = await crypto.subtle.importKey('raw', pwHash, alg, false, ['decrypt']); // use pw to generate key

    const ctStr = atob(ciphertext.slice(24));                                          // decode base64 ciphertext
    const ctUint8 = new Uint8Array(ctStr.match(/./g).map(ch => ch.charCodeAt(0)));     // ciphertext as Uint8Array
    // note: why doesn't ctUint8 = new TextEncoder().encode(ctStr) work?

    const plainBuffer = await crypto.subtle.decrypt(alg, key, ctUint8);                // decrypt ciphertext using key
    const plaintext = new TextDecoder().decode(plainBuffer);                           // decode password from UTF-8

    return plaintext;                                                                  // return the plaintext
}

Backend code:

package main

import (
    "bytes"
    "crypto/aes"
    "crypto/cipher"
    "crypto/sha256"
    "encoding/base64"
    "encoding/hex"
    "fmt"
    "io"
    "log"
)

func main() {
    data := "e2f4b56b1961ca12d38031420DVbsv0o5Tux4MUCGG0ZcMUZ7yZHlSWn3DsZJmbg"

    message := bytes.Buffer{}
    iv, err := hex.DecodeString(data[:24])
    if err != nil {
        log.Fatal(err)
    }
    message.Write(iv)

    payload, err := base64.StdEncoding.DecodeString(data[24:])
    if err != nil {
        fmt.Println("decode error:", err)
        return
    }
    message.Write(payload)

    h := sha256.New()
    io.WriteString(h, "pw")
    key := h.Sum(nil)

    out, err := Decrypt(key, message.Bytes())
    if err != nil {
        fmt.Println("decode error:", err)
        return
    }

    fmt.Printf("message: %s\n", out)
}

// Decrypt AES-256 GCM
func Decrypt(password, message []byte) ([]byte, error) {
    c, err := aes.NewCipher(password)
    if err != nil {
        return nil, err
    }

    gcm, err := cipher.NewGCM(c)
    if err != nil {
        return nil, err
    }

    nonce := make([]byte, gcm.NonceSize())
    copy(nonce, message[:gcm.NonceSize()])

    out, err := gcm.Open(nil, nonce, message[gcm.NonceSize():], nil)
    if err != nil {
        return nil, err
    }
    return out, nil
}

Example of web page index.html:

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width" />
    <title>test webcrypto</title>
    <script src="common.js"></script>
    <script>
        (async() => {
            const ciphertext = await aesGcmEncrypt("my secret text", "pw");
            out = await aesGcmDecrypt(ciphertext, "pw");
            console.log(ciphertext, out);
        })()
    </script>
</head>

<body>
check your console.log
</body>

</html>

To test create a directory with index.html and common.js and execute command www: