代码之家  ›  专栏  ›  技术社区  ›  Johannes

如何使用X509证书对xml进行签名,将摘要值和签名添加到xml模板

  •  3
  • Johannes  · 技术社区  · 9 年前

    我对C#很陌生,尤其是对X509签名很陌生。我有一个xml模板,我必须在其中添加使用的证书(完成),并对时间戳(TS-1)、二进制安全令牌和主体(id-1)进行签名。 此外,我需要用这3个元素的摘要值写入(例如替换占位符)并添加签名值。

    enter image description here

    然而,我并不真正理解这个概念,例如如何做到这一点。我读了几个网站,例如。 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节点
    • 如何计算签名值,因为它“包含”所有3个引用的签名信息???

    如你所见,我缺少一些一般背景和理解。如果你能帮我,那就太好了!

    非常感谢。

    1 回复  |  直到 8 年前
        1
  •  4
  •   sAlfaro    8 年前

    您不必手动创建签名的节点,在计算完签名后,您可以调用GetXml方法(您已经在做了:signedXml.GetXml()),这将返回如下内容:

    <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