C#支持对单个XML元素进行RSA SHA 256签名

时间:2013-06-23 08:06:12

标签: c# xml signature x509 sha256

我遇到过使用.NET Framework 4.5版的阻止程序来处理带有数字签名的XML。

我的问题是基于使用RSA SHA-256算法使用X.509证书签署单个XML元素的需要。我已经阅读了很多关于这个主题的.NET帖子,看来最初在CLR安全项目RSAPKCS1SHA256SignatureDescription.cs类中开发了一个解决方案。 RSAPKCS1SHA256SignatureDescription当然已经被整合到.net运行时中,从.NET 4.5开始,现在可以在分布式二进制System.Deployment.dll下使用。我已尝试在.NET中使用RSA SHA-256对上述解决方案进行签名,但尚未取得任何成功。

我正在尝试使用WSSE令牌签署符合Oasis ebms标准的SOAP消息。请注意,该课程的目的是为了满足Soap With Attachments(SwA)和签署个人附件。 我的代码如下

我的代码如下:

using System;
using System.Collections.Generic;
using System.IO;
using System.IdentityModel.Tokens;
using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
using System.ServiceModel.Channels;
using System.ServiceModel.Security;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
using System.Deployment.Internal.CodeSigning;

namespace TestCSharpX509CertificateRSSHA256
{
    public class SignatureSupportUtility
    {

    private bool IsSignatureContentTransform
    {
        get
        {
          return true;
          //get IsSignatureContentTransform                
        }
    }

       public SignatureSupportUtility()
       {
          Register();
       }


    private static void Register()
    {
        CryptoConfig.AddAlgorithm(typeof(RSAPKCS1SHA256SignatureDescription), "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
    }

    private void Sign(Message message, string[] elementIdsToSign, string[] attachmentsToSign, string wssNamespace, X509Certificate2 certificate)
    {
       //Prepare XML to encrypt and sign
       var element = this.PrepareEncyrptSign(message);

            bool signEntireDocument = true;
            string elementToBeSigned = string.Empty;
            var signedMessage = new XmlDocument();
            signedMessage.AppendChild(signedMessage.ImportNode(element, true));

            SignatureType signAs = SignatureType.InternallyDetached;
            signedMessage.PreserveWhitespace = false;

            OverrideSignedXml signedXml = new OverrideSignedXml(signedMessage);
            signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;

            if (elementIdsToSign != null && elementIdsToSign.Length > 0)
            {
                bool isContentTransform = this.IsSignatureContentTransform;

                foreach (string s in elementIdsToSign)
                {
                    // Create a reference to be signed.
                    Reference reference = new Reference(string.Format("#{0}", s));
                    reference.AddTransform(new XmlDsigExcC14NTransform());
                    reference.DigestMethod = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";


                    // Add the reference to the SignedXml object.
                    signedXml.AddReference(reference);
                }

                signEntireDocument = false;
            }

            // Reference attachments to sign
            if (attachmentsToSign != null && attachmentsToSign.Length > 0)
            {
                bool isContentTransform = this.IsSignatureContentTransform;

                foreach (string attachmentId in attachmentsToSign)
                {
                    // Create a reference to be signed.
                    Reference reference = new Reference(string.Format("{0}{1}", Constants.CidUriScheme, attachmentId));
                    reference.DigestMethod = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";

                    if (isContentTransform)
                    {
                        AttachmentContentSignatureTransform env = new AttachmentContentSignatureTransform();
                        reference.AddTransform(env);
                    }
                    else
                    {
                        AttachmentCompleteSignatureTransform env = new AttachmentCompleteSignatureTransform();
                        reference.AddTransform(env);
                    }

                    // Add the reference to the SignedXml object.
                    signedXml.AddReference(reference);
                }

                signEntireDocument = false;
            }

            if (signEntireDocument)
            {
                Reference reference = new Reference();
                reference.Uri = "";
                reference.DigestMethod = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";

                XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
                reference.AddTransform(env);
                signedXml.AddReference(reference);
                signAs = SignatureType.Enveloped;
            }

            string x509CertificateReferenceId = string.Format("{0}-{1}", Constants.IdAttributeName, Guid.NewGuid().ToString("N"));
            KeyInfo keyInfo = new KeyInfo();
            keyInfo.AddClause(new KeyInfoX509SecurityTokenReference(string.Format("#{0}", x509CertificateReferenceId), wssNamespace));
            signedXml.KeyInfo = keyInfo;
           signedXml.SignedInfo.SignatureMethod = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";

           RSA  key = (RSACryptoServiceProvider)certificate.PrivateKey;
           signedXML.SigningKey = key;
            CidWebRequest.Message = message;

            signedXml.ComputeSignature();
            var xmlSignature = signedXml.GetXml();
            XmlDocument unsignedEnvelopeDoc = new XmlDocument();
            unsignedEnvelopeDoc.LoadXml(message.MessageAsString); }}} 
using System;
using System.Collections.Generic;
using System.IO;
using System.IdentityModel.Tokens;
using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
using System.ServiceModel.Channels;
using System.ServiceModel.Security;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
using System.Deployment.Internal.CodeSigning;


namespace TestCSharpX509CertificateRSSHA256
{
public sealed class OverrideSignedXml : SignedXml
{
    public OverrideSignedXml()
        : base()
    {
    }

