2 // X501Name.cs: X.501 Distinguished Names stuff
5 // Sebastien Pouliot <sebastien@ximian.com>
7 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
8 // (C) 2004 Novell (http://www.novell.com)
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:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
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.
33 using System.Globalization;
38 namespace Mono.Security.X509 {
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
47 * Name ::= CHOICE { RDNSequence }
49 * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
51 * RelativeDistinguishedName ::= SET OF AttributeTypeAndValue
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 };
76 static public string ToString (ASN1 seq)
78 StringBuilder sb = new StringBuilder ();
79 for (int i = 0; i < seq.Count; i++) {
81 // multiple entries are valid
\r
82 for (int k = 0; k < entry.Count; k++) {
\r
83 ASN1 pair = entry [k];
\r
88 ASN1 poid = pair [0];
\r
92 if (poid.CompareValue (countryName))
\r
94 else if (poid.CompareValue (organizationName))
\r
96 else if (poid.CompareValue (organizationalUnitName))
\r
98 else if (poid.CompareValue (commonName))
\r
100 else if (poid.CompareValue (localityName))
\r
102 else if (poid.CompareValue (stateOrProvinceName))
\r
103 sb.Append ("S="); // NOTE: RFC2253 uses ST=
\r
104 else if (poid.CompareValue (streetAddress))
\r
105 sb.Append ("STREET=");
\r
106 else if (poid.CompareValue (domainComponent))
\r
108 else if (poid.CompareValue (userid))
\r
109 sb.Append ("UID=");
\r
110 else if (poid.CompareValue (email))
\r
111 sb.Append ("E="); // NOTE: Not part of RFC2253
\r
114 sb.Append ("OID."); // NOTE: Not present as RFC2253
\r
115 sb.Append (ASN1Convert.ToOid (poid));
\r
119 string sValue = null;
\r
120 // 16bits or 8bits string ? TODO not complete (+special chars!)
\r
121 if (s.Tag == 0x1E) {
\r
123 StringBuilder sb2 = new StringBuilder ();
\r
124 for (int j = 1; j < s.Value.Length; j += 2)
\r
125 sb2.Append ((char)s.Value[j]);
\r
126 sValue = sb2.ToString ();
\r
128 sValue = System.Text.Encoding.UTF8.GetString (s.Value);
\r
129 // in some cases we must quote (") the value
\r
130 // Note: this doesn't seems to conform to RFC2253
\r
131 char[] specials = { ',', '+', '"', '\\', '<', '>', ';' };
\r
132 if (sValue.IndexOfAny (specials, 0, sValue.Length) > 0)
\r
133 sValue = "\"" + sValue + "\"";
\r
134 else if (sValue.StartsWith (" "))
\r
135 sValue = "\"" + sValue + "\"";
\r
136 else if (sValue.EndsWith (" "))
\r
137 sValue = "\"" + sValue + "\"";
\r
140 sb.Append (sValue);
\r
142 // separator (not on last iteration)
\r
143 if (k < entry.Count - 1)
\r
147 // separator (not on last iteration)
148 if (i < seq.Count - 1)
151 return sb.ToString ();
154 static private X520.AttributeTypeAndValue GetAttributeFromOid (string attributeType)
156 switch (attributeType.ToUpper (CultureInfo.InvariantCulture).Trim ()) {
158 return new X520.CountryName ();
160 return new X520.OrganizationName ();
162 return new X520.OrganizationalUnitName ();
164 return new X520.CommonName ();
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 ();
173 // return streetAddress;
175 // return domainComponent;
181 static public ASN1 FromString (string rdn)
184 throw new ArgumentNullException ("rdn");
185 // get string from here to ',' or end of string
188 ASN1 asn1 = new ASN1 (0x30);
189 while (start < rdn.Length) {
190 end = rdn.IndexOf (',', end) + 1;
192 end = rdn.Length + 1;
193 string av = rdn.Substring (start, end - start - 1);
194 // get '=' position in substring
195 int equal = av.IndexOf ('=');
197 string attributeType = av.Substring (0, equal);
199 string attributeValue = av.Substring (equal + 1);
201 X520.AttributeTypeAndValue atv = GetAttributeFromOid (attributeType);
202 atv.Value = attributeValue;
203 asn1.Add (new ASN1 (0x31, atv.GetBytes ()));
208 if (end > rdn.Length)