Merge pull request #2816 from xmcclure/profile-clean-0
[mono.git] / mcs / class / System / System.Security.Cryptography.X509Certificates / X500DistinguishedName.cs
1 //
2 // System.Security.Cryptography.X509Certificates.X500DistinguishedName
3 //
4 // Author:
5 //      Sebastien Pouliot  <sebastien@ximian.com>
6 //
7 // Copyright (C) 2004-2006 Novell Inc. (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 #if SECURITY_DEP
30
31 #if MONO_SECURITY_ALIAS
32 extern alias MonoSecurity;
33 using MonoSecurity::Mono.Security;
34 using MX = MonoSecurity::Mono.Security.X509;
35 #else
36 using Mono.Security;
37 using MX = Mono.Security.X509;
38 #endif
39
40 using System.Collections;
41 using System.Text;
42
43 namespace System.Security.Cryptography.X509Certificates {
44
45         [MonoTODO ("Some X500DistinguishedNameFlags options aren't supported, like DoNotUsePlusSign, DoNotUseQuotes and ForceUTF8Encoding")]
46         public sealed class X500DistinguishedName : AsnEncodedData {
47
48                 private const X500DistinguishedNameFlags AllFlags = X500DistinguishedNameFlags.Reversed |
49                         X500DistinguishedNameFlags.UseSemicolons | X500DistinguishedNameFlags.DoNotUsePlusSign | 
50                         X500DistinguishedNameFlags.DoNotUseQuotes | X500DistinguishedNameFlags.UseCommas | 
51                         X500DistinguishedNameFlags.UseNewLines | X500DistinguishedNameFlags.UseUTF8Encoding | 
52                         X500DistinguishedNameFlags.UseT61Encoding | X500DistinguishedNameFlags.ForceUTF8Encoding;
53
54                 private string name;
55                 private byte[] canonEncoding;
56
57
58                 public X500DistinguishedName (AsnEncodedData encodedDistinguishedName)
59                 {
60                         if (encodedDistinguishedName == null)
61                                 throw new ArgumentNullException ("encodedDistinguishedName");
62
63                         RawData = encodedDistinguishedName.RawData;
64                         if (RawData.Length > 0)
65                                 DecodeRawData ();
66                         else
67                                 name = String.Empty;
68                 }
69
70                 public X500DistinguishedName (byte[] encodedDistinguishedName)
71                 {
72                         if (encodedDistinguishedName == null)
73                                 throw new ArgumentNullException ("encodedDistinguishedName");
74
75                         Oid = new Oid ();
76                         RawData = encodedDistinguishedName;
77                         if (encodedDistinguishedName.Length > 0)
78                                 DecodeRawData ();
79                         else
80                                 name = String.Empty;
81                 }
82
83                 public X500DistinguishedName (string distinguishedName)
84                         : this (distinguishedName, X500DistinguishedNameFlags.Reversed)
85                 {
86                 }
87
88                 public X500DistinguishedName (string distinguishedName, X500DistinguishedNameFlags flag)
89                 {
90                         if (distinguishedName == null)
91                                 throw new ArgumentNullException ("distinguishedName");
92                         if ((flag != 0) && ((flag & AllFlags) == 0))
93                                 throw new ArgumentException ("flag");
94
95                         Oid = new Oid ();
96                         if (distinguishedName.Length == 0) {
97                                 // empty (0x00) ASN.1 sequence (0x30)
98                                 RawData = new byte [2] { 0x30, 0x00 };
99                                 DecodeRawData ();
100                         } else {
101                                 var dn = MX.X501.FromString (distinguishedName);
102                                 if ((flag & X500DistinguishedNameFlags.Reversed) != 0) {
103                                         ASN1 rdn = new ASN1 (0x30);
104                                         for (int i = dn.Count - 1; i >= 0; i--) 
105                                                 rdn.Add (dn [i]);
106                                         dn = rdn;
107                                 }
108                                 RawData = dn.GetBytes ();
109                                 if (flag == X500DistinguishedNameFlags.None)
110                                         name = distinguishedName;
111                                 else
112                                         name = Decode (flag);
113                         }
114                 }
115
116                 public X500DistinguishedName (X500DistinguishedName distinguishedName)
117                 {
118                         if (distinguishedName == null)
119                                 throw new ArgumentNullException ("distinguishedName");
120
121                         Oid = new Oid ();
122                         RawData = distinguishedName.RawData;
123                         name = distinguishedName.name;
124                 }
125
126                 internal X500DistinguishedName (byte[] encoded, byte[] canonEncoding, string name)
127                         : this (encoded)
128                 {
129                         this.canonEncoding = canonEncoding;
130                         this.name = name;
131
132                         Oid = new Oid ();
133                         RawData = encoded;
134                 }
135
136                 internal byte[] CanonicalEncoding {
137                         get { return canonEncoding; }
138                 }
139
140
141                 public string Name {
142                         get { return name; }
143                 }
144
145
146                 public string Decode (X500DistinguishedNameFlags flag)
147                 {
148                         if ((flag != 0) && ((flag & AllFlags) == 0))
149                                 throw new ArgumentException ("flag");
150
151                         if (RawData.Length == 0)
152                                 return String.Empty;
153
154                         // Mono.Security reversed isn't the same as fx 2.0 (which is the reverse of 1.x)
155                         bool reversed = ((flag & X500DistinguishedNameFlags.Reversed) != 0);
156                         bool quotes = ((flag & X500DistinguishedNameFlags.DoNotUseQuotes) == 0);
157                         string separator = GetSeparator (flag);
158
159                         ASN1 rdn = new ASN1 (RawData);
160                         return MX.X501.ToString (rdn, reversed, separator, quotes);
161                 }
162
163                 public override string Format (bool multiLine)
164                 {
165                         if (multiLine) {
166                                 string s = Decode (X500DistinguishedNameFlags.UseNewLines);
167                                 if (s.Length > 0)
168                                         return s + Environment.NewLine;
169                                 else
170                                         return s;
171                         } else {
172                                 return Decode (X500DistinguishedNameFlags.UseCommas);
173                         }
174                 }
175
176                 // private stuff
177
178                 private static string GetSeparator (X500DistinguishedNameFlags flag)
179                 {
180                         if ((flag & X500DistinguishedNameFlags.UseSemicolons) != 0)
181                                 return "; ";
182                         if ((flag & X500DistinguishedNameFlags.UseCommas) != 0)
183                                 return ", ";
184                         if ((flag & X500DistinguishedNameFlags.UseNewLines) != 0)
185                                 return Environment.NewLine;
186                         return ", "; //default
187                 }
188
189                 // decode the DN using the (byte[]) RawData
190                 private void DecodeRawData ()
191                 {
192                         if ((RawData == null) || (RawData.Length < 3)) {
193                                 name = String.Empty;
194                                 return;
195                         }
196
197                         ASN1 sequence = new ASN1 (RawData);
198                         name = MX.X501.ToString (sequence, true, ", ", true);
199                 }
200
201                 private static string Canonize (string s)
202                 {
203                         int i = s.IndexOf ('=') + 1;
204                         StringBuilder r = new StringBuilder (s.Substring (0, i));
205                         // skip any white space starting the value
206                         while (i < s.Length && Char.IsWhiteSpace (s, i))
207                                 i++;
208                         // ensure we skip white spaces at the end of the value
209                         s = s.TrimEnd ();
210                         // keep track of internal multiple spaces
211                         bool space = false;
212                         for (; i < s.Length; i++) {
213                                 if (space) {
214                                         space = Char.IsWhiteSpace (s, i);
215                                         if (space)
216                                                 continue;
217                                 }
218                                 if (Char.IsWhiteSpace (s, i))
219                                         space = true;
220                                 r.Append (Char.ToUpperInvariant (s[i]));
221                         }
222                         return r.ToString ();
223                 }
224
225                 // of all X500DistinguishedNameFlags flags nothing can do a "correct" comparison :|
226                 internal static bool AreEqual (X500DistinguishedName name1, X500DistinguishedName name2)
227                 {
228                         if (name1 == null)
229                                 return (name2 == null);
230                         if (name2 == null)
231                                 return false;
232
233                         if (name1.canonEncoding != null && name2.canonEncoding != null) {
234                                 if (name1.canonEncoding.Length != name2.canonEncoding.Length)
235                                         return false;
236                                 for (int i = 0; i < name1.canonEncoding.Length; i++) {
237                                         if (name1.canonEncoding[i] != name2.canonEncoding[2])
238                                                 return false;
239                                 }
240                                 return true;
241                         }
242
243                         X500DistinguishedNameFlags flags = X500DistinguishedNameFlags.UseNewLines | X500DistinguishedNameFlags.DoNotUseQuotes;
244                         string[] split = new string[] { Environment.NewLine };
245                         string[] parts1 = name1.Decode (flags).Split (split, StringSplitOptions.RemoveEmptyEntries);
246                         string[] parts2 = name2.Decode (flags).Split (split, StringSplitOptions.RemoveEmptyEntries);
247                         if (parts1.Length != parts2.Length)
248                                 return false;
249
250                         for (int i = 0; i < parts1.Length; i++) {
251                                 if (Canonize (parts1[i]) != Canonize (parts2[i]))
252                                         return false;
253                         }
254                         return true;
255                 }
256         }
257 }
258
259 #endif