[bcl] Remove more NET_2_0 checks from class libs
[mono.git] / mcs / class / System.Security / Test / standalone_tests / xmldsig.cs
1 //
2 // xmldsig.cs: XML Digital Signature Tests
3 //
4 // Authors:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //      Sebastien Pouliot <sebastien@ximian.com>
7 //
8 // (C) 2004 Novell (http://www.novell.com)
9 //
10
11 using System;
12 using System.Collections;
13 using System.IO;
14 using System.Reflection;
15 using System.Runtime.InteropServices;
16 using System.Security.Cryptography;
17 using System.Security.Cryptography.X509Certificates;
18 using System.Security.Cryptography.Xml;
19 using System.Text;
20 using System.Xml;
21 using System.Xml.Xsl;
22
23 using Mono.Security.X509;
24
25 public class MyClass {
26         static int valid = 0;
27         static int invalid = 0;
28         static int error = 0;
29         static int skip = 0;
30
31         static bool exc14n;
32         static bool hmacmd5;
33
34         static bool useDecentReader;
35
36         public static void Main(string [] args) 
37         {
38                 foreach (string arg in args)
39                         if (arg == "--decent-reader")
40                                 useDecentReader = true;
41                 try {
42                         // automagically ajust tests to run depending on system config
43                         exc14n = (CryptoConfig.CreateFromName ("http://www.w3.org/2001/10/xml-exc-c14n#WithComments") != null);
44                         hmacmd5 = (CryptoConfig.CreateFromName ("HMACMD5") != null);
45
46                         Console.WriteLine ("MERLIN");
47                         Merlin ();
48                         Console.WriteLine ();
49                         // Non working on MS runtime;
50                         // they have insufficient support for namespaces.
51                         Console.WriteLine ("PHAOS");
52                         Phaos ();
53                 }
54                 catch (Exception ex) {
55                         Console.WriteLine (ex);
56                 }
57                 finally {
58                         Console.WriteLine ();
59                         Console.WriteLine ("TOTAL VALID   {0}", valid);
60                         Console.WriteLine ("TOTAL INVALID {0}", invalid);
61                         Console.WriteLine ("TOTAL ERROR   {0}", error);
62                         Console.WriteLine ("TOTAL SKIP    {0}", skip);
63
64                         Console.WriteLine ("Finished.");
65                 }
66         }
67
68         static Mono.Security.X509.X509Certificate LoadCertificate (string filename) 
69         {
70                 Mono.Security.X509.X509Certificate mx = null;
71                 if (File.Exists (filename)) {
72                         using (FileStream fs = File.OpenRead (filename)) {
73                                 byte[] data = new byte [fs.Length];
74                                 fs.Read (data, 0, data.Length);
75                                 mx = new Mono.Security.X509.X509Certificate (data);
76                         }
77                 }
78                 return mx;
79         }
80
81         static string GetPath (string dir, string name) 
82         {
83                 string path = Path.GetDirectoryName (dir);
84                 path = Path.Combine (path, "certs");
85                 path = Path.Combine (path, name);
86                 return path;
87         }
88
89         static TextReader GetReader (string filename)
90         {
91                 XmlResolver resolver = new XmlUrlResolver ();
92                 Stream stream = resolver.GetEntity (resolver.ResolveUri (null, filename), null, typeof (Stream)) as Stream;
93                 if (useDecentReader)
94                         return new XmlSignatureStreamReader (
95                                 new StreamReader (stream));
96                 else
97                         return new StreamReader (stream);
98         }
99
100         static void Symmetric (string filename, byte[] key) 
101         {
102                 string shortName = Path.GetFileName (filename);
103
104                 XmlDocument doc = new XmlDocument ();
105                 doc.PreserveWhitespace = true;
106                 XmlTextReader xtr = new XmlTextReader (GetReader (filename));
107                 XmlValidatingReader xvr = new XmlValidatingReader (xtr);
108                 xtr.Normalization = true;
109                 doc.Load (xvr);
110                 
111                 try {
112                         XmlNodeList nodeList = doc.GetElementsByTagName ("Signature", SignedXml.XmlDsigNamespaceUrl);
113                         XmlElement signature = (XmlElement) nodeList [0];
114
115                         SignedXml s = new SignedXml ();
116                         s.LoadXml (signature);
117
118                         HMACSHA1 mac = new HMACSHA1 (key);
119                         if (s.CheckSignature (mac)) {
120                                 Console.WriteLine ("valid {0}", shortName);
121                                 valid++;
122                         }
123                         else {
124                                 Console.WriteLine ("INVALID {0}", shortName);
125                                 invalid++;
126                         }
127                 }
128                 catch (Exception ex) {
129                         Console.WriteLine ("EXCEPTION " + shortName + " " + ex);
130                         error++;
131                 }
132         }
133
134         static void Asymmetric (string filename) 
135         {
136                 string shortName = Path.GetFileName (filename);
137
138                 XmlDocument doc = new XmlDocument ();
139                 XmlTextReader xtr = new XmlTextReader (GetReader (filename));
140                 XmlValidatingReader xvr = new XmlValidatingReader (xtr);
141                 xtr.Normalization = true;
142                 doc.PreserveWhitespace = true;
143                 doc.Load (xvr);
144
145                 try {
146                         SignedXml s = null;
147                         if (filename.IndexOf ("enveloped") >= 0)
148                                 s = new SignedXml (doc);
149                         else if (filename.IndexOf ("signature-big") >= 0)
150                                 s = new SignedXml (doc);
151                         else
152                                 s = new SignedXml ();
153
154                         XmlNodeList nodeList = doc.GetElementsByTagName ("Signature", "http://www.w3.org/2000/09/xmldsig#");
155                         s.LoadXml ((XmlElement) nodeList [0]);
156                                 
157 #if false // wanna dump?
158 Console.WriteLine ("\n\nFilename : " + fi.Name);
159 DumpSignedXml (s);
160 #endif
161                         // MS doesn't extract the public key out of the certificates
162                         // http://www.dotnet247.com/247reference/a.aspx?u=http://www.kbalertz.com/Feedback_320602.aspx
163                         Mono.Security.X509.X509Certificate mx = null;
164                         foreach (KeyInfoClause kic in s.KeyInfo) {
165                                 if (kic is KeyInfoX509Data) {
166                                         KeyInfoX509Data kix = (kic as KeyInfoX509Data);
167                                         if ((kix.Certificates != null) && (kix.Certificates.Count > 0)) {
168                                                 System.Security.Cryptography.X509Certificates.X509Certificate x509 = (System.Security.Cryptography.X509Certificates.X509Certificate) kix.Certificates [0];
169                                                 byte[] data = x509.GetRawCertData ();
170                                                 mx = new Mono.Security.X509.X509Certificate (data);
171                                         }
172                                 }
173                         }
174
175                         // special cases
176                         // 1- Merlin's certificate resolution (manual)
177                         // 2- Phaos (because Fx doesn't support RetrievalMethod
178                         switch (shortName) {
179                                 case "signature-keyname.xml":
180                                         mx = LoadCertificate (GetPath (filename, "lugh.crt"));
181                                         break;
182                                 case "signature-retrievalmethod-rawx509crt.xml":
183                                         mx = LoadCertificate (GetPath (filename, "balor.crt"));
184                                         break;
185                                 case "signature-x509-is.xml":
186                                         mx = LoadCertificate (GetPath (filename, "macha.crt"));
187                                         break;
188                                 case "signature-x509-ski.xml":
189                                         mx = LoadCertificate (GetPath (filename, "nemain.crt"));
190                                         break;
191                                 case "signature-x509-sn.xml":
192                                         mx = LoadCertificate (GetPath (filename, "badb.crt"));
193                                         break;
194                                 // Phaos
195                                 case "signature-big.xml":
196                                 case "signature-rsa-manifest-x509-data-issuer-serial.xml":
197                                 case "signature-rsa-manifest-x509-data-ski.xml":
198                                 case "signature-rsa-manifest-x509-data-subject-name.xml":
199                                 case "signature-rsa-detached-xslt-transform-retrieval-method.xml":
200                                         mx = LoadCertificate (GetPath (filename, "rsa-cert.der"));
201                                         break;
202                                 case "signature-rsa-detached-xslt-transform-bad-retrieval-method.xml":
203                                         mx = LoadCertificate (GetPath (filename, "dsa-ca-cert.der"));
204                                         break;
205                                 default:
206                                         break;
207                         }
208
209                         bool result = false;
210                         if (mx != null) {
211                                 if (mx.RSA != null) {
212                                         result = s.CheckSignature (mx.RSA);
213                                 }
214                                 else if (mx.DSA != null) {
215                                         result = s.CheckSignature (mx.DSA);
216                                 }
217                         }
218                         else {
219                                 // use a key existing in the document
220                                 result = s.CheckSignature ();
221                         }
222
223                         if (result) {
224                                 Console.WriteLine ("valid " + shortName);
225                                 valid++;
226                         }
227                         else {
228                                 Console.WriteLine ("INVALID {0}", shortName);
229                                 invalid++;
230                         }
231                 } 
232                 catch (Exception ex) {
233                         Console.WriteLine ("EXCEPTION " + shortName + " " + ex);
234                         error++;
235                 }
236         }
237
238         static void Merlin () 
239         {
240                 // see README
241                 byte[] key = Encoding.ASCII.GetBytes ("secret");
242
243                 foreach (FileInfo fi in new DirectoryInfo ("merlin-xmldsig-twenty-three").GetFiles ("signature-*.xml")) {
244                         if (fi.Name.IndexOf ("hmac") >= 0) {
245                                 Symmetric (fi.FullName, key);
246                         }
247                         else {
248                                 Asymmetric (fi.FullName);
249                         }
250                 }
251         }
252
253         static void Phaos ()
254         {
255                 // see README
256                 byte[] key = Encoding.ASCII.GetBytes ("test");  
257
258                 // some documents references other documents in the directory
259                 Directory.SetCurrentDirectory ("phaos-xmldsig-three");
260                 foreach (FileInfo fi in new DirectoryInfo (".").GetFiles ("signature-*.xml")) {
261                         if ((fi.Name.IndexOf ("md5") >= 0) && (!hmacmd5)) {
262                                 Console.WriteLine ("NOT RUN: " + fi.Name + " : System.Security.dll doesn't support HMAC-MD5.");
263                                 skip++;
264                                 continue;
265                         }
266                         if (fi.Name.IndexOf ("hmac") >= 0) {
267                                 Symmetric (fi.FullName, key);
268                         }
269                         else {
270                                 Asymmetric (fi.FullName);
271                         }
272                 }
273                 // return home before next tests
274                 Directory.SetCurrentDirectory ("..");
275         }
276
277         // dump methods under construction ;-)
278
279         static void DumpSignedXml (SignedXml s) 
280         {
281                 Console.WriteLine ("*** SignedXml ***");
282                 Console.WriteLine (s.SigningKeyName);
283                 Console.WriteLine (s.SigningKey);
284                 if (s.Signature != null)
285                         DumpSignature (s.Signature);
286                 if (s.SignedInfo != null)
287                         DumpSignedInfo (s.SignedInfo);
288                 Console.WriteLine (s.SignatureMethod);
289                 Console.WriteLine (s.SignatureLength);
290                 Console.WriteLine (s.SignatureValue);
291                 if (s.KeyInfo != null)
292                         DumpKeyInfo (s.KeyInfo);
293         }
294
295         static void DumpSignature (Signature s) 
296         {
297                 Console.WriteLine ("*** Signature ***");
298                 Console.WriteLine ("Id: " + s.Id);
299                 if (s.KeyInfo != null)
300                         DumpKeyInfo (s.KeyInfo);
301                 Console.WriteLine ("ObjectList: " + s.ObjectList);
302                 Console.WriteLine ("SignatureValue: " + s.SignatureValue);
303                 if (s.SignedInfo != null)
304                         DumpSignedInfo (s.SignedInfo);
305         }
306
307         static void DumpSignedInfo (SignedInfo s) 
308         {
309                 Console.WriteLine ("*** SignedInfo ***");
310                 Console.WriteLine ("CanonicalizationMethod: " + s.CanonicalizationMethod);
311                 Console.WriteLine ("Id: " + s.Id);
312                 Console.WriteLine ("References: " + s.References);
313                 Console.WriteLine ("SignatureLength: " + s.SignatureLength);
314                 Console.WriteLine ("SignatureMethod: " + s.SignatureMethod);
315         }
316
317         static void DumpKeyInfo (KeyInfo ki) 
318         {
319                 Console.WriteLine ("*** KeyInfo ***" + ki);
320                 Console.WriteLine ("Id: " + ki.Id);
321                 Console.WriteLine ("Count: " + ki.Count);
322                 foreach (KeyInfoClause kic in ki)
323                         DumpKeyInfoClause (kic);
324         }
325
326         static void DumpKeyInfoClause (KeyInfoClause kic) 
327         {
328                 KeyInfoName kn = kic as KeyInfoName;
329                 if (kn != null) {
330                         Console.WriteLine ("*** KeyInfoName ***");
331                         Console.WriteLine ("Value: " + kn.Value);
332                         return;
333                 }
334                 KeyInfoX509Data k509 = kic as KeyInfoX509Data;
335                 if (k509 != null) {
336                         Console.WriteLine ("*** KeyInfoX509Data ***");
337                         Console.WriteLine ("Certificates : " + k509.Certificates);
338                         Console.WriteLine ("CRL : " + k509.CRL);
339                         Console.WriteLine ("IssuerSerials : " + k509.IssuerSerials);
340                         Console.WriteLine ("SubjectKeyIds : " + k509.SubjectKeyIds);
341                         Console.WriteLine ("SubjectNames : " + k509.SubjectNames);
342                         return;
343                 }
344         }
345 }
346
347 class MySignedXml : SignedXml
348 {
349         public void TestKey ()
350         {
351                 Console.WriteLine (GetPublicKey () == null);
352         }
353 }
354
355 // below is a copy from our System.Security.dll source.
356         internal class XmlSignatureStreamReader : TextReader
357         {
358                 TextReader source;
359                 int cache = int.MinValue;
360
361                 public XmlSignatureStreamReader (TextReader input)
362                 {
363                         source =input;
364                 }
365
366                 public override void Close ()
367                 {
368                         source.Close ();
369                 }
370
371                 public override int Peek ()
372                 {
373                         if (cache != int.MinValue)
374                                 return cache;
375                         cache = source.Read ();
376                         if (cache != '\r')
377                                 return cache;
378                         // cache must be '\r' here.
379                         if (source.Peek () != '\n')
380                                 return '\r';
381                         // Now Peek() returns '\n', so clear cache.
382                         cache = int.MinValue;
383                         return '\n';
384                 }
385
386                 public override int Read ()
387                 {
388                         if (cache != int.MinValue) {
389                                 int ret = cache;
390                                 cache = int.MinValue;
391                                 return ret;
392                         }
393                         int i = source.Read ();
394                         if (i != '\r')
395                                 return i;
396                         // read one more char (after '\r')
397                         cache = source.Read ();
398                         if (cache != '\n')
399                                 return '\r';
400                         cache = int.MinValue;
401                         return '\n';
402                 }
403
404                 public override int ReadBlock (
405                         [In, Out] char [] buffer, int index, int count)
406                 {
407                         char [] tmp = new char [count];
408                         source.ReadBlock (tmp, 0, count);
409                         int j = index;
410                         for (int i = 0; i < count; j++) {
411                                 if (tmp [i] == '\r') {
412                                         if (++i < tmp.Length && tmp [i] == '\n')
413                                                 buffer [j] = tmp [i++];
414                                         else
415                                                 buffer [j] = '\r';
416                                 }
417                                 else
418                                         buffer [j] = tmp [i];
419                         }
420                         while (j < count) {
421                                 int d = Read ();
422                                 if (d < 0)
423                                         break;
424                                 buffer [j++] = (char) d;
425                         }
426                         return j;
427                 }
428
429                 public override string ReadLine ()
430                 {
431                         return source.ReadLine ().Replace ("\r\n", "\n");
432                 }
433
434                 public override string ReadToEnd ()
435                 {
436                         return source.ReadToEnd ().Replace ("\r\n", "\n");
437                 }
438
439         }
440