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环境,理解成浏览器的控制台,你后半部分就像你平时浏览器控制台中调用网页中的方法一样操作。