go语言实现WebAssembly数据加密

一、背景和意义

在Web开发中,有时候为了提升安全性需要对数据进行加密。由于js代码相对比较易读,直接在js中做加密安全性较低,而WebAssembly代码不如js易读,使用WebAssemply做数据加密对安全性有一定的提升(不过熟悉WebAssembly的人还是能看懂加解密过程)。本文提供一个用go语言实现的WebAssembly数据加密示例。

二、创建WebAssembly文件

创建一个空目录,执行如下命令初始化go模块:

bash 复制代码
go mod init wasm-demo

接下来在当前目录下创建main.go文件,提供数据加密与解密的方法:

go 复制代码
package main

import (
   "bytes"
   "crypto/aes"
   "crypto/cipher"
   "encoding/hex"
   "fmt"
   "syscall/js"
)

var aesCode = "0000000000000000" // 作为demo,这里只用16个0作为密钥

// pkcs5Padding 对数据进行填充,使其长度为块大小的倍数。
func pkcs5Padding(cipherText []byte, blockSize int) []byte {
   padding := blockSize - len(cipherText)%blockSize
   padText := bytes.Repeat([]byte{byte(padding)}, padding)
   return append(cipherText, padText...)
}

// pkcs5UnPadding 移除填充数据。
func pkcs5UnPadding(decrypted []byte) []byte {
   length := len(decrypted)
   unPadding := int(decrypted[length-1])
   return decrypted[:(length - unPadding)]
}

// aesDecrypt 加密
func aesEncrypt(encryptStr string) (string, error) {
   encryptBytes := []byte(encryptStr)
   block, err := aes.NewCipher([]byte(aesCode))
   if err != nil {
      return "", err
   }

   blockSize := block.BlockSize()
   encryptBytes = pkcs5Padding(encryptBytes, blockSize)

   blockMode := cipher.NewCBCEncrypter(block, []byte(aesCode)[:blockSize])
   encrypted := make([]byte, len(encryptBytes))
   blockMode.CryptBlocks(encrypted, encryptBytes)
   return hex.EncodeToString(encrypted), nil
}

// aesDecrypt 解密
func aesDecrypt(decryptStr string) (string, error) {
   decryptBytes, err := hex.DecodeString(decryptStr)
   if err != nil {
      return "", err
   }

   block, err := aes.NewCipher([]byte(aesCode))
   if err != nil {
      return "", err
   }

   blockMode := cipher.NewCBCDecrypter(block, []byte(aesCode)[:block.BlockSize()])
   decrypted := make([]byte, len(decryptBytes))

   blockMode.CryptBlocks(decrypted, decryptBytes)
   decrypted = pkcs5UnPadding(decrypted)
   return string(decrypted), nil
}

func jsFunc(handler func(string) (string, error)) js.Func {
   return js.FuncOf(func(this js.Value, args []js.Value) interface{} {
      var result string
      var err error
      if len(args) > 0 {
         result, err = handler(args[0].String())
      }
      return js.ValueOf(map[string]interface{}{
         "result": result,
         "err":    err,
      })
   })
}

func main() {
   fmt.Println("Go wasm loaded!")
   js.Global().Set("aesEncrypt", jsFunc(aesEncrypt))
   js.Global().Set("aesDecrypt", jsFunc(aesDecrypt))
   <-make(chan bool)
}

如果是在goland中编辑此代码,需要在Settings中设置一下Build Tags,将OS设置为js,Arch设置为wasm,否则代码编辑器里的syscall/js会标红:

接下来执行如下命令生成wasm文件main.wasm:

bash 复制代码
GOOS=js GOARCH=wasm go build -o main.wasm

三、在前端页面中使用WebAssemply的加解密方法

执行如下命令将go目录下的wasm_exec.js复制过来到当前目录:

bash 复制代码
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .

接下来创建前端代码index.html文件:

html 复制代码
<!DOCTYPE html>
<html lang="utf8">
    <head>
        <title>wasm demo</title>
        <script src="wasm_exec.js"></script>
        <script>
            const go = new Go();
            WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject).then(result => {
                go.run(result.instance);
                const encrypt = aesEncrypt("HelloWorld");
                console.log("encrypt result: ", encrypt);
                console.log("decrypt result: ", aesDecrypt(encrypt.result));
            });
        </script>
    </head>
    <body></body>
</html>

将当前目录添加到nginx中,然后通过nginx提供的端口访问index.html,可以看到控制台输出加解密的结果如下:

相关推荐
迅猛龙办公室4 小时前
Python输出当前计算机的系统日期和时间
开发语言·前端·python
小狼154544 小时前
浏览器扩展脚本为什么有时候不生效:注入时机、iframe 和单页路由,多多开票助手
前端·chrome
じòぴé南冸じょうげん4 小时前
油猴脚本突然发现变成灰色了,无法使用?页面不生效?刷新页面没反应?
前端
Csvn6 小时前
组合式 API(Composition API)
前端
火柴就是我7 小时前
Android 打包报错 25.0.3
android·前端
BD_Marathon7 小时前
消息对象中字段的说明
java·前端·python
八荒启·交互动画7 小时前
Web特效025—用 Canvas 2D 做Web特效的定义与边界:这支画笔能做到哪一步
前端·webgl·网页特效·八荒启-交互动画·八荒启
Ai-_Man7 小时前
您您这可以把Dola的多个会话比如说。左侧的多个会话一次性导出吗?不是单条会话里面的多次会对话。用AI导出鸭,答案是可以的
开发语言·前端·人工智能·小程序
excel8 小时前
Nuxt 中使用 useHead 优化 SEO 与 GEO
前端
IT_陈寒9 小时前
SpringBoot启动慢得像蜗牛?原来是这个配置在捣鬼
前端·人工智能·后端