New test.
[mono.git] / mcs / class / corlib / Mono.Security.X509 / X501Name.cs
1 //
2 // X501Name.cs: X.501 Distinguished Names stuff 
3 //
4 // Author:
5 //      Sebastien Pouliot <sebastien@ximian.com>
6 //
7 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
8 // (C) 2004 Novell (http://www.novell.com)
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System;
33 using System.Globalization;
34 using System.Text;
35
36 using Mono.Security;
37
38 namespace Mono.Security.X509 {
39
40         // References:
41         // 1.   Information technology - Open Systems Interconnection - The Directory: Models
42         //      http://www.itu.int/rec/recommendation.asp?type=items&lang=e&parent=T-REC-X.501-200102-I
43         // 2.   RFC2253: Lightweight Directory Access Protocol (v3): UTF-8 String Representation of Distinguished Names
44         //      http://www.ietf.org/rfc/rfc2253.txt
45
46         /*
47          * Name ::= CHOICE { RDNSequence }
48          * 
49          * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
50          * 
51          * RelativeDistinguishedName ::= SET OF AttributeTypeAndValue
52          */
53 #if INSIDE_CORLIB
54         internal
55 #else
56         public 
57 #endif
58         sealed class X501 {
59
60                 static byte[] countryName = { 0x55, 0x04, 0x06 };
61                 static byte[] organizationName = { 0x55, 0x04, 0x0A };
62                 static byte[] organizationalUnitName = { 0x55, 0x04, 0x0B };
63                 static byte[] commonName = { 0x55, 0x04, 0x03 };
64                 static byte[] localityName = { 0x55, 0x04, 0x07 };
65                 static byte[] stateOrProvinceName = { 0x55, 0x04, 0x08 };
66                 static byte[] streetAddress = { 0x55, 0x04, 0x09 };
67                 //static byte[] serialNumber = { 0x55, 0x04, 0x05 };
68                 static byte[] domainComponent = { 0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x19 };
69                 static byte[] userid = { 0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x01 };
70                 static byte[] email = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01 };
71
72                 private X501 () 
73                 {
74                 }
75
76                 static public string ToString (ASN1 seq) 
77                 {
78                         StringBuilder sb = new StringBuilder ();
79                         for (int i = 0; i < seq.Count; i++) {
80                                 ASN1 entry = seq [i];
81                                 // multiple entries are valid
82                                 for (int k = 0; k < entry.Count; k++) {
83                                         ASN1 pair = entry [k];
84                                         ASN1 s = pair [1];
85                                         if (s == null)
86                                                 continue;
87
88                                         ASN1 poid = pair [0];
89                                         if (poid == null)
90                                                 continue;
91
92                                         if (poid.CompareValue (countryName))
93                                                 sb.Append ("C=");
94                                         else if (poid.CompareValue (organizationName))
95                                                 sb.Append ("O=");
96                                         else if (poid.CompareValue (organizationalUnitName))
97                                                 sb.Append ("OU=");
98                                         else if (poid.CompareValue (commonName))
99                                                 sb.Append ("CN=");
100                                         else if (poid.CompareValue (localityName))
101                                                 sb.Append ("L=");
102                                         else if (poid.CompareValue (stateOrProvinceName))
103                                                 sb.Append ("S=");       // NOTE: RFC2253 uses ST=
104                                         else if (poid.CompareValue (streetAddress))
105                                                 sb.Append ("STREET=");
106                                         else if (poid.CompareValue (domainComponent))
107                                                 sb.Append ("DC=");
108                                         else if (poid.CompareValue (userid))
109                                                 sb.Append ("UID=");
110                                         else if (poid.CompareValue (email))
111                                                 sb.Append ("E=");       // NOTE: Not part of RFC2253
112                                         else {
113                                                 // unknown OID
114                                                 sb.Append ("OID.");     // NOTE: Not present as RFC2253
115                                                 sb.Append (ASN1Convert.ToOid (poid));
116                                                 sb.Append ("=");
117                                         }
118
119                                         string sValue = null;
120                                         // 16bits or 8bits string ? TODO not complete (+special chars!)
121                                         if (s.Tag == 0x1E) {
122                                                 // BMPSTRING
123                                                 StringBuilder sb2 = new StringBuilder ();
124                                                 for (int j = 1; j < s.Value.Length; j += 2)
125                                                         sb2.Append ((char)s.Value[j]);
126                                                 sValue = sb2.ToString ();
127                                         } else {
128                                                 sValue = System.Text.Encoding.UTF8.GetString (s.Value);
129                                                 // in some cases we must quote (") the value
130                                                 // Note: this doesn't seems to conform to RFC2253
131                                                 char[] specials = { ',', '+', '"', '\\', '<', '>', ';' };
132                                                 if (sValue.IndexOfAny (specials, 0, sValue.Length) > 0)
133                                                         sValue = "\"" + sValue + "\"";
134                                                 else if (sValue.StartsWith (" "))
135                                                         sValue = "\"" + sValue + "\"";
136                                                 else if (sValue.EndsWith (" "))
137                                                         sValue = "\"" + sValue + "\"";
138                                         }
139
140                                         sb.Append (sValue);
141
142                                         // separator (not on last iteration)
143                                         if (k < entry.Count - 1)
144                                                 sb.Append (", ");
145                                 }
146
147                                 // separator (not on last iteration)
148                                 if (i < seq.Count - 1)
149                                         sb.Append (", ");
150                         }
151                         return sb.ToString ();
152                 }
153
154                 static private X520.AttributeTypeAndValue GetAttributeFromOid (string attributeType) 
155                 {
156                         switch (attributeType.ToUpper (CultureInfo.InvariantCulture).Trim ()) {
157                                 case "C":
158                                         return new X520.CountryName ();
159                                 case "O":
160                                         return new X520.OrganizationName ();
161                                 case "OU":
162                                         return new X520.OrganizationalUnitName ();
163                                 case "CN":
164                                         return new X520.CommonName ();
165                                 case "L":
166                                         return new X520.LocalityName ();
167                                 case "S":       // Microsoft
168                                 case "ST":      // RFC2253
169                                         return new X520.StateOrProvinceName ();
170                                 case "E":       // NOTE: Not part of RFC2253
171                                         return new X520.EmailAddress ();
172                                 case "DC":
173 //                                      return streetAddress;
174                                 case "UID":
175 //                                      return domainComponent;
176                                 default:
177                                         return null;
178                         }
179                 }
180
181                 static public ASN1 FromString (string rdn) 
182                 {
183                         if (rdn == null)
184                                 throw new ArgumentNullException ("rdn");
185                         // get string from here to ',' or end of string
186                         int start = 0;
187                         int end = 0;
188                         ASN1 asn1 = new ASN1 (0x30);
189                         while (start < rdn.Length) {
190                                 end = rdn.IndexOf (',', end) + 1;
191                                 if (end == 0)
192                                         end = rdn.Length + 1;
193                                 string av = rdn.Substring (start, end - start - 1);
194                                 // get '=' position in substring
195                                 int equal = av.IndexOf ('=');
196                                 // get AttributeType
197                                 string attributeType = av.Substring (0, equal);
198                                 // get value
199                                 string attributeValue = av.Substring (equal + 1);
200
201                                 X520.AttributeTypeAndValue atv = GetAttributeFromOid (attributeType);
202                                 atv.Value = attributeValue;
203                                 asn1.Add (new ASN1 (0x31, atv.GetBytes ()));
204
205                                 // next part
206                                 start = end;
207                                 if (start != - 1) {
208                                         if (end > rdn.Length)
209                                                 break;
210                                 }
211                         }
212                         return asn1;
213                 }
214         }
215 }