Microsoft.ClearScript.V8单例模式封装,方便下次使用。

C#调用js还是没有python方便,但是微软出的V8算是目前最全的JS引擎了,完整的模拟了浏览器环境。并且支持js和C#双向通信。

下面是单例代码,可以直接调用。

using Microsoft.ClearScript;
using Microsoft.ClearScript.V8;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Text;

public sealed class SM2CryptoService : IDisposable
{
    private static readonly Lazy<SM2CryptoService> _lazyInstance =
        new Lazy<SM2CryptoService>(() => new SM2CryptoService(), LazyThreadSafetyMode.ExecutionAndPublication);

    public static SM2CryptoService Instance => _lazyInstance.Value;

    private const V8ScriptEngineFlags EngineFlags =
        V8ScriptEngineFlags.EnableTaskPromiseConversion |
        V8ScriptEngineFlags.EnableDateTimeConversion |
        V8ScriptEngineFlags.DisableGlobalMembers;

    private static readonly TimeSpan FileReadRetryDelay = TimeSpan.FromMilliseconds(100);
    private const int MaxFileReadRetries = 3;

    private readonly V8ScriptEngine _jsEngine;
    private bool _disposed;
    private readonly object _engineLock = new();

    private SM2CryptoService()
    {
        try
        {
            string jsPath = Path.Combine(
                AppDomain.CurrentDomain.BaseDirectory,
                "Areas", "GZ_RideAuth", "Js", "sm2.js");

            LogInfo($"初始化SM2服务,JS路径: {jsPath}");

            if (!File.Exists(jsPath))
                throw new FileNotFoundException("SM2加密库文件未找到", jsPath);

            string jsCode = ReadJsFileWithRetry(jsPath, MaxFileReadRetries);

            _jsEngine = new V8ScriptEngine("SM2Engine", EngineFlags)
            {
                AllowReflection = false,
                MaxRuntimeHeapSize = 1024 * 1024 * 1024 // 20MB内存限制
            };

            _jsEngine.Execute(jsCode);
            LogInfo("服务初始化成功");
        }
        catch (Exception ex)
        {
            LogError("初始化致命错误", ex);
            Dispose();
            throw new CryptographicException("SM2加密服务初始化失败", ex);
        }
    }

    public string Encrypt(string plainText)
    {
        if (_disposed)
            throw new ObjectDisposedException(GetType().FullName);

        if (string.IsNullOrWhiteSpace(plainText))
            return string.Empty;

        lock (_engineLock)
        {
            var sw = Stopwatch.StartNew();
            try
            {
                var result = _jsEngine.Script.window.SM2.sm2Encrypt(plainText);
                string cipherText = result.ToString();
                LogInfo($"加密完成,耗时: {sw.ElapsedMilliseconds}ms");
                return cipherText;
            }
            catch (ScriptEngineException ex)
            {
                LogError("JS执行错误", ex);
                throw new CryptographicException("SM2加密过程出错", ex);
            }
            catch (Exception ex)
            {
                LogError("系统错误", ex);
                throw;
            }
        }
    }

    public void Dispose()
    {
        if (_disposed) return;

        lock (_engineLock)
        {
            if (_disposed) return;

            try
            {
                _jsEngine?.Execute("window.SM2.cleanup && window.SM2.cleanup()");
                _jsEngine?.Dispose();
                LogInfo("V8引擎已释放");
            }
            catch (Exception ex)
            {
                LogError("资源释放异常", ex);
            }
            finally
            {
                _disposed = true;
                GC.SuppressFinalize(this);
            }
        }
    }

    ~SM2CryptoService() => Dispose();

    private string ReadJsFileWithRetry(string path, int maxRetries)
    {
        for (int i = 0; i < maxRetries; i++)
        {
            try
            {
                string content = File.ReadAllText(path, Encoding.UTF8);
                if (string.IsNullOrWhiteSpace(content))
                    throw new InvalidDataException("JS文件内容为空");
                return content;
            }
            catch (IOException) when (i < maxRetries - 1)
            {
                Thread.Sleep(FileReadRetryDelay * (i + 1));
            }
        }
        throw new IOException($"读取JS文件失败,已重试{maxRetries}次");
    }

    private void LogInfo(string message) =>
        Debug.WriteLine($"[{DateTime.UtcNow:u}] [SM2] [INFO] {message}");

    private void LogError(string message, Exception ex = null) =>
        Debug.WriteLine($"[{DateTime.UtcNow:u}] [SM2] [ERROR] {message}{(ex != null ? $"\n{ex}" : "")}");
}

注意:
1.先使用_jsEngine.Execute(jsCode);把JS脚本加载进入v8引擎
2.使用_jsEngine.Script.window.SM2.sm2Encrypt(plainText);进行调用,其中window.SM2.sm2Encrypt(plainText)这部分是我JS中的方法,我挂载在window上面。_jsEngine.Script这部分就是js环境,理解成浏览器的控制台,你后半部分就像你平时浏览器控制台中调用网页中的方法一样操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值