2004-05-13 Sebastien Pouliot <sebastien@ximian.com>
[mono.git] / mcs / class / System.Security / System.Security.Cryptography.Xml / Signature.cs
1 //
2 // Signature.cs - Signature implementation for XML Signature
3 //
4 // Author:
5 //      Sebastien Pouliot (spouliot@motus.com)
6 //
7 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
8 //
9
10 using System.Collections;
11 using System.Security.Cryptography;
12 using System.Xml;
13
14 namespace System.Security.Cryptography.Xml {
15
16         public class Signature {
17                 static XmlNamespaceManager dsigNsmgr;
18                 
19                 static Signature ()
20                 {
21                         dsigNsmgr = new XmlNamespaceManager (new NameTable ());
22                         dsigNsmgr.AddNamespace ("xd", XmlSignature.NamespaceURI);
23                 }
24
25                 private ArrayList list;
26                 private SignedInfo info;
27                 private KeyInfo key;
28                 private string id;
29                 private byte[] signature;
30                 private XmlElement element;
31
32                 public Signature () 
33                 {
34                         list = new ArrayList ();
35                 }
36
37                 public string Id {
38                         get { return id; }
39                         set {
40                                 element = null;
41                                 id = value;
42                         }
43                 }
44
45                 public KeyInfo KeyInfo {
46                         get { return key; }
47                         set {
48                                 element = null;
49                                 key = value;
50                         }
51                 }
52
53                 public IList ObjectList {
54                         get { return list; }
55                         set { list = ArrayList.Adapter (value); }
56                 }
57
58                 public byte[] SignatureValue {
59                         get { return signature; }
60                         set {
61                                 element = null;
62                                 signature = value;
63                         }
64                 }
65
66                 public SignedInfo SignedInfo {
67                         get { return info; }
68                         set {
69                                 element = null;
70                                 info = value;
71                         }
72                 }
73
74                 public void AddObject (DataObject dataObject) 
75                 {
76                         list.Add (dataObject);
77                 }
78
79                 public XmlElement GetXml () 
80                 {
81                         if (element != null)
82                                 return element;
83
84                         if (info == null)
85                                 throw new CryptographicException ("SignedInfo");
86                         if (signature == null)
87                                 throw new CryptographicException ("SignatureValue");
88
89                         XmlDocument document = new XmlDocument ();
90                         XmlElement xel = document.CreateElement (XmlSignature.ElementNames.Signature, XmlSignature.NamespaceURI);
91                         if (id != null)
92                                 xel.SetAttribute (XmlSignature.AttributeNames.Id, id);
93
94                         XmlNode xn = info.GetXml ();
95                         XmlNode newNode = document.ImportNode (xn, true);
96                         xel.AppendChild (newNode);
97
98                         if (signature != null) {
99                                 XmlElement sv = document.CreateElement (XmlSignature.ElementNames.SignatureValue, XmlSignature.NamespaceURI);
100                                 sv.InnerText = Convert.ToBase64String (signature);
101                                 xel.AppendChild (sv);
102                         }
103
104                         if (key != null) {
105                                 xn = key.GetXml ();
106                                 newNode = document.ImportNode (xn, true);
107                                 xel.AppendChild (newNode);
108                         }
109
110                         if (list.Count > 0) {
111                                 foreach (DataObject obj in list) {
112                                         xn = obj.GetXml ();
113                                         newNode = document.ImportNode (xn, true);
114                                         xel.AppendChild (newNode);
115                                 }
116                         }
117
118                         return xel;
119                 }
120
121                 private string GetAttribute (XmlElement xel, string attribute) 
122                 {
123                         XmlAttribute xa = xel.Attributes [attribute];
124                         return ((xa != null) ? xa.InnerText : null);
125                 }
126
127                 public void LoadXml (XmlElement value) 
128                 {
129                         if (value == null)
130                                 throw new ArgumentNullException ("value");
131
132                         if ((value.LocalName == XmlSignature.ElementNames.Signature) && (value.NamespaceURI == XmlSignature.NamespaceURI)) {
133                                 id = GetAttribute (value, XmlSignature.AttributeNames.Id);
134
135                                 // LAMESPEC: This library is totally useless against eXtensibly Marked-up document.
136                                 int i = NextElementPos (value.ChildNodes, 0, XmlSignature.ElementNames.SignedInfo, XmlSignature.NamespaceURI, true);
137                                 XmlElement sinfo = (XmlElement) value.ChildNodes [i];
138                                 info = new SignedInfo ();
139                                 info.LoadXml (sinfo);
140
141                                 i = NextElementPos (value.ChildNodes, ++i, XmlSignature.ElementNames.SignatureValue, XmlSignature.NamespaceURI, true);
142                                 XmlElement sigValue = (XmlElement) value.ChildNodes [i];
143                                 signature = Convert.FromBase64String (sigValue.InnerText);
144
145                                 // signature isn't required: <element ref="ds:KeyInfo" minOccurs="0"/> 
146                                 i = NextElementPos (value.ChildNodes, ++i, XmlSignature.ElementNames.KeyInfo, XmlSignature.NamespaceURI, false);
147                                 if (i > 0) {
148                                         XmlElement kinfo = (XmlElement) value.ChildNodes [i];
149                                         key = new KeyInfo ();
150                                         key.LoadXml (kinfo);
151                                 }
152
153                                 XmlNodeList xnl = value.SelectNodes ("xd:Object", dsigNsmgr);
154                                 foreach (XmlElement xn in xnl) {
155                                         DataObject obj = new DataObject ();
156                                         obj.LoadXml (xn);
157                                         AddObject (obj);
158                                 }
159                         }
160                         else
161                                 throw new CryptographicException ("Malformed element: Signature.");
162
163                         // if invalid
164                         if (info == null)
165                                 throw new CryptographicException ("SignedInfo");
166                         if (signature == null)
167                                 throw new CryptographicException ("SignatureValue");
168                 }
169
170                 private int NextElementPos (XmlNodeList nl, int pos, string name, string ns, bool required)
171                 {
172                         while (pos < nl.Count) {
173                                 if (nl [pos].NodeType == XmlNodeType.Element) {
174                                         if (nl [pos].LocalName != name && nl [pos].NamespaceURI != ns) {
175                                                 if (required)
176                                                         throw new CryptographicException ("Malformed element " + name);
177                                                 else
178                                                         return -2;
179                                         }
180                                         return pos;
181                                 }
182                                 else
183                                         pos++;
184                         }
185                         if (required)
186                                 throw new CryptographicException ("Malformed element " + name);
187                         return -1;
188                 }
189         }
190 }