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