**** Merged from MCS ****
[mono-project.git] / mcs / class / corlib / System.Security.Cryptography / CryptoConfig.cs
blobb9291d8e95d2a7797fdec26a2d064d4f07d961e8
1 //
2 // CryptoConfig.cs: Handles cryptographic implementations and OIDs mappings.
3 //
4 // Author:
5 // Sebastien Pouliot (sebastien@ximian.com)
6 // Tim Coleman (tim@timcoleman.com)
7 //
8 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
9 // Copyright (C) Tim Coleman, 2004
10 // (C) 2004 Novell (http://www.novell.com)
14 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
23 //
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 //
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using System;
37 using System.Collections;
38 using System.Globalization;
39 using System.IO;
40 using System.Reflection;
41 using System.Runtime.CompilerServices;
42 using System.Text;
44 using Mono.Xml;
46 namespace System.Security.Cryptography {
48 public class CryptoConfig {
50 static private object lockObject;
51 static private Hashtable algorithms;
52 static private Hashtable oid;
54 private const string defaultNamespace = "System.Security.Cryptography.";
55 private const string defaultSHA1 = defaultNamespace + "SHA1CryptoServiceProvider";
56 private const string defaultMD5 = defaultNamespace + "MD5CryptoServiceProvider";
57 private const string defaultSHA256 = defaultNamespace + "SHA256Managed";
58 private const string defaultSHA384 = defaultNamespace + "SHA384Managed";
59 private const string defaultSHA512 = defaultNamespace + "SHA512Managed";
60 private const string defaultRSA = defaultNamespace + "RSACryptoServiceProvider";
61 private const string defaultDSA = defaultNamespace + "DSACryptoServiceProvider";
62 private const string defaultDES = defaultNamespace + "DESCryptoServiceProvider";
63 private const string default3DES = defaultNamespace + "TripleDESCryptoServiceProvider";
64 private const string defaultRC2 = defaultNamespace + "RC2CryptoServiceProvider";
65 private const string defaultAES = defaultNamespace + "RijndaelManaged";
66 // LAMESPEC: undocumented names in CryptoConfig
67 private const string defaultRNG = defaultNamespace + "RNGCryptoServiceProvider";
68 private const string defaultHMAC = defaultNamespace + "HMACSHA1";
69 private const string defaultMAC3DES = defaultNamespace + "MACTripleDES";
70 // LAMESPEC: undocumented classes (also undocumented in CryptoConfig ;-)
71 private const string defaultDSASigDesc = defaultNamespace + "DSASignatureDescription";
72 private const string defaultRSASigDesc = defaultNamespace + "RSAPKCS1SHA1SignatureDescription";
73 #if NET_2_0
74 private const string defaultRIPEMD160 = defaultNamespace + "RIPEMD160Managed";
75 #endif
77 // LAMESPEC: undocumented names in CryptoConfig
78 #if (NET_2_0)
79 private const string xmlAssembly = ", System.Security, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
80 #elif (NET_1_1)
81 private const string xmlAssembly = ", System.Security, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
82 #elif (NET_1_0)
83 private const string xmlAssembly = ", System.Security, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
84 #else
85 private const string xmlAssembly = ", System.Security, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null";
86 #endif
87 private const string defaultC14N = defaultNamespace + "Xml.XmlDsigC14NTransform" + xmlAssembly;
88 private const string defaultC14NWithComments = defaultNamespace + "Xml.XmlDsigC14NWithCommentsTransform" + xmlAssembly;
89 private const string defaultBase64 = defaultNamespace + "Xml.XmlDsigBase64Transform" + xmlAssembly;
90 private const string defaultXPath = defaultNamespace + "Xml.XmlDsigXPathTransform" + xmlAssembly;
91 private const string defaultXslt = defaultNamespace + "Xml.XmlDsigXsltTransform" + xmlAssembly;
92 private const string defaultEnveloped = defaultNamespace + "Xml.XmlDsigEnvelopedSignatureTransform" + xmlAssembly;
93 #if NET_2_0
94 private const string defaultXmlDecryption = defaultNamespace + "Xml.XmlDecryptionTransform" + xmlAssembly;
95 private const string defaultExcC14N = defaultNamespace + "Xml.XmlDsigExcC14NTransform" + xmlAssembly;
96 private const string defaultExcC14NWithComments = defaultNamespace + "Xml.XmlDsigExcC14NWithCommentsTransform" + xmlAssembly;
97 #endif
99 // LAMESPEC: only documentated in ".NET Framework Security" book
100 private const string defaultX509Data = defaultNamespace + "Xml.KeyInfoX509Data" + xmlAssembly;
101 private const string defaultKeyName = defaultNamespace + "Xml.KeyInfoName" + xmlAssembly;
102 private const string defaultKeyValueDSA = defaultNamespace + "Xml.DSAKeyValue" + xmlAssembly;
103 private const string defaultKeyValueRSA = defaultNamespace + "Xml.RSAKeyValue" + xmlAssembly;
104 private const string defaultRetrievalMethod = defaultNamespace + "Xml.KeyInfoRetrievalMethod" + xmlAssembly;
106 private const string managedSHA1 = defaultNamespace + "SHA1Managed";
108 // Oddly OID seems only available for hash algorithms
109 private const string oidSHA1 = "1.3.14.3.2.26";
110 private const string oidMD5 = "1.2.840.113549.2.5";
111 private const string oidSHA256 = "2.16.840.1.101.3.4.1";
112 private const string oidSHA384 = "2.16.840.1.101.3.4.2";
113 private const string oidSHA512 = "2.16.840.1.101.3.4.3";
114 // LAMESPEC: only documentated in ".NET Framework Security" book
115 private const string oid3DESKeyWrap = "1.2.840.113549.1.9.16.3.6";
117 private const string nameSHA1a = "SHA";
118 private const string nameSHA1b = "SHA1";
119 private const string nameSHA1c = "System.Security.Cryptography.SHA1";
120 private const string nameSHA1d = "System.Security.Cryptography.HashAlgorithm";
121 private const string nameMD5a = "MD5";
122 private const string nameMD5b = "System.Security.Cryptography.MD5";
123 private const string nameSHA256a = "SHA256";
124 private const string nameSHA256b = "SHA-256";
125 private const string nameSHA256c = "System.Security.Cryptography.SHA256";
126 private const string nameSHA384a = "SHA384";
127 private const string nameSHA384b = "SHA-384";
128 private const string nameSHA384c = "System.Security.Cryptography.SHA384";
129 private const string nameSHA512a = "SHA512";
130 private const string nameSHA512b = "SHA-512";
131 private const string nameSHA512c = "System.Security.Cryptography.SHA512";
132 private const string nameRSAa = "RSA";
133 private const string nameRSAb = "System.Security.Cryptography.RSA";
134 private const string nameRSAc = "System.Security.Cryptography.AsymmetricAlgorithm";
135 private const string nameDSAa = "DSA";
136 private const string nameDSAb = "System.Security.Cryptography.DSA";
137 private const string nameDESa = "DES";
138 private const string nameDESb = "System.Security.Cryptography.DES";
139 private const string name3DESa = "3DES";
140 private const string name3DESb = "TripleDES";
141 private const string name3DESc = "Triple DES";
142 private const string name3DESd = "System.Security.Cryptography.TripleDES";
143 private const string nameRC2a = "RC2";
144 private const string nameRC2b = "System.Security.Cryptography.RC2";
145 private const string nameAESa = "Rijndael";
146 private const string nameAESb = "System.Security.Cryptography.Rijndael";
147 private const string nameAESc = "System.Security.Cryptography.SymmetricAlgorithm";
148 // LAMESPEC: undocumented names in CryptoConfig
149 private const string nameRNGa = "RandomNumberGenerator";
150 private const string nameRNGb = "System.Security.Cryptography.RandomNumberGenerator";
151 private const string nameKeyHasha = "System.Security.Cryptography.KeyedHashAlgorithm";
152 private const string nameHMACa = "HMACSHA1";
153 private const string nameHMACb = "System.Security.Cryptography.HMACSHA1";
154 private const string nameMAC3DESa = "MACTripleDES";
155 private const string nameMAC3DESb = "System.Security.Cryptography.MACTripleDES";
156 // LAMESPEC: only documentated in ".NET Framework Security" book
157 private const string name3DESKeyWrap = "TripleDESKeyWrap";
158 #if NET_2_0
159 private const string nameRIPEMD160a = "RIPEMD160";
160 private const string nameRIPEMD160b = "RIPEMD-160";
161 private const string nameRIPEMD160c = "System.Security.Cryptography.RIPEMD160";
162 #endif
164 private const string urlXmlDsig = "http://www.w3.org/2000/09/xmldsig#";
165 // LAMESPEC: undocumented URLs in CryptoConfig
166 private const string urlDSASHA1 = urlXmlDsig + "dsa-sha1"; // no space
167 private const string urlRSASHA1 = urlXmlDsig + "rsa-sha1"; // no space
168 private const string urlSHA1 = urlXmlDsig + "sha1"; // no space
169 private const string urlC14N = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315";
170 private const string urlC14NWithComments = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments";
171 private const string urlBase64 = "http://www.w3.org/2000/09/xmldsig#base64";
172 private const string urlXPath = "http://www.w3.org/TR/1999/REC-xpath-19991116";
173 private const string urlXslt = "http://www.w3.org/TR/1999/REC-xslt-19991116";
174 private const string urlEnveloped = urlXmlDsig + "enveloped-signature"; // no space
175 #if NET_2_0
176 private const string urlXmlDecryption = "http://www.w3.org/2002/07/decrypt#XML";
177 private const string urlExcC14N = "http://www.w3.org/2001/10/xml-exc-c14n#WithComments";
178 private const string urlExcC14NWithComments = "http://www.w3.org/2001/10/xml-exc-c14n#";
179 #endif
181 // LAMESPEC: only documentated in ".NET Framework Security" book
182 private const string urlX509Data = urlXmlDsig + " X509Data"; // space is required
183 private const string urlKeyName = urlXmlDsig + " KeyName"; // space is required
184 private const string urlKeyValueDSA = urlXmlDsig + " KeyValue/DSAKeyValue"; // space is required
185 private const string urlKeyValueRSA = urlXmlDsig + " KeyValue/RSAKeyValue"; // space is required
186 private const string urlRetrievalMethod = urlXmlDsig + " RetrievalMethod"; // space is required
188 static CryptoConfig ()
190 // lock(this) is bad
191 // http://msdn.microsoft.com/library/en-us/dnaskdr/html/askgui06032003.asp?frame=true
192 lockObject = new object ();
195 private static void Initialize ()
197 algorithms = new Hashtable ();
198 // see list @ http://msdn.microsoft.com/library/en-us/cpref/html/
199 // frlrfSystemSecurityCryptographyCryptoConfigClassTopic.asp
200 algorithms.Add (nameSHA1a, defaultSHA1);
201 algorithms.Add (nameSHA1b, defaultSHA1);
202 algorithms.Add (nameSHA1c, defaultSHA1);
203 algorithms.Add (nameSHA1d, defaultSHA1);
205 algorithms.Add (nameMD5a, defaultMD5);
206 algorithms.Add (nameMD5b, defaultMD5);
208 algorithms.Add (nameSHA256a, defaultSHA256);
209 algorithms.Add (nameSHA256b, defaultSHA256);
210 algorithms.Add (nameSHA256c, defaultSHA256);
212 algorithms.Add (nameSHA384a, defaultSHA384);
213 algorithms.Add (nameSHA384b, defaultSHA384);
214 algorithms.Add (nameSHA384c, defaultSHA384);
216 algorithms.Add (nameSHA512a, defaultSHA512);
217 algorithms.Add (nameSHA512b, defaultSHA512);
218 algorithms.Add (nameSHA512c, defaultSHA512);
220 algorithms.Add (nameRSAa, defaultRSA);
221 algorithms.Add (nameRSAb, defaultRSA);
222 algorithms.Add (nameRSAc, defaultRSA);
224 algorithms.Add (nameDSAa, defaultDSA);
225 algorithms.Add (nameDSAb, defaultDSA);
227 algorithms.Add (nameDESa, defaultDES);
228 algorithms.Add (nameDESb, defaultDES);
230 algorithms.Add (name3DESa, default3DES);
231 algorithms.Add (name3DESb, default3DES);
232 algorithms.Add (name3DESc, default3DES);
233 algorithms.Add (name3DESd, default3DES);
235 algorithms.Add (nameRC2a, defaultRC2);
236 algorithms.Add (nameRC2b, defaultRC2);
238 algorithms.Add (nameAESa, defaultAES);
239 algorithms.Add (nameAESb, defaultAES);
240 // LAMESPEC SymmetricAlgorithm documented as TripleDESCryptoServiceProvider
241 algorithms.Add (nameAESc, defaultAES);
243 // LAMESPEC These names aren't documented but (hint) the classes also have
244 // static Create methods. So logically they should (and are) here.
245 algorithms.Add (nameRNGa, defaultRNG);
246 algorithms.Add (nameRNGb, defaultRNG);
247 algorithms.Add (nameKeyHasha, defaultHMAC);
248 algorithms.Add (nameHMACa, defaultHMAC);
249 algorithms.Add (nameHMACb, defaultHMAC);
250 algorithms.Add (nameMAC3DESa, defaultMAC3DES);
251 algorithms.Add (nameMAC3DESb, defaultMAC3DES);
252 #if NET_2_0
253 algorithms.Add (nameRIPEMD160a, defaultRIPEMD160);
254 algorithms.Add (nameRIPEMD160b, defaultRIPEMD160);
255 algorithms.Add (nameRIPEMD160c, defaultRIPEMD160);
256 #endif
258 // LAMESPEC These URLs aren't documented but (hint) installing the WSDK
259 // add some of the XMLDSIG urls into machine.config (and they make a LOT
260 // of sense for implementing XMLDSIG in System.Security.Cryptography.Xml)
261 algorithms.Add (urlDSASHA1, defaultDSASigDesc);
262 algorithms.Add (urlRSASHA1, defaultRSASigDesc);
263 algorithms.Add (urlSHA1, defaultSHA1);
264 algorithms.Add (urlC14N, defaultC14N);
265 algorithms.Add (urlC14NWithComments, defaultC14NWithComments);
266 algorithms.Add (urlBase64, defaultBase64);
267 algorithms.Add (urlXPath, defaultXPath);
268 algorithms.Add (urlXslt, defaultXslt);
269 algorithms.Add (urlEnveloped, defaultEnveloped);
270 #if NET_2_0
271 algorithms.Add (urlExcC14N, defaultExcC14N);
272 algorithms.Add (urlExcC14NWithComments, defaultExcC14NWithComments);
273 algorithms.Add (urlXmlDecryption, defaultXmlDecryption);
274 #endif
275 // LAMESPEC: only documentated in ".NET Framework Security" book
276 algorithms.Add (urlX509Data, defaultX509Data);
277 algorithms.Add (urlKeyName, defaultKeyName);
278 algorithms.Add (urlKeyValueDSA, defaultKeyValueDSA);
279 algorithms.Add (urlKeyValueRSA, defaultKeyValueRSA);
280 algorithms.Add (urlRetrievalMethod, defaultRetrievalMethod);
282 oid = new Hashtable ();
283 // comments here are to match with MS implementation (but not with doc)
284 // LAMESPEC: only HashAlgorithm seems to have their OID included
285 oid.Add (defaultSHA1, oidSHA1);
286 oid.Add (managedSHA1, oidSHA1);
287 oid.Add (nameSHA1b, oidSHA1);
288 oid.Add (nameSHA1c, oidSHA1);
290 oid.Add (defaultMD5, oidMD5);
291 oid.Add (nameMD5a, oidMD5);
292 oid.Add (nameMD5b, oidMD5);
294 oid.Add (defaultSHA256, oidSHA256);
295 oid.Add (nameSHA256a, oidSHA256);
296 oid.Add (nameSHA256c, oidSHA256);
298 oid.Add (defaultSHA384, oidSHA384);
299 oid.Add (nameSHA384a, oidSHA384);
300 oid.Add (nameSHA384c, oidSHA384);
302 oid.Add (defaultSHA512, oidSHA512);
303 oid.Add (nameSHA512a, oidSHA512);
304 oid.Add (nameSHA512c, oidSHA512);
306 // surprise! documented in ".NET Framework Security" book
307 oid.Add (name3DESKeyWrap, oid3DESKeyWrap);
309 // Add/modify the config as specified by machine.config
310 string config = Environment.GetMachineConfigPath ();
311 LoadConfig (config);
314 private static void LoadConfig (string filename)
316 if (!File.Exists (filename))
317 return;
319 SecurityParser sp = new SecurityParser ();
320 StreamReader sr = new StreamReader (filename);
321 try {
322 sp.LoadXml (sr.ReadToEnd ());
324 finally {
325 sr.Close ();
328 try {
329 SecurityElement root = sp.ToXml ();
330 SecurityElement mscorlib = root.SearchForChildByTag ("mscorlib");
331 if (mscorlib == null)
332 return;
333 SecurityElement cryptographySettings = mscorlib.SearchForChildByTag ("cryptographySettings");
334 if (cryptographySettings == null)
335 return;
337 // algorithms
338 SecurityElement cryptoNameMapping = cryptographySettings.SearchForChildByTag ("cryptoNameMapping");
339 if (cryptoNameMapping != null) {
340 SecurityElement cryptoClasses = cryptoNameMapping.SearchForChildByTag ("cryptoClasses");
341 if (cryptoClasses != null) {
342 foreach (SecurityElement nameEntry in cryptoNameMapping.Children) {
343 if (nameEntry.Tag == "nameEntry") {
344 string name = (string) nameEntry.Attributes ["name"];
345 string clas = (string) nameEntry.Attributes ["class"];
346 foreach (SecurityElement cryptoClass in cryptoClasses.Children) {
347 string fullname = (string) cryptoClass.Attributes [clas];
348 if (fullname != null) {
349 if (algorithms.ContainsKey (name))
350 algorithms.Remove (name);
351 algorithms.Add (name, fullname);
352 break;
360 // oid
361 SecurityElement oidMap = cryptographySettings.SearchForChildByTag ("oidMap");
362 if (oidMap == null)
363 return;
364 foreach (SecurityElement oidEntry in oidMap.Children) {
365 if (oidEntry.Tag == "oidEntry") {
366 string oids = (string) oidEntry.Attributes ["OID"];
367 if (oid.ContainsKey (oids))
368 oid.Remove (oids);
369 oid.Add (oids, oidEntry.Attributes ["name"]);
373 catch {
374 // there's no error/warning in case the machine.config contains bad entries
378 public static object CreateFromName (string name)
380 return CreateFromName (name, null);
383 public static object CreateFromName (string name, object[] args)
385 if (name == null)
386 throw new ArgumentNullException ("name");
388 if (algorithms == null) {
389 lock (lockObject) {
390 Initialize ();
394 try {
395 Type algoClass = null;
396 string algo = (string) algorithms [name];
397 // do we have an entry
398 if (algo == null)
399 algo = name;
400 algoClass = Type.GetType (algo);
401 // call the constructor for the type
402 return Activator.CreateInstance (algoClass, args);
404 catch {
405 // method doesn't throw any exception
406 return null;
410 // encode (7bits array) number greater than 127
411 private static byte[] EncodeLongNumber (long x)
413 // for MS BCL compatibility
414 // comment next two lines to remove restriction
415 if ((x > Int32.MaxValue) || (x < Int32.MinValue))
416 throw new OverflowException (Locale.GetText ("Part of OID doesn't fit in Int32"));
418 long y = x;
419 // number of bytes required to encode this number
420 int n = 1;
421 while (y > 0x7F) {
422 y = y >> 7;
423 n++;
425 byte[] num = new byte [n];
426 // encode all bytes
427 for (int i = 0; i < n; i++) {
428 y = x >> (7 * i);
429 y = y & 0x7F;
430 if (i != 0)
431 y += 0x80;
432 num[n-i-1] = Convert.ToByte (y);
434 return num;
437 public static byte[] EncodeOID (string str)
439 char[] delim = { '.' };
440 string[] parts = str.Split (delim);
441 // according to X.208 n is always at least 2
442 if (parts.Length < 2) {
443 throw new CryptographicUnexpectedOperationException (
444 Locale.GetText ("OID must have at least two parts"));
447 // we're sure that the encoded OID is shorter than its string representation
448 byte[] oid = new byte [str.Length];
449 // now encoding value
450 try {
451 byte part0 = Convert.ToByte (parts [0]);
452 // OID[0] > 2 is invalid but "supported" in MS BCL
453 // uncomment next line to trap this error
454 // if (part0 > 2) throw new CryptographicUnexpectedOperationException ();
455 byte part1 = Convert.ToByte (parts [1]);
456 // OID[1] >= 40 is illegal for OID[0] < 2 because of the % 40
457 // however the syntax is "supported" in MS BCL
458 // uncomment next 2 lines to trap this error
459 //if ((part0 < 2) && (part1 >= 40))
460 // throw new CryptographicUnexpectedOperationException ();
461 oid[2] = Convert.ToByte (part0 * 40 + part1);
463 catch {
464 throw new CryptographicUnexpectedOperationException (
465 Locale.GetText ("Invalid OID"));
467 int j = 3;
468 for (int i = 2; i < parts.Length; i++) {
469 long x = Convert.ToInt64 (parts [i]);
470 if (x > 0x7F) {
471 byte[] num = EncodeLongNumber (x);
472 Buffer.BlockCopy (num, 0, oid, j, num.Length);
473 j += num.Length;
475 else
476 oid[j++] = Convert.ToByte (x);
479 int k = 2;
480 // copy the exact number of byte required
481 byte[] oid2 = new byte [j];
482 oid2[0] = 0x06; // always - this tag means OID
483 // Length (of value)
484 if (j > 0x7F) {
485 // for compatibility with MS BCL
486 throw new CryptographicUnexpectedOperationException (
487 Locale.GetText ("OID > 127 bytes"));
488 // comment exception and uncomment next 3 lines to remove restriction
489 //byte[] num = EncodeLongNumber (j);
490 //Buffer.BlockCopy (num, 0, oid, j, num.Length);
491 //k = num.Length + 1;
493 else
494 oid2 [1] = Convert.ToByte (j - 2);
496 Buffer.BlockCopy (oid, k, oid2, k, j - k);
497 return oid2;
500 public static string MapNameToOID (string name)
502 if (name == null)
503 throw new ArgumentNullException ("name");
505 if (oid == null) {
506 lock (lockObject) {
507 Initialize ();
511 return (string)oid [name];