智能卡的XML文件DSig(数字签名)无效
我正在尝试使用智能卡签署XML文件。我使用的物理智能卡称为SafeNet eToken
和Yubikey from Yubico
。我也使用全数字智能卡。对于这个问题,我将使用SafeNet eToken
。此智能卡上有一个证书,当您插入USB记忆棒时,该证书在Windows Certificate Store
中可见。它是一个RSA Certificate
,有公钥和私钥。您可以将证书安装到机器上,但是您需要USB记忆棒作为私钥。此外,您还需要私钥的PIN,并且必须安装正确的驱动程序。
现在我有了一个C#方法,它通过使用保存XML文件的路径将其加载到XmlDocument
对象并将其作为参数传递。另外,我使用智能卡的名称从Windows Certificate Store
获取证书,并将此证书作为参数传递。
当我执行该方法时,SafeNet eToken
的客户端启动并要求输入PIN。当正确输入PIN时,文件将使用智能卡的私钥进行签名。XML文件被保存在我写的路径,当我读它一切看起来很好。可以看到,XML文件已签名,并且正典化也有效。
但是,当我尝试在Chilkat Tools上验证XML数字签名时,我每次都得到一条错误消息,说我的签名无效。我尝试了.NET本身和其他一些工具的验证方法,我总是得到相同的结果。签名无效。我用哪张智能卡都没关系。看起来它可以与所有人一起工作,但是在最后签名无效。
现在我很难找出为什么会发生这种情况。看看答案,找出原因。
/// <summary>
/// Signs an XMLDocument with additional Keyinfo with a Smart Card.
/// The Smart Card can be physical like a Yubikey from Yubico or completely digitally.
/// Access to the private key of the certificate is mandatory.
/// </summary>
/// <param name="claimsDocument"></param>
/// <param name="cert"></param>
/// <returns></returns>
public void SignClaimsDocument(XmlDocument claimsDocument, X509Certificate2 cert)
{
// Variables
const string outputPath = @"C:\Example\User\Files\signed_claims";
SignedXml signedClaims = new SignedXml(claimsDocument)
{
// Add the key to the SignedXml document.
SigningKey = cert.GetRSAPrivateKey()
};
// Create a reference to be signed. Letting the Uri variable empty means that the whole XML will be signed.
Reference reference = new Reference
{
Uri = ""
};
// Add an enveloped transformation to the reference.
reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
// Add canonicalization transformation to the reference using C14N Type.
reference.AddTransform(new XmlDsigC14NTransform());
// Add the reference to the SignedXml object.
signedClaims.AddReference(reference);
// Include the public key of the certificate in the assertion.
KeyInfo keyinfo = new KeyInfo();
// Add Keyinfo
KeyInfoX509Data kiData = new KeyInfoX509Data();
kiData.AddSubjectName(cert.SubjectName.Name);
kiData.AddCertificate(cert);
keyinfo.AddClause(kiData);
// RSAKeyValue
RSACryptoServiceProvider rsaprovider = (RSACryptoServiceProvider)cert.PublicKey.Key;
RSAKeyValue rkv = new RSAKeyValue(rsaprovider);
keyinfo.AddClause(rkv);
signedClaims.KeyInfo = keyinfo;
try
{
// Computes the signature using signedClaims.SigningKey. This happens because we give no parameter to the ComputeSignature method.
signedClaims.ComputeSignature();
}
catch (CryptographicException cex)
{
Console.WriteLine(cex);
throw cex;
}
// Get the XML representation of the signature and save
// it to an XmlElement object.
XmlElement xmlDigitalSignature = signedClaims.GetXml();
// Appends the element to the XML document.
claimsDocument.DocumentElement?.AppendChild(claimsDocument.ImportNode(xmlDigitalSignature, true));
// Saves the signed XML Claims File at the given path
claimsDocument.Save(outputPath + "_" + DateTime.Now.ToString("mm/HH-dd/MM/yyyy") + ".xml");
}
1条答案
按热度按时间h43kikqp1#
Signature无效原因
您需要知道,更改已签名XML文件的内容当然会使签名无效。过了一会儿,我发现问题出现在我的方法的最后一行。
当保存
XmlDocument
类型的claimsDocument
变量时,它使用XmlDocument
类中的.Save(outputPath)
方法。在我的方法中看起来像这样:
当我们检查
.Save(outputPath)
方法的代码时,我们看到一些东西解释了一切。保存方式:
这里可以看到if子句,它检查
preserveWhitespace
bool变量是否为false。默认情况下,这个变量是false
,if-clause中的代码将被执行。在这个if子句中,XML文件被格式化/美化。这些只是空白和新行。问题是空格和新行都是字符,由于这种格式化发生在签名之后,XML中的数据被更改,使签名无效。
在使用
.Save(outputPath)
方法之前,只需将preserveWhitespace
设置为true
一行,就可以轻松解决这个问题。如下:
完全固定方式:
现在,由于
PreserveWhitespace
变量是true
,签名后没有格式化,因此签名的XML文件没有修改,这意味着签名是有效的。Chilkat XML DSig Verification批准签名有效,我也尝试了其他所有工具。此外,来自我国的API验证签名有效,这是最重要的。**快速摘要:**默认情况下,.NET中XmlDocument类的保存方法本身会美化XML文件。由于签名过程发生在我们保存已签名的XML文件之前,因此格式化发生在签名之后,这使得签名无效。即使格式化只添加新行和空格,这些也被视为字符。因此,您需要将要签名和保存的
XmlDocument
对象的bool variable PreserveWhitespace
设置为true
。