    public OverrideSignedXml(XmlDocument doc)
        : base(doc)
    {
    }

    public override XmlElement GetIdElement(XmlDocument document, string idValue)
    {
        XmlElement element = base.GetIdElement(document, idValue);

        if (element == null)
        {
            XmlNamespaceManager nsmgr = new XmlNamespaceManager(document.NameTable);
            nsmgr.AddNamespace("wsu", ="http://docs.oasis-open. org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");

            element = document.SelectSingleNode("//*[@wsu:Id=\"" + idValue + "\"]", nsmgr) as XmlElement;
        }

        return element;
    }
}

}

我的SignatureSupportUtility类中的Sign方法应该足以签署单个XML元素或整个消息,但是我一直收到一个声明不支持SHA-256的Cryptography异常。我认为此异常不应该是有效的观察RSAPKCS1SHA256SignatureDescription.cs已注册。但是,观察SignedXML类不包含SHA-256的命名空间而只包含SHA-128我开始怀疑是否支持SHA 256而不管注册。

有人可以告诉我如何最好地解决我的问题,并能够通过RSA SHA 256算法使用X.509证书签署XML吗?

2 个答案:

答案 0 :(得分:8)

我也在看Oasis ebms的东西。

我找不到我从中获取的文章,但我使用了4.5中的那个类:

public class RsaPkCs1Sha256SignatureDescription : SignatureDescription
{
    public RsaPkCs1Sha256SignatureDescription()
    {
        KeyAlgorithm = "System.Security.Cryptography.RSACryptoServiceProvider";
        DigestAlgorithm = "System.Security.Cryptography.SHA256Managed";
        FormatterAlgorithm = "System.Security.Cryptography.RSAPKCS1SignatureFormatter";
        DeformatterAlgorithm = "System.Security.Cryptography.RSAPKCS1SignatureDeformatter";
    }

    public override AsymmetricSignatureDeformatter CreateDeformatter(AsymmetricAlgorithm key)
    {
        var asymmetricSignatureDeformatter = (AsymmetricSignatureDeformatter) CryptoConfig.CreateFromName(DeformatterAlgorithm);
        asymmetricSignatureDeformatter.SetKey(key);
        asymmetricSignatureDeformatter.SetHashAlgorithm("SHA256");
        return asymmetricSignatureDeformatter;
    }

然后使用类似的东西进行签名(编辑了一些不相关的位):

    public XmlElement SignDocument(XmlDocument doc, List<string> idsToSign)
    {
        CryptoConfig.AddAlgorithm(typeof(RsaPkCs1Sha256SignatureDescription), @"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");

        var cspParams = new CspParameters(24) { KeyContainerName = "XML_DISG_RSA_KEY" };
        var key = new RSACryptoServiceProvider(cspParams);
        key.FromXmlString(_x509SecurityToken.Certificate.PrivateKey.ToXmlString(true));

        var signer = new SoapSignedXml(doc) { SigningKey = key };

        signer.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;

        var keyInfo = new KeyInfo();
        keyInfo.AddClause(new SecurityTokenReference(_x509SecurityToken, SecurityTokenReference.SerializationOptions.Embedded));

        signer.KeyInfo = keyInfo;
        signer.SignedInfo.SignatureMethod = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";

        var cn14Transform = new XmlDsigExcC14NTransform();
        string referenceDigestMethod = "http://www.w3.org/2001/04/xmlenc#sha256";

        foreach (string id in idsToSign)
        {
            var reference = new Reference("#" + id);
            reference.AddTransform(cn14Transform);
            reference.DigestMethod = referenceDigestMethod;
            signer.AddReference(reference);
        }

        signer.ComputeSignature();

        return signer.GetXml();
    }

似乎工作并在另一端验证确定。前几天使用Holodeck进行了测试,我认为它在签名元素中缺少的时间戳上失败了。

然而,附件的签名似乎是.NET中的一个真正问题 - 我不认为相关的转换是受支持的。

答案 1 :(得分:0)

不幸的是,当无法导出私钥时,Andrew的答案不适用。

我使用的是智能卡,到目前为止,我发现没有办法将SignedXML与SHA-256一起使用。在RSACryptoServiceProvider的当前实现中似乎打破了此功能。

我认为唯一的解决方案是从CSP切换到PKCS#11,然后使用BouncyCastle.Net。并重写一切。