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