Remove duplicated files from system.dll (saves about 100kb)
[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 using System.Collections;
32 using System.Text;
33
34 using Mono.Security;
35 using MX = Mono.Security.X509;
36
37 namespace System.Security.Cryptography.X509Certificates {
38
39         [MonoTODO ("Some X500DistinguishedNameFlags options aren't supported, like DoNotUsePlusSign, DoNotUseQuotes and ForceUTF8Encoding")]
40         public sealed class X500DistinguishedName : AsnEncodedData {
41
42                 private const X500DistinguishedNameFlags AllFlags = X500DistinguishedNameFlags.Reversed |
43                         X500DistinguishedNameFlags.UseSemicolons | X500DistinguishedNameFlags.DoNotUsePlusSign | 
44                         X500DistinguishedNameFlags.DoNotUseQuotes | X500DistinguishedNameFlags.UseCommas | 
45                         X500DistinguishedNameFlags.UseNewLines | X500DistinguishedNameFlags.UseUTF8Encoding | 
46                         X500DistinguishedNameFlags.UseT61Encoding | X500DistinguishedNameFlags.ForceUTF8Encoding;
47
48                 private string name;
49
50
51                 public X500DistinguishedName (AsnEncodedData encodedDistinguishedName)
52                 {
53                         if (encodedDistinguishedName == null)
54                                 throw new ArgumentNullException ("encodedDistinguishedName");
55
56                         RawData = encodedDistinguishedName.RawData;
57                         if (RawData.Length > 0)
58                                 DecodeRawData ();
59                         else
60                                 name = String.Empty;
61                 }
62
63                 public X500DistinguishedName (byte[] encodedDistinguishedName)
64                 {
65                         if (encodedDistinguishedName == null)
66                                 throw new ArgumentNullException ("encodedDistinguishedName");
67
68                         Oid = new Oid ();
69                         RawData = encodedDistinguishedName;
70                         if (encodedDistinguishedName.Length > 0)
71                                 DecodeRawData ();
72                         else
73                                 name = String.Empty;
74                 }
75
76                 public X500DistinguishedName (string distinguishedName)
77                         : this (distinguishedName, X500DistinguishedNameFlags.Reversed)
78                 {
79                 }
80
81                 public X500DistinguishedName (string distinguishedName, X500DistinguishedNameFlags flag)
82                 {
83                         if (distinguishedName == null)
84                                 throw new ArgumentNullException ("distinguishedName");
85                         if ((flag != 0) && ((flag & AllFlags) == 0))
86                                 throw new ArgumentException ("flag");
87
88                         Oid = new Oid ();
89                         if (distinguishedName.Length == 0) {
90                                 // empty (0x00) ASN.1 sequence (0x30)
91                                 RawData = new byte [2] { 0x30, 0x00 };
92                                 DecodeRawData ();
93                         } else {
94                                 ASN1 dn = MX.X501.FromString (distinguishedName);
95                                 if ((flag & X500DistinguishedNameFlags.Reversed) != 0) {
96                                         ASN1 rdn = new ASN1 (0x30);
97                                         for (int i = dn.Count - 1; i >= 0; i--) 
98                                                 rdn.Add (dn [i]);
99                                         dn = rdn;
100                                 }
101                                 RawData = dn.GetBytes ();
102                                 if (flag == X500DistinguishedNameFlags.None)
103                                         name = distinguishedName;
104                                 else
105                                         name = Decode (flag);
106                         }
107                 }
108
109                 public X500DistinguishedName (X500DistinguishedName distinguishedName)
110                 {
111                         if (distinguishedName == null)
112                                 throw new ArgumentNullException ("distinguishedName");
113
114                         Oid = new Oid ();
115                         RawData = distinguishedName.RawData;
116                         name = distinguishedName.name;
117                 }
118
119
120                 public string Name {
121                         get { return name; }
122                 }
123
124
125                 public string Decode (X500DistinguishedNameFlags flag)
126                 {
127                         if ((flag != 0) && ((flag & AllFlags) == 0))
128                                 throw new ArgumentException ("flag");
129
130                         if (RawData.Length == 0)
131                                 return String.Empty;
132
133                         // Mono.Security reversed isn't the same as fx 2.0 (which is the reverse of 1.x)
134                         bool reversed = ((flag & X500DistinguishedNameFlags.Reversed) != 0);
135                         bool quotes = ((flag & X500DistinguishedNameFlags.DoNotUseQuotes) == 0);
136                         string separator = GetSeparator (flag);
137
138                         ASN1 rdn = new ASN1 (RawData);
139                         return MX.X501.ToString (rdn, reversed, separator, quotes);
140                 }
141
142                 public override string Format (bool multiLine)
143                 {
144                         if (multiLine) {
145                                 string s = Decode (X500DistinguishedNameFlags.UseNewLines);
146                                 if (s.Length > 0)
147                                         return s + Environment.NewLine;
148                                 else
149                                         return s;
150                         } else {
151                                 return Decode (X500DistinguishedNameFlags.UseCommas);
152                         }
153                 }
154
155                 // private stuff
156
157                 private static string GetSeparator (X500DistinguishedNameFlags flag)
158                 {
159                         if ((flag & X500DistinguishedNameFlags.UseSemicolons) != 0)
160                                 return "; ";
161                         if ((flag & X500DistinguishedNameFlags.UseCommas) != 0)
162                                 return ", ";
163                         if ((flag & X500DistinguishedNameFlags.UseNewLines) != 0)
164                                 return Environment.NewLine;
165                         return ", "; //default
166                 }
167
168                 // decode the DN using the (byte[]) RawData
169                 private void DecodeRawData ()
170                 {
171                         if ((RawData == null) || (RawData.Length < 3)) {
172                                 name = String.Empty;
173                                 return;
174                         }
175
176                         ASN1 sequence = new ASN1 (RawData);
177                         name = MX.X501.ToString (sequence, true, ", ", true);
178                 }
179
180                 private static string Canonize (string s)
181                 {
182                         int i = s.IndexOf ('=') + 1;
183                         StringBuilder r = new StringBuilder (s.Substring (0, i));
184                         // skip any white space starting the value
185                         while (i < s.Length && Char.IsWhiteSpace (s, i))
186                                 i++;
187                         // ensure we skip white spaces at the end of the value
188                         s = s.TrimEnd ();
189                         // keep track of internal multiple spaces
190                         bool space = false;
191                         for (; i < s.Length; i++) {
192                                 if (space) {
193                                         space = Char.IsWhiteSpace (s, i);
194                                         if (space)
195                                                 continue;
196                                 }
197                                 if (Char.IsWhiteSpace (s, i))
198                                         space = true;
199                                 r.Append (Char.ToUpperInvariant (s[i]));
200                         }
201                         return r.ToString ();
202                 }
203
204                 // of all X500DistinguishedNameFlags flags nothing can do a "correct" comparison :|
205                 internal static bool AreEqual (X500DistinguishedName name1, X500DistinguishedName name2)
206                 {
207                         if (name1 == null)
208                                 return (name2 == null);
209                         if (name2 == null)
210                                 return false;
211
212                         X500DistinguishedNameFlags flags = X500DistinguishedNameFlags.UseNewLines | X500DistinguishedNameFlags.DoNotUseQuotes;
213                         string[] split = new string[] { Environment.NewLine };
214                         string[] parts1 = name1.Decode (flags).Split (split, StringSplitOptions.RemoveEmptyEntries);
215                         string[] parts2 = name2.Decode (flags).Split (split, StringSplitOptions.RemoveEmptyEntries);
216                         if (parts1.Length != parts2.Length)
217                                 return false;
218
219                         for (int i = 0; i < parts1.Length; i++) {
220                                 if (Canonize (parts1[i]) != Canonize (parts2[i]))
221                                         return false;
222                         }
223                         return true;
224                 }
225         }
226 }
227
228 #endif