C# 如何使用 SignedXml 使用 XAdES 正确签署消息?

问题描述 投票:0回答:2

我正在为使用 XAdES 的消息签名而苦苦挣扎。我已经搜索了将我带到下面代码的解决方案。但是我尝试连接的服务告诉我签名错误。我做错了什么?

public static class Signature
    {
        #region Private fields
        public const string XmlDsigSignatureProperties = "http://uri.etsi.org/01903#SignedProperties";
        public const string XadesProofOfApproval = "http://uri.etsi.org/01903/v1.2.2#ProofOfApproval";
        public const string XadesPrefix = "xades";
        public const string SignaturePrefix = "ds";
        public const string SignatureNamespace = "http://www.w3.org/2000/09/xmldsig#";
        public const string XadesNamespaceUrl = "http://uri.etsi.org/01903/v1.3.2#";

        private const string SignatureId = "Signature";
        private const string SignaturePropertiesId = "SignedProperties";
        #endregion Private fields

        #region Public methods
        public static XmlElement SignWithXades(X509Certificate2 signingCertificate, string xml)
        {
            var xmlDocument = new XmlDocument();
            xmlDocument.LoadXml(xml);

            AddSignatureProperties(xmlDocument, signingCertificate);

            var signedXml = new SignedXml(xmlDocument);
            signedXml.Signature.Id = SignatureId;
            signedXml.SigningKey = signingCertificate.PrivateKey;

            var signatureReference = new Reference { Uri = "", };
            signatureReference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
            signedXml.AddReference(signatureReference);

            var parametersSignature = new Reference
            {
                Uri = $"#{SignaturePropertiesId}",
                Type = XmlDsigSignatureProperties,
            };
            signedXml.AddReference(parametersSignature);

            var keyInfo = new KeyInfo();
            keyInfo.AddClause(new KeyInfoX509Data(signingCertificate));
            signedXml.KeyInfo = keyInfo;

            signedXml.ComputeSignature();

            var signatureNode = signedXml.GetXml();

            AssignNameSpacePrefixToElementTree(signatureNode, SignaturePrefix);

            var signedInfoNode = signatureNode.SelectSingleNode("//*[local-name()='SignedInfo']");
            var signatureValue = signatureNode.SelectSingleNode("//*[local-name()='SignatureValue']");
            var keyInfoNode = xmlDocument.SelectSingleNode("//*[local-name()='KeyInfo']");

            var finalSignatureNode = xmlDocument.SelectSingleNode("//*[local-name()='Signature']");
            finalSignatureNode.InsertBefore(signatureValue, keyInfoNode);
            finalSignatureNode.InsertBefore(signedInfoNode, signatureValue);

            return (XmlElement)finalSignatureNode;
        }
        #endregion Public methods

        #region Private methods
        private static void AddSignatureProperties(XmlDocument document, X509Certificate2 signingCertificate)
        {
            // <Signature>
            var signatureNode = document.CreateElement(SignaturePrefix, "Signature", SignatureNamespace);
            var signatureIdAttribute = document.CreateAttribute("Id");
            signatureIdAttribute.InnerText = SignatureId;
            signatureNode.Attributes.Append(signatureIdAttribute);
            document.DocumentElement.AppendChild(signatureNode);

            AddKeyInfo(document, signatureNode, signingCertificate);
            AddCertificateObject(document, signatureNode, signingCertificate);
        }

        private static void AddKeyInfo(XmlDocument document, XmlElement signatureNode, X509Certificate2 signingCertificate)
        {
            // <KeyInfo>
            var keyInfoNode = document.CreateElement(SignaturePrefix, "KeyInfo", SignatureNamespace);
            signatureNode.AppendChild(keyInfoNode);

            // <KeyInfo><X509Data>
            var x509DataNode = document.CreateElement(SignaturePrefix, "X509Data", SignatureNamespace);
            keyInfoNode.AppendChild(x509DataNode);

            var x509CertificateNode = document.CreateElement(SignaturePrefix, "X509Certificate", SignatureNamespace);
            x509CertificateNode.InnerText = Convert.ToBase64String(signingCertificate.GetRawCertData());
            x509DataNode.AppendChild(x509CertificateNode);
        }

        private static void AddCertificateObject(XmlDocument document, XmlElement signatureNode, X509Certificate2 signingCertificate)
        {
            // <Object>
            var objectNode = document.CreateElement(SignaturePrefix, "Object", SignatureNamespace);
            signatureNode.AppendChild(objectNode);

            // <Object><QualifyingProperties>
            var qualifyingPropertiesNode = document.CreateElement(XadesPrefix, "QualifyingProperties", XadesNamespaceUrl);
            qualifyingPropertiesNode.SetAttribute("Target", $"#{SignatureId}");
            objectNode.AppendChild(qualifyingPropertiesNode);

            // <Object><QualifyingProperties><SignedProperties>
            var signedPropertiesNode = document.CreateElement(XadesPrefix, "SignedProperties", XadesNamespaceUrl);
            signedPropertiesNode.SetAttribute("Id", SignaturePropertiesId);
            qualifyingPropertiesNode.AppendChild(signedPropertiesNode);

            // <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties>
            var signedSignaturePropertiesNode = document.CreateElement(XadesPrefix, "SignedSignatureProperties", XadesNamespaceUrl);
            signedPropertiesNode.AppendChild(signedSignaturePropertiesNode);

            // <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties> </SigningTime>
            var signingTime = document.CreateElement(XadesPrefix, "SigningTime", XadesNamespaceUrl);
            signingTime.InnerText = $"{DateTime.UtcNow.ToString("s")}Z";
            signedSignaturePropertiesNode.AppendChild(signingTime);

            // <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate>
            var signingCertificateNode = document.CreateElement(XadesPrefix, "SigningCertificate", XadesNamespaceUrl);
            signedSignaturePropertiesNode.AppendChild(signingCertificateNode);

            // <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert>
            var certNode = document.CreateElement(XadesPrefix, "Cert", XadesNamespaceUrl);
            signingCertificateNode.AppendChild(certNode);

            // <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert><CertDigest>
            var certDigestNode = document.CreateElement(XadesPrefix, "CertDigest", XadesNamespaceUrl);
            certNode.AppendChild(certDigestNode);

            // <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert><CertDigest> </DigestMethod>
            var digestMethod = document.CreateElement("ds", "DigestMethod", SignedXml.XmlDsigNamespaceUrl);
            var digestMethodAlgorithmAtribute = document.CreateAttribute("Algorithm");
            digestMethodAlgorithmAtribute.InnerText = SignedXml.XmlDsigSHA1Url;
            digestMethod.Attributes.Append(digestMethodAlgorithmAtribute);
            certDigestNode.AppendChild(digestMethod);

            // <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert><CertDigest> </DigestMethod>
            var digestValue = document.CreateElement("ds", "DigestValue", SignedXml.XmlDsigNamespaceUrl);
            digestValue.InnerText = Convert.ToBase64String(signingCertificate.GetCertHash());
            certDigestNode.AppendChild(digestValue);

            // <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert><IssuerSerial>
            var issuerSerialNode = document.CreateElement(XadesPrefix, "IssuerSerial", XadesNamespaceUrl);
            certNode.AppendChild(issuerSerialNode);

            // <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert><IssuerSerial> </X509IssuerName>
            var x509IssuerName = document.CreateElement("ds", "X509IssuerName", SignedXml.XmlDsigNamespaceUrl);
            x509IssuerName.InnerText = signingCertificate.Issuer;
            issuerSerialNode.AppendChild(x509IssuerName);

            // <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert><IssuerSerial> </X509SerialNumber>
            var x509SerialNumber = document.CreateElement("ds", "X509SerialNumber", SignedXml.XmlDsigNamespaceUrl);
            x509SerialNumber.InnerText = ToDecimalString(signingCertificate.SerialNumber);
            issuerSerialNode.AppendChild(x509SerialNumber);

            // <Object><QualifyingProperties><SignedProperties><SignedDataObjectProperties>
            var signedDataObjectPropertiesNode = document.CreateElement(XadesPrefix, "SignedDataObjectProperties", XadesNamespaceUrl);
            signedPropertiesNode.AppendChild(signedDataObjectPropertiesNode);

            // <Object><QualifyingProperties><SignedProperties><SignedDataObjectProperties><CommitmentTypeIndication>
            var commitmentTypeIndicationNode = document.CreateElement(XadesPrefix, "CommitmentTypeIndication", XadesNamespaceUrl);
            signedDataObjectPropertiesNode.AppendChild(commitmentTypeIndicationNode);

            // <Object><QualifyingProperties><SignedProperties><SignedDataObjectProperties><CommitmentTypeIndication><CommitmentTypeId>
            var commitmentTypeIdNode = document.CreateElement(XadesPrefix, "CommitmentTypeId", XadesNamespaceUrl);
            commitmentTypeIndicationNode.AppendChild(commitmentTypeIdNode);

            // <Object><QualifyingProperties><SignedProperties><SignedDataObjectProperties><CommitmentTypeIndication><CommitmentTypeId><Identifier>
            var identifierNode = document.CreateElement(XadesPrefix, "Identifier", XadesNamespaceUrl);
            identifierNode.InnerText = XadesProofOfApproval;
            commitmentTypeIdNode.AppendChild(identifierNode);

            // <Object><QualifyingProperties><SignedProperties><SignedDataObjectProperties><CommitmentTypeIndication><AllSignedDataObjects>
            var allSignedDataObjectsNode = document.CreateElement(XadesPrefix, "AllSignedDataObjects", XadesNamespaceUrl);
            commitmentTypeIndicationNode.AppendChild(allSignedDataObjectsNode);
        }

        private static string ToDecimalString(string serialNumber)
        {
            BigInteger bi;

            if (BigInteger.TryParse(serialNumber, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out bi))
            {
                return bi.ToString(CultureInfo.InvariantCulture);
            }
            else
            {
                return serialNumber;
            }
        }

        private static void AssignNameSpacePrefixToElementTree(XmlElement element, string prefix)
        {
            element.Prefix = prefix;

            foreach (var child in element.ChildNodes)
            {
                if (child is XmlElement)
                    AssignNameSpacePrefixToElementTree(child as XmlElement, prefix);
            }
        }
        #endregion Private methods
    }

