使用x509证书签署xml文档

时间:2014-04-30 17:39:34

标签: c# xml xml-signature x509

每次我尝试发送签名的XML时,Web服务验证程序都会拒绝它。

要签署文档,我刚刚调整了Microsoft提供的示例代码:

http://msdn.microsoft.com/es-es/library/ms229745(v=vs.110).aspx

我的实施:

    public static XmlDocument FirmarXML(XmlDocument xmlDoc)
    {
        try
        {
            X509Certificate2 myCert = null;
            var store = new X509Store(StoreLocation.CurrentUser); //StoreLocation.LocalMachine fails too
            store.Open(OpenFlags.ReadOnly);
            var certificates = store.Certificates;
            foreach (var certificate in certificates)
            {
                if (certificate.Subject.Contains("xxx"))
                {
                    myCert = certificate;
                    break;
                }
            }

            if (myCert != null)
            {
                RSA rsaKey = ((RSA)myCert.PrivateKey);

                // Sign the XML document. 
                SignXml(xmlDoc, rsaKey);                    
            }

        }
        catch (Exception e)
        {
            MessageBox.Show(e.Message);
        }
        return xmlDoc;
    }


    // Sign an XML file. 
    // This document cannot be verified unless the verifying 
    // code has the key with which it was signed.
    public static void SignXml(XmlDocument xmlDoc, RSA Key)
    {
        // Check arguments.
        if (xmlDoc == null)
            throw new ArgumentException("xmlDoc");
        if (Key == null)
            throw new ArgumentException("Key");

        // Create a SignedXml object.
        SignedXml signedXml = new SignedXml(xmlDoc);

        // Add the key to the SignedXml document.
        signedXml.SigningKey = Key;

        // Create a reference to be signed.
        Reference reference = new Reference();
        reference.Uri = "";

        // Add an enveloped transformation to the reference.
        XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
        reference.AddTransform(env);

        // 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();

        // Append the element to the XML document.
        xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(xmlDigitalSignature, true));

    }

我认为我使用自己的证书遵循相同的步骤,但是它没有按预期工作。

欢迎任何建议。

3 个答案:

答案 0 :(得分:10)

服务器如何知道文档签名的证书?您好像不在签名文档中包含证书:

    KeyInfo keyInfo = new KeyInfo();
    KeyInfoX509Data keyInfoData = new KeyInfoX509Data( Key );
    keyInfo.AddClause( keyInfoData );
    signedXml.KeyInfo = keyInfo;

如果您需要更多详细信息,请参阅我的博客条目

http://www.wiktorzychla.com/2012/12/interoperable-xml-digital-signatures-c_20.html

答案 1 :(得分:3)

我已经根据Wiktor的样本做了一些更改。但是,签名仍然被Web服务拒绝。

我现在用来签名的方法是:

public static string SignXml(XmlDocument Document, X509Certificate2 cert)
{
    SignedXml signedXml = new SignedXml(Document);
    signedXml.SigningKey = cert.PrivateKey;

    // Create a reference to be signed.
    Reference reference = new Reference();
    reference.Uri = "";

    // 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));

    return Document.OuterXml;
}

签名在文档中显示如下:

<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>
        <KeyName>Public key of certificate</KeyName>
        <KeyValue>
            <RSAKeyValue>
                <Modulus>0mmCc5Rlibh44o/C/k5....</Modulus>
                <Exponent>AQAB</Exponent>
            </RSAKeyValue>
        </KeyValue>
        <X509Data>
            <X509Certificate>MIIF3jCCBUegAwIBAgIEPQa1....</X509Certificate>
        </X509Data>
    </KeyInfo>
</Signature>

当一个正确的文档被发送到服务器时,这将返回我用作模式的签名以识别必要的字段:

<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    <ds:SignedInfo>
        <ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
        <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
        <ds:Reference URI="">
            <ds:Transforms>
                <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
                <ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
            </ds:Transforms>
            <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
            <ds:DigestValue>nQOtmW194/aI+hedq+Dqp+n3IuU=</ds:DigestValue>
        </ds:Reference>
    </ds:SignedInfo>
    <ds:SignatureValue>kyp+a6arETylW8ZuucKJyd....</ds:SignatureValue>
    <ds:KeyInfo>
        <ds:KeyName>Public key of certificate</ds:KeyName>
        <ds:KeyValue>
            <ds:RSAKeyValue>
                <ds:Modulus>t0Yial28LxcIoPj16PlLIzaV...</ds:Modulus>
                <ds:Exponent>AQAB</ds:Exponent>
            </ds:RSAKeyValue>
        </ds:KeyValue>
        <ds:X509Data>
            <ds:X509Certificate>MIIHOTCCBiGgAwIBAgICVJIwDQYJKo....</ds:X509Certificate>
        </ds:X509Data>
    </ds:KeyInfo>
</ds:Signature>

在我看来是对的。但仍然无法正常工作。任何人都有任何线索?

答案 2 :(得分:0)

创建此帖子已有很长时间了。我遇到了无法验证数字签名的相同问题。

对于谁有同样的问题。在我的情况下,不同之处是XmlDocument.PreserveWhitespace选项。

当PreserveWhitespace = true时。对照公钥检查文档无效。 PreserveWhitespace = false使签名的xml有效。

我想这是在将签名的xml保存到文件并将其发送到服务器时。文档中有一些空格或特殊字符,使其无效。