| File: System\Security\Cryptography\Xml\SignedInfo.cs | Web Access |
| Project: src\runtime\src\libraries\System.Security.Cryptography.Xml\src\System.Security.Cryptography.Xml.csproj (System.Security.Cryptography.Xml) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; using System.Diagnostics.CodeAnalysis; using System.Xml; namespace System.Security.Cryptography.Xml { [RequiresDynamicCode(CryptoHelpers.XsltRequiresDynamicCodeMessage)] [RequiresUnreferencedCode(CryptoHelpers.CreateFromNameUnreferencedCodeMessage)] public class SignedInfo : ICollection { private string? _id; private string? _canonicalizationMethod; private string? _signatureMethod; private string? _signatureLength; private readonly ArrayList _references; private XmlElement? _cachedXml; private SignedXml? _signedXml; private Transform? _canonicalizationMethodTransform; internal SignedXml? SignedXml { get { return _signedXml; } set { _signedXml = value; } } public SignedInfo() { _references = new ArrayList(); } public IEnumerator GetEnumerator() { throw new NotSupportedException(); } public void CopyTo(Array array, int index) { throw new NotSupportedException(); } public int Count { get { throw new NotSupportedException(); } } public bool IsReadOnly { get { throw new NotSupportedException(); } } public bool IsSynchronized { get { throw new NotSupportedException(); } } public object SyncRoot { get { throw new NotSupportedException(); } } // // public properties // public string? Id { get { return _id; } set { _id = value; _cachedXml = null; } } [AllowNull] public string CanonicalizationMethod { get { // Default the canonicalization method to C14N if (_canonicalizationMethod == null) return SignedXml.XmlDsigC14NTransformUrl; return _canonicalizationMethod; } set { _canonicalizationMethod = value; _cachedXml = null; } } public Transform CanonicalizationMethodObject { get { if (_canonicalizationMethodTransform == null) { _canonicalizationMethodTransform = CryptoHelpers.CreateFromName<Transform>(CanonicalizationMethod); if (_canonicalizationMethodTransform == null) throw new CryptographicException(SR.Format(SR.Cryptography_Xml_CreateTransformFailed, CanonicalizationMethod)); _canonicalizationMethodTransform.SignedXml = SignedXml; _canonicalizationMethodTransform.Reference = null; } return _canonicalizationMethodTransform; } } public string? SignatureMethod { get { return _signatureMethod; } set { _signatureMethod = value; _cachedXml = null; } } public string? SignatureLength { get { return _signatureLength; } set { _signatureLength = value; _cachedXml = null; } } public ArrayList References { get { return _references; } } [MemberNotNullWhen(true, nameof(_cachedXml))] internal bool CacheValid { get { if (_cachedXml == null) return false; // now check all the references foreach (Reference reference in References) { if (!reference.CacheValid) return false; } return true; } } // // public methods // public XmlElement GetXml() { if (CacheValid) return _cachedXml; XmlDocument document = new XmlDocument(); document.PreserveWhitespace = true; return GetXml(document); } internal XmlElement GetXml(XmlDocument document) { // Create the root element XmlElement signedInfoElement = document.CreateElement("SignedInfo", SignedXml.XmlDsigNamespaceUrl); if (!string.IsNullOrEmpty(_id)) signedInfoElement.SetAttribute("Id", _id); // Add the canonicalization method, defaults to SignedXml.XmlDsigNamespaceUrl XmlElement canonicalizationMethodElement = CanonicalizationMethodObject.GetXml(document, "CanonicalizationMethod"); signedInfoElement.AppendChild(canonicalizationMethodElement); // Add the signature method if (string.IsNullOrEmpty(_signatureMethod)) throw new CryptographicException(SR.Cryptography_Xml_SignatureMethodRequired); XmlElement signatureMethodElement = document.CreateElement("SignatureMethod", SignedXml.XmlDsigNamespaceUrl); signatureMethodElement.SetAttribute("Algorithm", _signatureMethod); // Add HMACOutputLength tag if we have one if (_signatureLength != null) { XmlElement hmacLengthElement = document.CreateElement(null, "HMACOutputLength", SignedXml.XmlDsigNamespaceUrl); XmlText outputLength = document.CreateTextNode(_signatureLength); hmacLengthElement.AppendChild(outputLength); signatureMethodElement.AppendChild(hmacLengthElement); } signedInfoElement.AppendChild(signatureMethodElement); // Add the references if (_references.Count == 0) throw new CryptographicException(SR.Cryptography_Xml_ReferenceElementRequired); for (int i = 0; i < _references.Count; ++i) { Reference reference = (Reference)_references[i]!; signedInfoElement.AppendChild(reference.GetXml(document)); } return signedInfoElement; } public void LoadXml(XmlElement value) { ArgumentNullException.ThrowIfNull(value); // SignedInfo XmlElement signedInfoElement = value; if (!signedInfoElement.LocalName.Equals("SignedInfo")) throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "SignedInfo"); XmlNamespaceManager nsm = new XmlNamespaceManager(value.OwnerDocument.NameTable); nsm.AddNamespace("ds", SignedXml.XmlDsigNamespaceUrl); int expectedChildNodes = 0; // Id attribute -- optional _id = Utils.GetAttribute(signedInfoElement, "Id", SignedXml.XmlDsigNamespaceUrl); if (!Utils.VerifyAttributes(signedInfoElement, "Id")) throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "SignedInfo"); // CanonicalizationMethod -- must be present XmlNodeList? canonicalizationMethodNodes = signedInfoElement.SelectNodes("ds:CanonicalizationMethod", nsm); if (canonicalizationMethodNodes == null || canonicalizationMethodNodes.Count == 0 || canonicalizationMethodNodes.Count > 1) throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "SignedInfo/CanonicalizationMethod"); XmlElement canonicalizationMethodElement = (canonicalizationMethodNodes.Item(0) as XmlElement)!; expectedChildNodes += canonicalizationMethodNodes.Count; _canonicalizationMethod = Utils.GetAttribute(canonicalizationMethodElement, "Algorithm", SignedXml.XmlDsigNamespaceUrl); if (_canonicalizationMethod == null || !Utils.VerifyAttributes(canonicalizationMethodElement, "Algorithm")) throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "SignedInfo/CanonicalizationMethod"); _canonicalizationMethodTransform = null; if (canonicalizationMethodElement.ChildNodes.Count > 0) CanonicalizationMethodObject.LoadInnerXml(canonicalizationMethodElement.ChildNodes); // SignatureMethod -- must be present XmlNodeList? signatureMethodNodes = signedInfoElement.SelectNodes("ds:SignatureMethod", nsm); if (signatureMethodNodes == null || signatureMethodNodes.Count == 0 || signatureMethodNodes.Count > 1) throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "SignedInfo/SignatureMethod"); XmlElement signatureMethodElement = (signatureMethodNodes.Item(0) as XmlElement)!; expectedChildNodes += signatureMethodNodes.Count; _signatureMethod = Utils.GetAttribute(signatureMethodElement, "Algorithm", SignedXml.XmlDsigNamespaceUrl); if (_signatureMethod == null || !Utils.VerifyAttributes(signatureMethodElement, "Algorithm")) throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "SignedInfo/SignatureMethod"); // Now get the output length if we are using a MAC algorithm XmlElement? signatureLengthElement = signatureMethodElement.SelectSingleNode("ds:HMACOutputLength", nsm) as XmlElement; if (signatureLengthElement != null) _signatureLength = signatureLengthElement.InnerXml; // flush out any reference that was there _references.Clear(); // Reference - 0 or more XmlNodeList? referenceNodes = signedInfoElement.SelectNodes("ds:Reference", nsm); if (referenceNodes != null) { if (referenceNodes.Count > LocalAppContextSwitches.MaxReferencesPerSignedInfo) { throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "SignedInfo/Reference"); } foreach (XmlNode node in referenceNodes) { XmlElement referenceElement = (node as XmlElement)!; Reference reference = new Reference(); AddReference(reference); reference.LoadXml(referenceElement); } expectedChildNodes += referenceNodes.Count; // Verify that there aren't any extra nodes that aren't allowed if (signedInfoElement.SelectNodes("*")!.Count != expectedChildNodes) { throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "SignedInfo"); } } // Save away the cached value _cachedXml = signedInfoElement; } public void AddReference(Reference reference) { ArgumentNullException.ThrowIfNull(reference); reference.SignedXml = SignedXml; _references.Add(reference); } } }