升级SafeNet驱动后C#调用ComputeSignature()报错,需替换为eTpkcs11.dll
我开发的应用原本支持通过内置证书的USB令牌签署XML文件,近期将SafeNet Authentication Client驱动从9版本升级至10版本后,程序调用ComputeSignature()函数时出现异常:
'System.Security.Cryptography.CryptographicException' occurred in mscorlib.dll,The system cannot find the file specified.
厂商提示需替换原生mscorlib.dll为指定的eTpkcs11.dll,但尝试导入该DLL时遇到编译错误:"the feature of local function attributes is not available in C#7.3",不知道该怎么着手替换。相关代码如下:
public void InsertSignature(...) { //Opens the local user certificates store X509Store store = new X509Store(StoreLocation.CurrentUser); store.Open(OpenFlags.ReadOnly); X509Certificate2Collection certs = store.Certificates; XmlDocument documento = new XmlDocument(); documento.PreserveWhitespace = true; try { documento.Load(pathDocument); SignedXml firmado = new SignedXml(documento); X509Certificate2 certificado = null; foreach (X509Certificate2 cert in certs) { if (cert.Thumbprint == huella) { certificado = new X509Certificate2(cert.GetRawCertData(), ""); certificado.PrivateKey = cert.PrivateKey; break; } } if (certificado == null) { MessageBox.Show("Check the token/certificate", "Certificate not found", MessageBoxButtons.OK); return; } store.Close(); firmado.SigningKey = certificado.PrivateKey; firmado.SignedInfo.SignatureMethod = SignedXml.XmlDsigRSASHA1Url; #region looking for the reference (the structure of the file to sign may vary) #endregion //Add the reference to the SignedXml object. firmado.AddReference(referencia); KeyInfo keyInfo = new KeyInfo(); keyInfo.AddClause(new RSAKeyValue((RSA)certificado.PrivateKey)); keyInfo.AddClause(new KeyInfoX509Data(certificado)); firmado.KeyInfo = keyInfo; //HERE HAPPENS THE ERROR/EXCEPTION firmado.ComputeSignature(); XmlElement xmlDigitalSignature = null; xmlDigitalSignature = firmado.GetXml(); #region looking the place to set the sign #endregion //Insert the signature XmlNode parent = elemento.ParentNode; parent.InsertAfter(xmlDigitalSignature, elemento); #region location routes #endregion //File is saved XmlWriter writer = XmlWriter.Create(ruta_completa, _xmlWriterSettings); documento.Save(writer); writer.Close(); } catch (Exception ex) { MessageBox.Show("Excepción producida en: " + ex.Source.ToString() + ". " + (ex.ToString()).Substring(0,107) , "Error en Try/Catch"); } }
一、解决DLL导入的编译错误
编译提示的局部函数属性问题,是因为eTpkcs11.dll的导入代码可能使用了C# 8.0及以上才支持的特性(比如局部函数上的[UnmanagedCallersOnly]属性),而你的项目当前使用的是C# 7.3。解决步骤:
- 右键项目 → 属性 → 生成 → 高级 → 语言版本,选择C# 8.0或更高版本(建议选最新稳定版)。
- 确认项目的.NET框架版本适配所选C#版本:.NET Framework 4.7.2及以上支持C# 8.0,若框架版本过低需先升级。
二、正确适配SafeNet 10的PKCS#11接口
厂商所说的“替换mscorlib.dll”表述不准确,实际是要通过PKCS#11标准接口直接调用eTpkcs11.dll,而非依赖系统默认的证书存储和PrivateKey对象。原代码依赖X509Certificate2.PrivateKey在驱动升级后无法定位令牌私钥,需重构签名逻辑:
1. 导入PKCS#11核心函数
可以通过P/Invoke手动声明eTpkcs11.dll的关键函数,示例:
[DllImport("eTpkcs11.dll", CharSet = CharSet.Ansi, SetLastError = true)] public static extern int C_Initialize(IntPtr pInitArgs); [DllImport("eTpkcs11.dll", CharSet = CharSet.Ansi, SetLastError = true)] public static extern int C_GetSlotList(bool tokenPresent, IntPtr pSlotList, ref uint pulCount); // 按需添加其他必要函数:C_OpenSession、C_Login、C_SignInit、C_Sign等
也可以使用现成的PKCS#11封装库(如Pkcs11Interop),避免手动声明大量函数。
2. 替换签名逻辑
不再通过X509Store获取私钥,改为直接通过PKCS#11接口从令牌中获取签名密钥:
- 初始化PKCS#11库,获取令牌所在的slot
- 打开会话并登录令牌(如需PIN验证)
- 查找对应的签名密钥对象
- 调用
C_Sign完成签名,将结果注入SignedXml对象
3. 自定义SignedXml签名方式
原代码的ComputeSignature()会调用系统默认实现,需改为自定义逻辑:
- 继承
SignedXml类,重写Sign方法,在方法内调用PKCS#11签名接口 - 或手动计算待签名数据的哈希,用PKCS#11签名后,将结果赋值给
SignedXml的SignatureValue属性
三、验证驱动配置
- 确保SafeNet Authentication Client 10安装正确,
eTpkcs11.dll路径已加入系统环境变量,或复制到程序运行目录 - 打开SafeNet管理工具,确认令牌及证书状态正常
内容的提问来源于stack exchange,提问作者jonhurono

