mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-01 20:16:36 +08:00
510 lines
17 KiB
Go
510 lines
17 KiB
Go
package rule
|
||
|
||
import (
|
||
"crypto/hmac"
|
||
"crypto/md5"
|
||
"crypto/sha1"
|
||
"crypto/sha256"
|
||
"crypto/sha512"
|
||
"encoding/base64"
|
||
"encoding/hex"
|
||
"fmt"
|
||
"hash"
|
||
"strings"
|
||
|
||
"github.com/dop251/goja"
|
||
)
|
||
|
||
// 本文件对应 legado JsExtensions / JsEncodeUtils / RegexJsExtensions 中
|
||
// 注入为 `java` 对象的函数。函数名与 Kotlin 版一一对应(存量书源硬编码),
|
||
// 实现全部为 Go 原生;浏览器/文件/压缩包类能力明确抛出不支持错误。
|
||
|
||
func newJavaObject(vm *goja.Runtime, r *JSRunner, a *AnalyzeRule) *goja.Object {
|
||
o := vm.NewObject()
|
||
set := func(k string, v any) {
|
||
if err := o.Set(k, v); err != nil {
|
||
panic(vm.ToValue(err.Error()))
|
||
}
|
||
}
|
||
|
||
bridgeErr := func(name string, err error) goja.Value {
|
||
panic(vm.ToValue(fmt.Sprintf("java.%s: %v", name, err)))
|
||
}
|
||
|
||
// ── 网络(对应 JsExtensions.ajax/ajaxAll/connect/get/post/head) ──
|
||
fetchStr := func(name string, urlVal goja.Value, extraHeaders map[string]any) string {
|
||
if r.cfg.Fetch == nil {
|
||
bridgeErr(name, ErrJsUnsupported)
|
||
}
|
||
urlStr := urlVal.String()
|
||
if arr, ok := urlVal.Export().([]any); ok && len(arr) > 0 {
|
||
urlStr = fmt.Sprintf("%v", arr[0]) // 对应 legado:List 取第一个
|
||
}
|
||
req, err := ParseAnalyzeUrlWithJS(urlStr, "", 0, r.cfg.BaseURL, r)
|
||
if err != nil {
|
||
bridgeErr(name, err)
|
||
}
|
||
if req.Unsupported != nil {
|
||
bridgeErr(name, req.Unsupported)
|
||
}
|
||
for k, v := range extraHeaders {
|
||
if _, exists := req.Headers[k]; !exists {
|
||
req.Headers[k] = fmt.Sprintf("%v", v)
|
||
}
|
||
}
|
||
body, _, _, err := r.cfg.Fetch(req)
|
||
if err != nil {
|
||
bridgeErr(name, err)
|
||
}
|
||
return body
|
||
}
|
||
|
||
fetchResp := func(name string, args []goja.Value, method, body string) *goja.Object {
|
||
if r.cfg.Fetch == nil {
|
||
bridgeErr(name, ErrJsUnsupported)
|
||
}
|
||
urlStr := ""
|
||
if len(args) > 0 {
|
||
urlStr = args[0].String()
|
||
}
|
||
req, err := ParseAnalyzeUrlWithJS(urlStr, "", 0, r.cfg.BaseURL, r)
|
||
if err != nil {
|
||
bridgeErr(name, err)
|
||
}
|
||
if method == "POST" {
|
||
req.Method = "POST"
|
||
req.Body = body
|
||
trimmed := strings.TrimSpace(body)
|
||
if strings.HasPrefix(trimmed, "{") || strings.HasPrefix(trimmed, "[") {
|
||
req.IsJSON = true
|
||
} else if body != "" {
|
||
req.IsForm = true
|
||
req.Body = encodeParams(body, req.Charset, false)
|
||
}
|
||
}
|
||
if len(args) > 1 && !goja.IsUndefined(args[1]) && !goja.IsNull(args[1]) {
|
||
if extra, ok := args[1].Export().(map[string]any); ok {
|
||
for k, v := range extra {
|
||
req.Headers[k] = fmt.Sprintf("%v", v)
|
||
}
|
||
}
|
||
}
|
||
if req.Unsupported != nil {
|
||
bridgeErr(name, req.Unsupported)
|
||
}
|
||
respBody, finalURL, code, err := r.cfg.Fetch(req)
|
||
if err != nil {
|
||
bridgeErr(name, err)
|
||
}
|
||
return newResponseObject(vm, respBody, code, finalURL, nil)
|
||
}
|
||
|
||
set("ajax", func(call goja.FunctionCall) goja.Value {
|
||
extra := map[string]any(nil)
|
||
if len(call.Arguments) > 1 && !goja.IsUndefined(call.Arguments[1]) && !goja.IsNull(call.Arguments[1]) {
|
||
if m, ok := call.Arguments[1].Export().(map[string]any); ok {
|
||
extra = m
|
||
}
|
||
}
|
||
return vm.ToValue(fetchStr("ajax", call.Arguments[0], extra))
|
||
})
|
||
set("ajaxAll", func(call goja.FunctionCall) goja.Value {
|
||
var urls []any
|
||
if len(call.Arguments) > 0 {
|
||
if arr, ok := call.Arguments[0].Export().([]any); ok {
|
||
urls = arr
|
||
}
|
||
}
|
||
out := make([]any, 0, len(urls))
|
||
for _, u := range urls {
|
||
out = append(out, fetchStr("ajaxAll", vm.ToValue(u), nil))
|
||
}
|
||
return vm.ToValue(out)
|
||
})
|
||
set("connect", func(call goja.FunctionCall) goja.Value {
|
||
return fetchResp("connect", call.Arguments, "GET", "")
|
||
})
|
||
// get 双语义(对应 Kotlin 重载):get(url, headers) 走网络,get(key) 读变量
|
||
set("get", func(call goja.FunctionCall) goja.Value {
|
||
if len(call.Arguments) >= 2 && !goja.IsUndefined(call.Arguments[1]) && !goja.IsNull(call.Arguments[1]) {
|
||
return fetchResp("get", call.Arguments, "GET", "")
|
||
}
|
||
key := stringArg(call, 0)
|
||
if a != nil {
|
||
return vm.ToValue(a.Get(key))
|
||
}
|
||
return vm.ToValue(r.vars[key])
|
||
})
|
||
set("post", func(call goja.FunctionCall) goja.Value {
|
||
body := stringArgOr(call, 1, "")
|
||
return fetchResp("post", call.Arguments, "POST", body)
|
||
})
|
||
set("head", func(call goja.FunctionCall) goja.Value {
|
||
return fetchResp("head", call.Arguments, "HEAD", "")
|
||
})
|
||
|
||
// ── 规则回调(桥回当前 AnalyzeRule,对应 AnalyzeRule.getString 等公有方法) ──
|
||
ruleStr := func(name string, args []goja.Value) (string, error) {
|
||
if a == nil {
|
||
return "", fmt.Errorf("无规则上下文")
|
||
}
|
||
mContent := any(nil)
|
||
if len(args) > 1 && !goja.IsUndefined(args[1]) && !goja.IsNull(args[1]) {
|
||
mContent = args[1].Export()
|
||
}
|
||
isURL := len(args) > 2 && args[2].ToBoolean()
|
||
return a.GetString(args[0].String(), mContent, isURL)
|
||
}
|
||
set("getString", func(call goja.FunctionCall) goja.Value {
|
||
s, err := ruleStr("getString", call.Arguments)
|
||
if err != nil {
|
||
bridgeErr("getString", err)
|
||
}
|
||
return vm.ToValue(s)
|
||
})
|
||
set("getStringList", func(call goja.FunctionCall) goja.Value {
|
||
if a == nil {
|
||
return goja.Null()
|
||
}
|
||
list, err := a.GetStringList(stringArgOr(call, 0, ""), nil, false)
|
||
if err != nil {
|
||
bridgeErr("getStringList", err)
|
||
}
|
||
return vm.ToValue(list)
|
||
})
|
||
set("getElements", func(call goja.FunctionCall) goja.Value {
|
||
if a == nil {
|
||
return goja.Null()
|
||
}
|
||
els, err := a.GetElements(stringArgOr(call, 0, ""))
|
||
if err != nil {
|
||
bridgeErr("getElements", err)
|
||
}
|
||
out := make([]any, 0, len(els))
|
||
for _, el := range els {
|
||
out = append(out, resultString(el))
|
||
}
|
||
return vm.ToValue(out)
|
||
})
|
||
set("put", func(call goja.FunctionCall) goja.Value {
|
||
key := stringArg(call, 0)
|
||
val := stringArgOr(call, 1, "")
|
||
if a != nil {
|
||
return vm.ToValue(a.Put(key, val))
|
||
}
|
||
r.vars[key] = val
|
||
return vm.ToValue(val)
|
||
})
|
||
|
||
// ── 编码(对应 JsEncodeUtils / JsExtensions) ──
|
||
set("md5Encode", func(call goja.FunctionCall) goja.Value {
|
||
return vm.ToValue(md5Hex(stringArg(call, 0), false))
|
||
})
|
||
set("md5Encode16", func(call goja.FunctionCall) goja.Value {
|
||
return vm.ToValue(md5Hex(stringArg(call, 0), true))
|
||
})
|
||
set("base64Decode", func(call goja.FunctionCall) goja.Value {
|
||
s, err := base64DecodeString(stringArg(call, 0))
|
||
if err != nil {
|
||
bridgeErr("base64Decode", err)
|
||
}
|
||
return vm.ToValue(s)
|
||
})
|
||
set("base64DecodeToByteArray", func(call goja.FunctionCall) goja.Value {
|
||
b, err := base64DecodeBytes(stringArg(call, 0))
|
||
if err != nil {
|
||
bridgeErr("base64DecodeToByteArray", err)
|
||
}
|
||
return vm.ToValue(vm.NewArrayBuffer(b))
|
||
})
|
||
set("base64Encode", func(call goja.FunctionCall) goja.Value {
|
||
return vm.ToValue(base64.StdEncoding.EncodeToString([]byte(stringArg(call, 0))))
|
||
})
|
||
set("hexDecodeToString", func(call goja.FunctionCall) goja.Value {
|
||
b, err := hex.DecodeString(strings.TrimSpace(stringArg(call, 0)))
|
||
if err != nil {
|
||
bridgeErr("hexDecodeToString", err)
|
||
}
|
||
return vm.ToValue(string(b))
|
||
})
|
||
set("hexDecodeToByteArray", func(call goja.FunctionCall) goja.Value {
|
||
b, err := hex.DecodeString(strings.TrimSpace(stringArg(call, 0)))
|
||
if err != nil {
|
||
bridgeErr("hexDecodeToByteArray", err)
|
||
}
|
||
return vm.ToValue(vm.NewArrayBuffer(b))
|
||
})
|
||
set("hexEncodeToString", func(call goja.FunctionCall) goja.Value {
|
||
return vm.ToValue(hex.EncodeToString([]byte(stringArg(call, 0))))
|
||
})
|
||
set("strToBytes", func(call goja.FunctionCall) goja.Value {
|
||
return vm.ToValue(vm.NewArrayBuffer([]byte(stringArg(call, 0))))
|
||
})
|
||
set("bytesToStr", func(call goja.FunctionCall) goja.Value {
|
||
if len(call.Arguments) == 0 || goja.IsUndefined(call.Arguments[0]) || goja.IsNull(call.Arguments[0]) {
|
||
return vm.ToValue("")
|
||
}
|
||
if buf, ok := call.Arguments[0].Export().([]byte); ok {
|
||
return vm.ToValue(string(buf))
|
||
}
|
||
return vm.ToValue(call.Arguments[0].String())
|
||
})
|
||
set("encodeURI", func(call goja.FunctionCall) goja.Value {
|
||
return vm.ToValue(javaURLEncode(stringArg(call, 0), stringArgOr(call, 1, "utf-8")))
|
||
})
|
||
set("timeFormat", func(call goja.FunctionCall) goja.Value {
|
||
ts := int64(call.Arguments[0].ToFloat())
|
||
format := stringArgOr(call, 1, "yyyy-MM-dd HH:mm")
|
||
return vm.ToValue(javaTimeFormat(ts, format, 0))
|
||
})
|
||
set("timeFormatUTC", func(call goja.FunctionCall) goja.Value {
|
||
ts := int64(call.Arguments[0].ToFloat())
|
||
format := stringArgOr(call, 1, "yyyy-MM-dd HH:mm")
|
||
sh := int64(0)
|
||
if len(call.Arguments) > 2 {
|
||
sh = int64(call.Arguments[2].ToFloat())
|
||
}
|
||
return vm.ToValue(javaTimeFormat(ts, format, sh))
|
||
})
|
||
|
||
// ── 加解密(对应 JsEncodeUtils) ──
|
||
set("createSymmetricCrypto", func(call goja.FunctionCall) goja.Value {
|
||
transformation := stringArgOr(call, 0, "AES/CBC/PKCS5Padding")
|
||
key := stringArgOr(call, 1, "")
|
||
iv := stringArgOr(call, 2, "")
|
||
cipher, err := newSymmetricCipher(transformation, key, iv)
|
||
if err != nil {
|
||
bridgeErr("createSymmetricCrypto", err)
|
||
}
|
||
return newCipherObject(vm, cipher)
|
||
})
|
||
aesVariant := func(mode string) func(goja.FunctionCall) goja.Value {
|
||
return func(call goja.FunctionCall) goja.Value {
|
||
data := stringArg(call, 0)
|
||
key := stringArgOr(call, 1, "")
|
||
defaultTrans := mode
|
||
trans := stringArgOr(call, 2, defaultTrans)
|
||
iv := stringArgOr(call, 3, "")
|
||
cipher, err := newSymmetricCipher(trans, key, iv)
|
||
if err != nil {
|
||
bridgeErr(mode, err)
|
||
}
|
||
out, err := cipher.decryptAuto(data)
|
||
if err != nil {
|
||
bridgeErr(mode, err)
|
||
}
|
||
return vm.ToValue(string(out))
|
||
}
|
||
}
|
||
set("aesDecodeToString", aesVariant("AES/CBC/PKCS5Padding"))
|
||
set("aesBase64DecodeToString", aesVariant("AES/CBC/PKCS5Padding"))
|
||
set("aesBase64DecodeToByteArray", aesVariant("AES/CBC/PKCS5Padding"))
|
||
set("desDecodeToString", aesVariant("DES/CBC/PKCS5Padding"))
|
||
set("desBase64DecodeToString", aesVariant("DES/CBC/PKCS5Padding"))
|
||
encryptStr := func(data, key, trans, iv string) string {
|
||
cipher, err := newSymmetricCipher(trans, key, iv)
|
||
if err != nil {
|
||
bridgeErr("encrypt", err)
|
||
}
|
||
out, err := cipher.encrypt([]byte(data))
|
||
if err != nil {
|
||
bridgeErr("encrypt", err)
|
||
}
|
||
return string(out)
|
||
}
|
||
set("aesEncodeToString", func(call goja.FunctionCall) goja.Value {
|
||
return vm.ToValue(encryptStr(stringArg(call, 0), stringArgOr(call, 1, ""), stringArgOr(call, 2, "AES/CBC/PKCS5Padding"), stringArgOr(call, 3, "")))
|
||
})
|
||
set("desEncodeToString", func(call goja.FunctionCall) goja.Value {
|
||
return vm.ToValue(encryptStr(stringArg(call, 0), stringArgOr(call, 1, ""), stringArgOr(call, 2, "DES/CBC/PKCS5Padding"), stringArgOr(call, 3, "")))
|
||
})
|
||
encryptB64 := func(data, key, trans, iv string) string {
|
||
cipher, err := newSymmetricCipher(trans, key, iv)
|
||
if err != nil {
|
||
bridgeErr("encrypt", err)
|
||
}
|
||
out, err := cipher.encrypt([]byte(data))
|
||
if err != nil {
|
||
bridgeErr("encrypt", err)
|
||
}
|
||
return base64.StdEncoding.EncodeToString(out)
|
||
}
|
||
set("aesEncodeToBase64String", func(call goja.FunctionCall) goja.Value {
|
||
return vm.ToValue(encryptB64(stringArg(call, 0), stringArgOr(call, 1, ""), stringArgOr(call, 2, "AES/CBC/PKCS5Padding"), stringArgOr(call, 3, "")))
|
||
})
|
||
set("desEncodeToBase64String", func(call goja.FunctionCall) goja.Value {
|
||
return vm.ToValue(encryptB64(stringArg(call, 0), stringArgOr(call, 1, ""), stringArgOr(call, 2, "DES/CBC/PKCS5Padding"), stringArgOr(call, 3, "")))
|
||
})
|
||
set("tripleDESDecodeStr", func(call goja.FunctionCall) goja.Value {
|
||
data := stringArg(call, 0)
|
||
key := stringArgOr(call, 1, "")
|
||
trans := "DESede/CBC/PKCS5Padding"
|
||
var ivv string
|
||
switch {
|
||
case len(call.Arguments) > 4: // (data, key, mode, padding, iv)
|
||
trans = "DESede/" + stringArgOr(call, 2, "CBC") + "/" + stringArgOr(call, 3, "PKCS5Padding")
|
||
ivv = stringArgOr(call, 4, "")
|
||
case len(call.Arguments) > 2: // (data, key, transformation, iv?)
|
||
trans = stringArgOr(call, 2, trans)
|
||
ivv = stringArgOr(call, 3, "")
|
||
}
|
||
cipher, err := newSymmetricCipher(trans, key, ivv)
|
||
if err != nil {
|
||
bridgeErr("tripleDESDecodeStr", err)
|
||
}
|
||
out, err := cipher.decryptAuto(data)
|
||
if err != nil {
|
||
bridgeErr("tripleDESDecodeStr", err)
|
||
}
|
||
return vm.ToValue(string(out))
|
||
})
|
||
set("digestHex", func(call goja.FunctionCall) goja.Value {
|
||
h, err := digestHash(stringArgOr(call, 1, "MD5"))
|
||
if err != nil {
|
||
bridgeErr("digestHex", err)
|
||
}
|
||
h.Write([]byte(stringArg(call, 0)))
|
||
return vm.ToValue(hex.EncodeToString(h.Sum(nil)))
|
||
})
|
||
set("digestBase64Str", func(call goja.FunctionCall) goja.Value {
|
||
h, err := digestHash(stringArgOr(call, 1, "MD5"))
|
||
if err != nil {
|
||
bridgeErr("digestBase64Str", err)
|
||
}
|
||
h.Write([]byte(stringArg(call, 0)))
|
||
return vm.ToValue(base64.StdEncoding.EncodeToString(h.Sum(nil)))
|
||
})
|
||
set("HMacHex", func(call goja.FunctionCall) goja.Value {
|
||
h, err := hmacHash(stringArgOr(call, 1, "HmacSHA256"), []byte(stringArgOr(call, 2, "")))
|
||
if err != nil {
|
||
bridgeErr("HMacHex", err)
|
||
}
|
||
h.Write([]byte(stringArg(call, 0)))
|
||
return vm.ToValue(hex.EncodeToString(h.Sum(nil)))
|
||
})
|
||
set("HMacBase64", func(call goja.FunctionCall) goja.Value {
|
||
h, err := hmacHash(stringArgOr(call, 1, "HmacSHA256"), []byte(stringArgOr(call, 2, "")))
|
||
if err != nil {
|
||
bridgeErr("HMacBase64", err)
|
||
}
|
||
h.Write([]byte(stringArg(call, 0)))
|
||
return vm.ToValue(base64.StdEncoding.EncodeToString(h.Sum(nil)))
|
||
})
|
||
|
||
// ── 其他 ──
|
||
set("log", func(call goja.FunctionCall) goja.Value {
|
||
if r.cfg.Log != nil {
|
||
r.cfg.Log(call.Arguments[0].String())
|
||
}
|
||
return goja.Null()
|
||
})
|
||
set("getWebViewUA", func(call goja.FunctionCall) goja.Value {
|
||
return vm.ToValue(defaultWebViewUA)
|
||
})
|
||
set("t2s", func(call goja.FunctionCall) goja.Value { return vm.ToValue(stringArg(call, 0)) })
|
||
set("s2t", func(call goja.FunctionCall) goja.Value { return vm.ToValue(stringArg(call, 0)) })
|
||
set("htmlFormat", func(call goja.FunctionCall) goja.Value { return vm.ToValue(stringArg(call, 0)) })
|
||
|
||
// ── 书源会话状态(legado 中 `java` 与 `source` 是同一对象) ──
|
||
if r.state != nil {
|
||
bindSourceState(vm, set, r.state, r.cfg.SourceProps)
|
||
}
|
||
|
||
// ── 宿主交互:服务端无 UI,转为可回传前端的提示 / 待打开链接 ──
|
||
if r.state != nil {
|
||
toast := func(call goja.FunctionCall) goja.Value {
|
||
if len(call.Arguments) > 0 {
|
||
r.state.Toast(call.Arguments[0].String())
|
||
}
|
||
return goja.Null()
|
||
}
|
||
set("toast", toast)
|
||
set("longToast", toast)
|
||
// startBrowser(url, title):记录待打开地址,前端可代开新标签。
|
||
set("startBrowser", func(call goja.FunctionCall) goja.Value {
|
||
r.state.OpenBrowser(stringArg(call, 0), stringArgOr(call, 1, ""))
|
||
return goja.Null()
|
||
})
|
||
// startBrowserAwait:服务端无 WebView,无法等待人工校验。
|
||
// 记录地址后抛出明确错误,避免书源逻辑误把空 body 当成功。
|
||
set("startBrowserAwait", func(call goja.FunctionCall) goja.Value {
|
||
url := stringArg(call, 0)
|
||
r.state.OpenBrowser(url, stringArgOr(call, 1, ""))
|
||
panic(vm.ToValue("java.startBrowserAwait: 服务端无浏览器,需要人工操作的页面请手动打开:" + url))
|
||
})
|
||
}
|
||
// 设备标识:部分源用 deviceID/androidId 做"是否支持该环境"探测,
|
||
// 成功返回会让源走安卓分支,这里统一以异常告知不支持并回退到通用分支。
|
||
for _, name := range []string{"deviceID", "androidId"} {
|
||
set(name, func(call goja.FunctionCall) goja.Value {
|
||
panic(vm.ToValue("java." + name + ": 服务端无设备标识"))
|
||
})
|
||
}
|
||
// 刷新发现页 / 打开界面:纯 UI 动作,服务端空实现。
|
||
for _, name := range []string{"refreshExplore", "open", "showBrowser", "reLoginView", "qread"} {
|
||
set(name, func(call goja.FunctionCall) goja.Value { return goja.Null() })
|
||
}
|
||
|
||
// 需要真正无头浏览器/本地文件系统的能力:明确抛出不支持
|
||
unsupported := func(name string) func(goja.FunctionCall) goja.Value {
|
||
return func(call goja.FunctionCall) goja.Value {
|
||
panic(vm.ToValue("java." + name + " 需要浏览器或本地文件能力,服务端不支持"))
|
||
}
|
||
}
|
||
for _, name := range []string{
|
||
"webView", "webViewGetSource", "webViewGetOverrideUrl",
|
||
"openVideoPlayer", "getVerificationCode",
|
||
"importScript", "cacheFile", "downloadFile", "readFile", "readTxtFile", "deleteFile",
|
||
"unzipFile", "un7zFile", "unrarFile", "unArchiveFile", "getTxtInFolder",
|
||
"getZipStringContent", "getZipByteArrayContent",
|
||
"getRarStringContent", "get7zStringContent",
|
||
} {
|
||
set(name, unsupported(name))
|
||
}
|
||
|
||
return o
|
||
}
|
||
|
||
const defaultWebViewUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
|
||
|
||
func md5Hex(s string, short bool) string {
|
||
sum := md5.Sum([]byte(s))
|
||
h := hex.EncodeToString(sum[:])
|
||
if short {
|
||
return h[8:24]
|
||
}
|
||
return h
|
||
}
|
||
|
||
func digestHash(algorithm string) (hash.Hash, error) {
|
||
switch strings.ToUpper(strings.NewReplacer("-", "", "_", "").Replace(algorithm)) {
|
||
case "MD5":
|
||
return md5.New(), nil
|
||
case "SHA1":
|
||
return sha1.New(), nil
|
||
case "SHA256":
|
||
return sha256.New(), nil
|
||
case "SHA384":
|
||
return sha512.New384(), nil
|
||
case "SHA512":
|
||
return sha512.New(), nil
|
||
default:
|
||
return nil, fmt.Errorf("不支持的摘要算法: %s", algorithm)
|
||
}
|
||
}
|
||
|
||
func hmacHash(algorithm string, key []byte) (hash.Hash, error) {
|
||
switch strings.ToLower(strings.NewReplacer("-", "", "_", "").Replace(algorithm)) {
|
||
case "hmacmd5":
|
||
return hmac.New(md5.New, key), nil
|
||
case "hmacsha1":
|
||
return hmac.New(sha1.New, key), nil
|
||
case "hmacsha256":
|
||
return hmac.New(sha256.New, key), nil
|
||
case "hmacsha512":
|
||
return hmac.New(sha512.New, key), nil
|
||
default:
|
||
return nil, fmt.Errorf("不支持的 HMAC 算法: %s", algorithm)
|
||
}
|
||
}
|