1 //
2 // MonoBtlsUtils.cs
3 //
4 // Author:
5 //       Martin Baulig <martin.baulig@xamarin.com>
6 //
7 // Copyright (c) 2016 Xamarin Inc. (http://www.xamarin.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining a copy
10 // of this software and associated documentation files (the "Software"), to deal
11 // in the Software without restriction, including without limitation the rights
12 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 // copies of the Software, and to permit persons to whom the Software is
14 // furnished to do so, subject to the following conditions:
15 //
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
18 //
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 // THE SOFTWARE.
26 #if SECURITY_DEP && MONO_FEATURE_BTLS
27 using System;
28 using System.Text;
29 using System.Security.Cryptography.X509Certificates;
30 
31 namespace Mono.Btls
32 {
33 	static class MonoBtlsUtils
34 	{
35 		static byte[] emailOid = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01 };
36 
Compare(byte[] a, byte[] b)37 		public static bool Compare (byte[] a, byte[] b)
38 		{
39 			if (a.Length != b.Length)
40 				return false;
41 			for (int i = 0; i < a.Length; i++) {
42 				if (a[i] != b[i])
43 					return false;
44 			}
45 			return true;
46 		}
47 
AppendEntry(StringBuilder sb, MonoBtlsX509Name name, int index, string separator, bool quotes)48 		static bool AppendEntry (StringBuilder sb, MonoBtlsX509Name name, int index, string separator, bool quotes)
49 		{
50 			var type = name.GetEntryType (index);
51 			if (type < 0)
52 				return false;
53 			else if (type == 0) {
54 				var oidValue = name.GetEntryOidData (index);
55 				if (Compare (oidValue, emailOid))
56 					type = MonoBtlsX509NameEntryType.Email;
57 			}
58 			int tag;
59 			var text = name.GetEntryValue (index, out tag);
60 			if (text == null)
61 				return false;
62 			var oid = name.GetEntryOid (index);
63 			if (oid == null)
64 				return false;
65 
66 			if (sb.Length > 0)
67 				sb.Append (separator);
68 
69 			switch (type) {
70 			case MonoBtlsX509NameEntryType.CountryName:
71 				sb.Append ("C=");
72 				break;
73 			case MonoBtlsX509NameEntryType.OrganizationName:
74 				sb.Append ("O=");
75 				break;
76 			case MonoBtlsX509NameEntryType.OrganizationalUnitName:
77 				sb.Append ("OU=");
78 				break;
79 			case MonoBtlsX509NameEntryType.CommonName:
80 				sb.Append ("CN=");
81 				break;
82 			case MonoBtlsX509NameEntryType.LocalityName:
83 				sb.Append ("L=");
84 				break;
85 			case MonoBtlsX509NameEntryType.StateOrProvinceName:
86 				sb.Append ("S=");       // NOTE: RFC2253 uses ST=
87 				break;
88 			case MonoBtlsX509NameEntryType.StreetAddress:
89 				sb.Append ("STREET=");
90 				break;
91 			case MonoBtlsX509NameEntryType.DomainComponent:
92 				sb.Append ("DC=");
93 				break;
94 			case MonoBtlsX509NameEntryType.UserId:
95 				sb.Append ("UID=");
96 				break;
97 			case MonoBtlsX509NameEntryType.Email:
98 				sb.Append ("E=");       // NOTE: Not part of RFC2253
99 				break;
100 			case MonoBtlsX509NameEntryType.DnQualifier:
101 				sb.Append ("dnQualifier=");
102 				break;
103 			case MonoBtlsX509NameEntryType.Title:
104 				sb.Append ("T=");
105 				break;
106 			case MonoBtlsX509NameEntryType.Surname:
107 				sb.Append ("SN=");
108 				break;
109 			case MonoBtlsX509NameEntryType.GivenName:
110 				sb.Append ("G=");
111 				break;
112 			case MonoBtlsX509NameEntryType.Initial:
113 				sb.Append ("I=");
114 				break;
115 			default:
116 				// unknown OID
117 				sb.Append ("OID.");     // NOTE: Not present as RFC2253
118 				sb.Append (oid);
119 				sb.Append ("=");
120 				break;
121 			}
122 
123 			// 16bits or 8bits string ? TODO not complete (+special chars!)
124 			char[] specials = { ',', '+', '"', '\\', '<', '>', ';' };
125 			if (quotes && tag != 0x1E) {
126 				if ((text.IndexOfAny (specials, 0, text.Length) > 0) ||
127 				    text.StartsWith (" ") || (text.EndsWith (" ")))
128 					text = "\"" + text + "\"";
129 			}
130 
131 			sb.Append (text);
132 			return true;
133 		}
134 
135 		const X500DistinguishedNameFlags AllFlags = X500DistinguishedNameFlags.Reversed |
136 			X500DistinguishedNameFlags.UseSemicolons | X500DistinguishedNameFlags.DoNotUsePlusSign |
137 			X500DistinguishedNameFlags.DoNotUseQuotes | X500DistinguishedNameFlags.UseCommas |
138 			X500DistinguishedNameFlags.UseNewLines | X500DistinguishedNameFlags.UseUTF8Encoding |
139 			X500DistinguishedNameFlags.UseT61Encoding | X500DistinguishedNameFlags.ForceUTF8Encoding;
140 
GetSeparator(X500DistinguishedNameFlags flag)141 		static string GetSeparator (X500DistinguishedNameFlags flag)
142 		{
143 			if ((flag & X500DistinguishedNameFlags.UseSemicolons) != 0)
144 				return "; ";
145 			if ((flag & X500DistinguishedNameFlags.UseCommas) != 0)
146 				return ", ";
147 			if ((flag & X500DistinguishedNameFlags.UseNewLines) != 0)
148 				return Environment.NewLine;
149 			return ", "; //default
150 		}
151 
FormatName(MonoBtlsX509Name name, X500DistinguishedNameFlags flag)152 		public static string FormatName (MonoBtlsX509Name name, X500DistinguishedNameFlags flag)
153 		{
154 			if ((flag != 0) && ((flag & AllFlags) == 0))
155 				throw new ArgumentException ("flag");
156 
157 			if (name.GetEntryCount () == 0)
158 				return String.Empty;
159 
160 			// Mono.Security reversed isn't the same as fx 2.0 (which is the reverse of 1.x)
161 			bool reversed = ((flag & X500DistinguishedNameFlags.Reversed) != 0);
162 			bool quotes = ((flag & X500DistinguishedNameFlags.DoNotUseQuotes) == 0);
163 			string separator = GetSeparator (flag);
164 
165 			return FormatName (name, reversed, separator, quotes);
166 		}
167 
FormatName(MonoBtlsX509Name name, bool reversed, string separator, bool quotes)168 		public static string FormatName (MonoBtlsX509Name name, bool reversed, string separator, bool quotes)
169 		{
170 			var count = name.GetEntryCount ();
171 			StringBuilder sb = new StringBuilder ();
172 
173 			if (reversed) {
174 				for (int i = count - 1; i >= 0; i--) {
175 					AppendEntry (sb, name, i, separator, quotes);
176 				}
177 			} else {
178 				for (int i = 0; i < count; i++) {
179 					AppendEntry (sb, name, i, separator, quotes);
180 				}
181 			}
182 
183 			return sb.ToString ();
184 		}
185 	}
186 }
187 #endif
188