1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System; 6 using System.Collections; 7 using System.Runtime.InteropServices; 8 using System.Xml; 9 using System.Globalization; 10 11 namespace System.Security.Cryptography.Xml 12 { 13 public class SignedInfo : ICollection 14 { 15 private string _id; 16 private string _canonicalizationMethod; 17 private string _signatureMethod; 18 private string _signatureLength; 19 private ArrayList _references; 20 private XmlElement _cachedXml = null; 21 private SignedXml _signedXml = null; 22 private Transform _canonicalizationMethodTransform = null; 23 24 internal SignedXml SignedXml 25 { 26 get { return _signedXml; } 27 set { _signedXml = value; } 28 } 29 SignedInfo()30 public SignedInfo() 31 { 32 _references = new ArrayList(); 33 } 34 GetEnumerator()35 public IEnumerator GetEnumerator() 36 { 37 throw new NotSupportedException(); 38 } 39 CopyTo(Array array, int index)40 public void CopyTo(Array array, int index) 41 { 42 throw new NotSupportedException(); 43 } 44 45 public int Count 46 { 47 get { throw new NotSupportedException(); } 48 } 49 50 public bool IsReadOnly 51 { 52 get { throw new NotSupportedException(); } 53 } 54 55 public bool IsSynchronized 56 { 57 get { throw new NotSupportedException(); } 58 } 59 60 public object SyncRoot 61 { 62 get { throw new NotSupportedException(); } 63 } 64 65 // 66 // public properties 67 // 68 69 public string Id 70 { 71 get { return _id; } 72 set 73 { 74 _id = value; 75 _cachedXml = null; 76 } 77 } 78 79 public string CanonicalizationMethod 80 { 81 get 82 { 83 // Default the canonicalization method to C14N 84 if (_canonicalizationMethod == null) 85 return SignedXml.XmlDsigC14NTransformUrl; 86 return _canonicalizationMethod; 87 } 88 set 89 { 90 _canonicalizationMethod = value; 91 _cachedXml = null; 92 } 93 } 94 95 public Transform CanonicalizationMethodObject 96 { 97 get 98 { 99 if (_canonicalizationMethodTransform == null) 100 { 101 _canonicalizationMethodTransform = CryptoHelpers.CreateFromName(CanonicalizationMethod) as Transform; 102 if (_canonicalizationMethodTransform == null) 103 throw new CryptographicException(string.Format(CultureInfo.CurrentCulture, SR.Cryptography_Xml_CreateTransformFailed, CanonicalizationMethod)); 104 _canonicalizationMethodTransform.SignedXml = SignedXml; 105 _canonicalizationMethodTransform.Reference = null; 106 } 107 return _canonicalizationMethodTransform; 108 } 109 } 110 111 public string SignatureMethod 112 { 113 get { return _signatureMethod; } 114 set 115 { 116 _signatureMethod = value; 117 _cachedXml = null; 118 } 119 } 120 121 public string SignatureLength 122 { 123 get { return _signatureLength; } 124 set 125 { 126 _signatureLength = value; 127 _cachedXml = null; 128 } 129 } 130 131 public ArrayList References 132 { 133 get { return _references; } 134 } 135 136 internal bool CacheValid 137 { 138 get 139 { 140 if (_cachedXml == null) return false; 141 // now check all the references 142 foreach (Reference reference in References) 143 { 144 if (!reference.CacheValid) return false; 145 } 146 return true; 147 } 148 } 149 150 // 151 // public methods 152 // 153 GetXml()154 public XmlElement GetXml() 155 { 156 if (CacheValid) return _cachedXml; 157 158 XmlDocument document = new XmlDocument(); 159 document.PreserveWhitespace = true; 160 return GetXml(document); 161 } 162 GetXml(XmlDocument document)163 internal XmlElement GetXml(XmlDocument document) 164 { 165 // Create the root element 166 XmlElement signedInfoElement = document.CreateElement("SignedInfo", SignedXml.XmlDsigNamespaceUrl); 167 if (!string.IsNullOrEmpty(_id)) 168 signedInfoElement.SetAttribute("Id", _id); 169 170 // Add the canonicalization method, defaults to SignedXml.XmlDsigNamespaceUrl 171 XmlElement canonicalizationMethodElement = CanonicalizationMethodObject.GetXml(document, "CanonicalizationMethod"); 172 signedInfoElement.AppendChild(canonicalizationMethodElement); 173 174 // Add the signature method 175 if (string.IsNullOrEmpty(_signatureMethod)) 176 throw new CryptographicException(SR.Cryptography_Xml_SignatureMethodRequired); 177 178 XmlElement signatureMethodElement = document.CreateElement("SignatureMethod", SignedXml.XmlDsigNamespaceUrl); 179 signatureMethodElement.SetAttribute("Algorithm", _signatureMethod); 180 // Add HMACOutputLength tag if we have one 181 if (_signatureLength != null) 182 { 183 XmlElement hmacLengthElement = document.CreateElement(null, "HMACOutputLength", SignedXml.XmlDsigNamespaceUrl); 184 XmlText outputLength = document.CreateTextNode(_signatureLength); 185 hmacLengthElement.AppendChild(outputLength); 186 signatureMethodElement.AppendChild(hmacLengthElement); 187 } 188 189 signedInfoElement.AppendChild(signatureMethodElement); 190 191 // Add the references 192 if (_references.Count == 0) 193 throw new CryptographicException(SR.Cryptography_Xml_ReferenceElementRequired); 194 195 for (int i = 0; i < _references.Count; ++i) 196 { 197 Reference reference = (Reference)_references[i]; 198 signedInfoElement.AppendChild(reference.GetXml(document)); 199 } 200 201 return signedInfoElement; 202 } 203 LoadXml(XmlElement value)204 public void LoadXml(XmlElement value) 205 { 206 if (value == null) 207 throw new ArgumentNullException(nameof(value)); 208 209 // SignedInfo 210 XmlElement signedInfoElement = value; 211 if (!signedInfoElement.LocalName.Equals("SignedInfo")) 212 throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "SignedInfo"); 213 214 XmlNamespaceManager nsm = new XmlNamespaceManager(value.OwnerDocument.NameTable); 215 nsm.AddNamespace("ds", SignedXml.XmlDsigNamespaceUrl); 216 217 // Id attribute -- optional 218 _id = Utils.GetAttribute(signedInfoElement, "Id", SignedXml.XmlDsigNamespaceUrl); 219 220 // CanonicalizationMethod -- must be present 221 XmlElement canonicalizationMethodElement = signedInfoElement.SelectSingleNode("ds:CanonicalizationMethod", nsm) as XmlElement; 222 if (canonicalizationMethodElement == null) 223 throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "SignedInfo/CanonicalizationMethod"); 224 _canonicalizationMethod = Utils.GetAttribute(canonicalizationMethodElement, "Algorithm", SignedXml.XmlDsigNamespaceUrl); 225 _canonicalizationMethodTransform = null; 226 if (canonicalizationMethodElement.ChildNodes.Count > 0) 227 CanonicalizationMethodObject.LoadInnerXml(canonicalizationMethodElement.ChildNodes); 228 229 // SignatureMethod -- must be present 230 XmlElement signatureMethodElement = signedInfoElement.SelectSingleNode("ds:SignatureMethod", nsm) as XmlElement; 231 if (signatureMethodElement == null) 232 throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "SignedInfo/SignatureMethod"); 233 _signatureMethod = Utils.GetAttribute(signatureMethodElement, "Algorithm", SignedXml.XmlDsigNamespaceUrl); 234 235 // Now get the output length if we are using a MAC algorithm 236 XmlElement signatureLengthElement = signatureMethodElement.SelectSingleNode("ds:HMACOutputLength", nsm) as XmlElement; 237 if (signatureLengthElement != null) 238 _signatureLength = signatureLengthElement.InnerXml; 239 240 // flush out any reference that was there 241 _references.Clear(); 242 243 XmlNodeList referenceNodes = signedInfoElement.SelectNodes("ds:Reference", nsm); 244 if (referenceNodes != null) 245 { 246 foreach (XmlNode node in referenceNodes) 247 { 248 XmlElement referenceElement = node as XmlElement; 249 Reference reference = new Reference(); 250 AddReference(reference); 251 reference.LoadXml(referenceElement); 252 } 253 } 254 255 // Save away the cached value 256 _cachedXml = signedInfoElement; 257 } 258 AddReference(Reference reference)259 public void AddReference(Reference reference) 260 { 261 if (reference == null) 262 throw new ArgumentNullException(nameof(reference)); 263 264 reference.SignedXml = SignedXml; 265 _references.Add(reference); 266 } 267 } 268 } 269