如何使用 X509 证书签署 xml,将摘要值和签名添加到 xml 模板
How to sign xml with X509 cert, add digest value and signature to xml template
我是 C# 的新手,尤其是使用 X509 签名的新手。我有一个 xml 模板,我必须在其中添加使用的证书(完成)并签署时间戳 (TS-1)、二进制安全令牌和正文 (id-1)。
此外,我需要用这 3 个元素的摘要值编写(例如替换占位符)并添加签名值。
但是,我不是很理解这个概念,例如这该怎么做。我阅读了几个网站,例如signing a xml document with x509 certificate
但我无法使代码适应我的问题。
这是我尝试过的:
public static string SignXml(string template)
{
XmlDocument document = new XmlDocument();
document.LoadXml(template);
// define elements that will be signed
XmlNode securityToken = null;
XmlNode validityPeriod = null;
XmlNode body = null;
XmlNode signedInfo = null;
XmlNode signatureValue = null;
XmlNodeList digestTags = null;
XmlNamespaceManager namespaces = new XmlNamespaceManager(document.NameTable);
namespaces.AddNamespace("ns", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
namespaces.AddNamespace("nu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
namespaces.AddNamespace("bo", "http://schemas.xmlsoap.org/soap/envelope/");
namespaces.AddNamespace("si", "http://www.w3.org/2000/09/xmldsig#");
namespaces.AddNamespace("sinfo", "soapenv xd xe");
document.LoadXml(template);
//XmlNode idNode = document.SelectSingleNode("/My_RootNode/ns:id", namespaces);
securityToken = document.SelectSingleNode("descendant::ns:BinarySecurityToken", namespaces);
validityPeriod = document.SelectSingleNode("descendant::nu:Timestamp", namespaces);
body = document.SelectSingleNode("descendant::bo:Body", namespaces);
signedInfo = document.SelectSingleNode("descendant::si:SignedInfo", namespaces);
signatureValue = document.SelectSingleNode("descendant::si::sinfo:SignatureValue", namespaces);
digestTags = document.SelectNodes("descendant::si:DigestValue", namespaces);
// add the digests (to know where to write the digests)
String nodeName = null;
for (int counter = 0; counter < digestTags.Count; counter++)
{
nodeName = digestTags[counter].FirstChild.InnerText;
if (WebserviceConstants.PLACEHOLDER_AUTHNREQUEST_DIGEST.Equals(nodeName))
{
generateDigest(body, digestTags[counter]);
}
else if (WebserviceConstants.PLACEHOLDER_CERTIFICATE_DIGEST.Equals(nodeName))
{
generateDigest(securityToken, digestTags[counter]);
}
else if (WebserviceConstants.PLACEHOLDER_TIMESTAMP_TAG_DIGEST.Equals(nodeName))
{
generateDigest(validityPeriod, digestTags[counter]);
}
}
SignedXml signedXml = new SignedXml(document);
X509Certificate2 cert = new X509Certificate2();
cert = getbase();
signedXml.SigningKey = cert.PrivateKey;
// Create a reference to be signed.
Reference reference = new Reference();
reference.Uri = "#TS-1";
// Add an enveloped transformation to the reference.
XmlDsigEnvelopedSignatureTransform env =
new XmlDsigEnvelopedSignatureTransform(true);
reference.AddTransform(env);
//canonicalize
XmlDsigC14NTransform c14t = new XmlDsigC14NTransform();
reference.AddTransform(c14t);
KeyInfo keyInfo = new KeyInfo();
KeyInfoX509Data keyInfoData = new KeyInfoX509Data(cert);
KeyInfoName kin = new KeyInfoName();
kin.Value = "Public key of certificate";
RSACryptoServiceProvider rsaprovider = (RSACryptoServiceProvider)cert.PublicKey.Key;
RSAKeyValue rkv = new RSAKeyValue(rsaprovider);
keyInfo.AddClause(kin);
keyInfo.AddClause(rkv);
keyInfo.AddClause(keyInfoData);
signedXml.KeyInfo = keyInfo;
// Add the reference to the SignedXml object.
signedXml.AddReference(reference);
// Compute the signature.
signedXml.ComputeSignature();
// Get the XML representation of the signature and save
// it to an XmlElement object.
XmlElement xmlDigitalSignature = signedXml.GetXml();
document.DocumentElement.AppendChild(
document.ImportNode(xmlDigitalSignature, true));
document.DocumentElement.AppendChild(document.ImportNode(xmlDigitalSignature, true));
return document.OuterXml;
}
}
我在质疑自己:
- 如何获取摘要值以及如何将其写入对应的xml节点
- 如何计算签名值,因为它 "contains" 所有 3 个引用的签名信息???
如您所见,我缺少一些一般背景和理解。如果你能帮助我,那就太好了!
谢谢
您不必手动创建签名的节点,在计算签名后调用 GetXml 方法(您已经在这样做:signedXml.GetXml()),这将 return 像这样:
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>zRSPtja5EtX7hVbyJ11EjoYTRDk=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>Ua1/WP28WzfXaxUj....</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIIF3jCCBUegAwIBAgIEPQa1....</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
那么您只需替换 xml 模板上的整个签名节点。
--请记住,SignedXml 现在将为您提供该结构,我将回答您的问题
您的第一个问题是关于您参考文献的摘要值吗?如果是这样,当您调用 ComputeSignature 方法时,它将对其进行计算并将其添加到相应的 xml 节点。
签名值是在您计算签名时计算的,您不必自己计算。
当您调用 ComputeSignature 方法时,它所做的是获取 SignedInfo 节点并消化它。您的引用在此节点内,因此您将获得包含所有引用信息的签名值
这就是 ComputeSignature 方法如何获取 signedinfo 节点的摘要值,使用该值计算签名值:
XmlElement e = this.SignedInfo.GetXml(); //get the signedinfo nodes
document.AppendChild(document.ImportNode(e, true));
Transform canonicalizationMethodObject=this.SignedInfo.CanonicalizationMethodObject;
canonicalizationMethodObject.LoadInput(document);
canonicalizationMethodObject.GetDigestedOutput(hash); //digest the signedinfo node
我是 C# 的新手,尤其是使用 X509 签名的新手。我有一个 xml 模板,我必须在其中添加使用的证书(完成)并签署时间戳 (TS-1)、二进制安全令牌和正文 (id-1)。 此外,我需要用这 3 个元素的摘要值编写(例如替换占位符)并添加签名值。
但是,我不是很理解这个概念,例如这该怎么做。我阅读了几个网站,例如signing a xml document with x509 certificate 但我无法使代码适应我的问题。
这是我尝试过的:
public static string SignXml(string template)
{
XmlDocument document = new XmlDocument();
document.LoadXml(template);
// define elements that will be signed
XmlNode securityToken = null;
XmlNode validityPeriod = null;
XmlNode body = null;
XmlNode signedInfo = null;
XmlNode signatureValue = null;
XmlNodeList digestTags = null;
XmlNamespaceManager namespaces = new XmlNamespaceManager(document.NameTable);
namespaces.AddNamespace("ns", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
namespaces.AddNamespace("nu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
namespaces.AddNamespace("bo", "http://schemas.xmlsoap.org/soap/envelope/");
namespaces.AddNamespace("si", "http://www.w3.org/2000/09/xmldsig#");
namespaces.AddNamespace("sinfo", "soapenv xd xe");
document.LoadXml(template);
//XmlNode idNode = document.SelectSingleNode("/My_RootNode/ns:id", namespaces);
securityToken = document.SelectSingleNode("descendant::ns:BinarySecurityToken", namespaces);
validityPeriod = document.SelectSingleNode("descendant::nu:Timestamp", namespaces);
body = document.SelectSingleNode("descendant::bo:Body", namespaces);
signedInfo = document.SelectSingleNode("descendant::si:SignedInfo", namespaces);
signatureValue = document.SelectSingleNode("descendant::si::sinfo:SignatureValue", namespaces);
digestTags = document.SelectNodes("descendant::si:DigestValue", namespaces);
// add the digests (to know where to write the digests)
String nodeName = null;
for (int counter = 0; counter < digestTags.Count; counter++)
{
nodeName = digestTags[counter].FirstChild.InnerText;
if (WebserviceConstants.PLACEHOLDER_AUTHNREQUEST_DIGEST.Equals(nodeName))
{
generateDigest(body, digestTags[counter]);
}
else if (WebserviceConstants.PLACEHOLDER_CERTIFICATE_DIGEST.Equals(nodeName))
{
generateDigest(securityToken, digestTags[counter]);
}
else if (WebserviceConstants.PLACEHOLDER_TIMESTAMP_TAG_DIGEST.Equals(nodeName))
{
generateDigest(validityPeriod, digestTags[counter]);
}
}
SignedXml signedXml = new SignedXml(document);
X509Certificate2 cert = new X509Certificate2();
cert = getbase();
signedXml.SigningKey = cert.PrivateKey;
// Create a reference to be signed.
Reference reference = new Reference();
reference.Uri = "#TS-1";
// Add an enveloped transformation to the reference.
XmlDsigEnvelopedSignatureTransform env =
new XmlDsigEnvelopedSignatureTransform(true);
reference.AddTransform(env);
//canonicalize
XmlDsigC14NTransform c14t = new XmlDsigC14NTransform();
reference.AddTransform(c14t);
KeyInfo keyInfo = new KeyInfo();
KeyInfoX509Data keyInfoData = new KeyInfoX509Data(cert);
KeyInfoName kin = new KeyInfoName();
kin.Value = "Public key of certificate";
RSACryptoServiceProvider rsaprovider = (RSACryptoServiceProvider)cert.PublicKey.Key;
RSAKeyValue rkv = new RSAKeyValue(rsaprovider);
keyInfo.AddClause(kin);
keyInfo.AddClause(rkv);
keyInfo.AddClause(keyInfoData);
signedXml.KeyInfo = keyInfo;
// Add the reference to the SignedXml object.
signedXml.AddReference(reference);
// Compute the signature.
signedXml.ComputeSignature();
// Get the XML representation of the signature and save
// it to an XmlElement object.
XmlElement xmlDigitalSignature = signedXml.GetXml();
document.DocumentElement.AppendChild(
document.ImportNode(xmlDigitalSignature, true));
document.DocumentElement.AppendChild(document.ImportNode(xmlDigitalSignature, true));
return document.OuterXml;
}
}
我在质疑自己:
- 如何获取摘要值以及如何将其写入对应的xml节点
- 如何计算签名值,因为它 "contains" 所有 3 个引用的签名信息???
如您所见,我缺少一些一般背景和理解。如果你能帮助我,那就太好了!
谢谢
您不必手动创建签名的节点,在计算签名后调用 GetXml 方法(您已经在这样做:signedXml.GetXml()),这将 return 像这样:
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>zRSPtja5EtX7hVbyJ11EjoYTRDk=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>Ua1/WP28WzfXaxUj....</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIIF3jCCBUegAwIBAgIEPQa1....</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
那么您只需替换 xml 模板上的整个签名节点。
--请记住,SignedXml 现在将为您提供该结构,我将回答您的问题
您的第一个问题是关于您参考文献的摘要值吗?如果是这样,当您调用 ComputeSignature 方法时,它将对其进行计算并将其添加到相应的 xml 节点。
签名值是在您计算签名时计算的,您不必自己计算。
当您调用 ComputeSignature 方法时,它所做的是获取 SignedInfo 节点并消化它。您的引用在此节点内,因此您将获得包含所有引用信息的签名值
这就是 ComputeSignature 方法如何获取 signedinfo 节点的摘要值,使用该值计算签名值:
XmlElement e = this.SignedInfo.GetXml(); //get the signedinfo nodes
document.AppendChild(document.ImportNode(e, true));
Transform canonicalizationMethodObject=this.SignedInfo.CanonicalizationMethodObject;
canonicalizationMethodObject.LoadInput(document);
canonicalizationMethodObject.GetDigestedOutput(hash); //digest the signedinfo node