需要服务的消息应如下所示

<Tag1>
  <Tag2>...</Tag2>
  <ds:Signature ...> ... </ds:Signature>
</Tag1>

消息的签名部分必须是:

<Tag1>
    <Tag2>...</Tag2>
</Tag1>
c# signature sign signedxml
2个回答
6
投票

我终于解决了我的问题。问题是在破坏参考摘要值的签名过程后添加前缀“ds”。结果表明不需要此前缀。我在下面发布工作代码。

这是我需要访问 XAdES QualifyingProperties 的 SignedXml 的重写类,它存储在 DataObject 而不是实际消息中。

public class XadesSignedXml : SignedXml
    {
        #region Public fields
        public const string XmlDsigSignatureProperties = "http://uri.etsi.org/01903#SignedProperties";
        public const string XadesProofOfApproval = "http://uri.etsi.org/01903/v1.2.2#ProofOfApproval";
        public const string XadesPrefix = "xades";
        public const string XadesNamespaceUrl = "http://uri.etsi.org/01903/v1.3.2#";
        public XmlElement PropertiesNode { get; set; }
        #endregion Public fields

        #region Private fields
        private readonly List<DataObject> _dataObjects = new List<DataObject>();
        #endregion Private fields

        #region Constructor
        public XadesSignedXml(XmlDocument document) : base(document) { }
        #endregion Constructor

        #region SignedXml
        public override XmlElement GetIdElement(XmlDocument document, string idValue)
        {
            if (string.IsNullOrEmpty(idValue))
                return null;

            var xmlElement = base.GetIdElement(document, idValue);
            if (xmlElement != null)
                return xmlElement;

            if (_dataObjects.Count == 0)
                return null;

            foreach (var dataObject in _dataObjects)
            {
                var nodeWithSameId = XmlHelper.FindNodeWithAttributeValueIn(dataObject.Data, "Id", idValue);
                if (nodeWithSameId != null)
                    return nodeWithSameId;
            }

            return null;
        }

        public new void AddObject(DataObject dataObject)
        {
            base.AddObject(dataObject);
            _dataObjects.Add(dataObject);
        }
        #endregion SignedXml
    }

这是签名方法。

public static class Signature
    {
        #region Private fields
        private const string SignatureId = "Signature";
        private const string SignaturePropertiesId = "SignedProperties";
        #endregion Private fields

        #region Public methods
        public static XmlElement SignWithXAdES(X509Certificate2 signingCertificate, XmlDocument xmlDocument)
        {
            var signedXml = new XadesSignedXml(xmlDocument);
            signedXml.Signature.Id = SignatureId;
            signedXml.SigningKey = signingCertificate.PrivateKey;

            var signatureReference = new Reference { Uri = "", };
            signatureReference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
            signedXml.AddReference(signatureReference);

            var keyInfo = new KeyInfo();
            keyInfo.AddClause(new KeyInfoX509Data(signingCertificate));
            signedXml.KeyInfo = keyInfo;

            AddXAdESProperties(xmlDocument, signedXml, signingCertificate);

            signedXml.ComputeSignature();

            return signedXml.GetXml();
        }
        #endregion Public methods

        #region Private methods
        private static void AddXAdESProperties(XmlDocument document, XadesSignedXml xadesSignedXml, X509Certificate2 signingCertificate)
        {
            var parametersSignature = new Reference
            {
                Uri = $"#{SignaturePropertiesId}",
                Type = XadesSignedXml.XmlDsigSignatureProperties,
            };
            xadesSignedXml.AddReference(parametersSignature);

            // <Object>
            var objectNode = document.CreateElement("Object", SignedXml.XmlDsigNamespaceUrl);

            // <Object><QualifyingProperties>
            var qualifyingPropertiesNode = document.CreateElement(XadesSignedXml.XadesPrefix, "QualifyingProperties", XadesSignedXml.XadesNamespaceUrl);
            qualifyingPropertiesNode.SetAttribute("Target", $"#{SignatureId}");
            objectNode.AppendChild(qualifyingPropertiesNode);

            // <Object><QualifyingProperties><SignedProperties>
            var signedPropertiesNode = document.CreateElement(XadesSignedXml.XadesPrefix, "SignedProperties", XadesSignedXml.XadesNamespaceUrl);
            signedPropertiesNode.SetAttribute("Id", SignaturePropertiesId);
            qualifyingPropertiesNode.AppendChild(signedPropertiesNode);

            // <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties>
            var signedSignaturePropertiesNode = document.CreateElement(XadesSignedXml.XadesPrefix, "SignedSignatureProperties", XadesSignedXml.XadesNamespaceUrl);
            signedPropertiesNode.AppendChild(signedSignaturePropertiesNode);

            // <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties> </SigningTime>
            var signingTime = document.CreateElement(XadesSignedXml.XadesPrefix, "SigningTime", XadesSignedXml.XadesNamespaceUrl);
            signingTime.InnerText = $"{DateTime.UtcNow.ToString("s")}Z";
            signedSignaturePropertiesNode.AppendChild(signingTime);

            // <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate>
            var signingCertificateNode = document.CreateElement(XadesSignedXml.XadesPrefix, "SigningCertificate", XadesSignedXml.XadesNamespaceUrl);
            signedSignaturePropertiesNode.AppendChild(signingCertificateNode);

            // <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert>
            var certNode = document.CreateElement(XadesSignedXml.XadesPrefix, "Cert", XadesSignedXml.XadesNamespaceUrl);
            signingCertificateNode.AppendChild(certNode);

            // <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert><CertDigest>
            var certDigestNode = document.CreateElement(XadesSignedXml.XadesPrefix, "CertDigest", XadesSignedXml.XadesNamespaceUrl);
            certNode.AppendChild(certDigestNode);

            // <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert><CertDigest> </DigestMethod>
            var digestMethod = document.CreateElement("DigestMethod", SignedXml.XmlDsigNamespaceUrl);
            var digestMethodAlgorithmAtribute = document.CreateAttribute("Algorithm");
            digestMethodAlgorithmAtribute.InnerText = SignedXml.XmlDsigSHA1Url;
            digestMethod.Attributes.Append(digestMethodAlgorithmAtribute);
            certDigestNode.AppendChild(digestMethod);

            // <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert><CertDigest> </DigestMethod>
            var digestValue = document.CreateElement("DigestValue", SignedXml.XmlDsigNamespaceUrl);
            digestValue.InnerText = Convert.ToBase64String(signingCertificate.GetCertHash());
            certDigestNode.AppendChild(digestValue);

            // <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert><IssuerSerial>
            var issuerSerialNode = document.CreateElement(XadesSignedXml.XadesPrefix, "IssuerSerial", XadesSignedXml.XadesNamespaceUrl);
            certNode.AppendChild(issuerSerialNode);

            // <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert><IssuerSerial> </X509IssuerName>
            var x509IssuerName = document.CreateElement("X509IssuerName", SignedXml.XmlDsigNamespaceUrl);
            x509IssuerName.InnerText = signingCertificate.Issuer;
            issuerSerialNode.AppendChild(x509IssuerName);

            // <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert><IssuerSerial> </X509SerialNumber>
            var x509SerialNumber = document.CreateElement("X509SerialNumber", SignedXml.XmlDsigNamespaceUrl);
            x509SerialNumber.InnerText = ToDecimalString(signingCertificate.SerialNumber);
            issuerSerialNode.AppendChild(x509SerialNumber);

            // <Object><QualifyingProperties><SignedProperties><SignedDataObjectProperties>
            var signedDataObjectPropertiesNode = document.CreateElement(XadesSignedXml.XadesPrefix, "SignedDataObjectProperties", XadesSignedXml.XadesNamespaceUrl);
            signedPropertiesNode.AppendChild(signedDataObjectPropertiesNode);

            // <Object><QualifyingProperties><SignedProperties><SignedDataObjectProperties><CommitmentTypeIndication>
            var commitmentTypeIndicationNode = document.CreateElement(XadesSignedXml.XadesPrefix, "CommitmentTypeIndication", XadesSignedXml.XadesNamespaceUrl);
            signedDataObjectPropertiesNode.AppendChild(commitmentTypeIndicationNode);

            // <Object><QualifyingProperties><SignedProperties><SignedDataObjectProperties><CommitmentTypeIndication><CommitmentTypeId>
            var commitmentTypeIdNode = document.CreateElement(XadesSignedXml.XadesPrefix, "CommitmentTypeId", XadesSignedXml.XadesNamespaceUrl);
            commitmentTypeIndicationNode.AppendChild(commitmentTypeIdNode);

            // <Object><QualifyingProperties><SignedProperties><SignedDataObjectProperties><CommitmentTypeIndication><CommitmentTypeId><Identifier>
            var identifierNode = document.CreateElement(XadesSignedXml.XadesPrefix, "Identifier", XadesSignedXml.XadesNamespaceUrl);
            identifierNode.InnerText = XadesSignedXml.XadesProofOfApproval;
            commitmentTypeIdNode.AppendChild(identifierNode);

            // <Object><QualifyingProperties><SignedProperties><SignedDataObjectProperties><CommitmentTypeIndication><AllSignedDataObjects>
            var allSignedDataObjectsNode = document.CreateElement(XadesSignedXml.XadesPrefix, "AllSignedDataObjects", XadesSignedXml.XadesNamespaceUrl);
            commitmentTypeIndicationNode.AppendChild(allSignedDataObjectsNode);

            var dataObject = new DataObject();
            dataObject.Data = qualifyingPropertiesNode.SelectNodes(".");
            xadesSignedXml.AddObject(dataObject);
        }

        private static string ToDecimalString(string serialNumber)
        {
            BigInteger bi;

            if (BigInteger.TryParse(serialNumber, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out bi))
            {
                return bi.ToString(CultureInfo.InvariantCulture);
            }
            else
            {
                return serialNumber;
            }
        }
        #endregion Private methods
    }

0
投票

这是个老话题,但我也在寻找解决方案。没有工作。即使 C# 不抛出任何异常并且签名看起来正常,验证也总是返回:“签名已修改”。搜索解决方案花了我太长时间,所以当我找到一个解决方案时,我决定在这个主题中分享以备将来使用。也许其他人需要这个。 对我来说最好的方法是使用这个项目:

https://administracionelectronica.gob.es/ctt/firmaxadesnet/mas#.ZBxLWR-ZOUk

这是 github 仓库:

https://github.com/ctt-gob-es/FirmaXadesNet

https://github.com/ctt-gob-es/FirmaXadesNet45

本项目作者:

新技术部 - 城市化总局 卡塔赫纳市政厅

干得好。

© www.soinside.com 2019 - 2024. All rights reserved.