5 // Martin Baulig <martin.baulig@xamarin.com>
7 // Copyright (c) 2016 Xamarin Inc. (http://www.xamarin.com)
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:
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
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
26 #if SECURITY_DEP && MONO_FEATURE_BTLS
27 #if MONO_SECURITY_ALIAS
28 extern alias MonoSecurity;
33 using System.Threading;
34 using System.Threading.Tasks;
35 using System.Security.Cryptography.X509Certificates;
36 using System.Security.Authentication;
38 #if MONO_SECURITY_ALIAS
39 using MonoSecurity::Mono.Security.Interface;
40 using MX = MonoSecurity::Mono.Security.X509;
42 using Mono.Security.Interface;
43 using MX = Mono.Security.X509;
46 using MNS = Mono.Net.Security;
50 class MonoBtlsProvider : MonoTlsProvider
52 static readonly Guid id = new Guid ("432d18c9-9348-4b90-bfbf-9f2a10e1f15b");
54 public override Guid ID {
57 public override string Name {
58 get { return "btls"; }
61 internal MonoBtlsProvider ()
63 if (!MNS.MonoTlsProviderFactory.IsBtlsSupported ())
64 throw new NotSupportedException ("BTLS is not supported in this runtime.");
67 public override bool SupportsSslStream {
71 public override bool SupportsMonoExtensions {
75 public override bool SupportsConnectionInfo {
79 public override SslProtocols SupportedProtocols {
80 get { return SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls; }
83 public override IMonoSslStream CreateSslStream (
84 Stream innerStream, bool leaveInnerStreamOpen,
85 MonoTlsSettings settings = null)
87 return new MonoBtlsStream (
88 innerStream, leaveInnerStreamOpen, settings, this);
91 internal override bool HasNativeCertificates {
95 internal override X509Certificate2Impl GetNativeCertificate (
96 byte[] data, string password, X509KeyStorageFlags flags)
98 var impl = new X509CertificateImplBtls (false);
99 impl.Import (data, password, flags);
103 internal override X509Certificate2Impl GetNativeCertificate (
104 X509Certificate certificate)
106 var impl = certificate.Impl as X509CertificateImplBtls;
108 return (X509Certificate2Impl)impl.Clone ();
110 var data = certificate.GetRawCertData ();
111 return new X509CertificateImplBtls (data, MonoBtlsX509Format.DER, false);
114 internal static MonoBtlsX509VerifyParam GetVerifyParam (string targetHost, bool serverMode)
116 MonoBtlsX509VerifyParam param;
118 param = MonoBtlsX509VerifyParam.GetSslClient ();
120 param = MonoBtlsX509VerifyParam.GetSslServer ();
122 if (targetHost == null)
126 var copy = param.Copy ();
127 copy.SetHost (targetHost);
134 internal override bool ValidateCertificate (
135 ICertificateValidator2 validator, string targetHost, bool serverMode,
136 X509CertificateCollection certificates, bool wantsChain, ref X509Chain chain,
137 ref MonoSslPolicyErrors errors, ref int status11)
140 var chainImpl = (X509ChainImplBtls)chain.Impl;
141 var success = chainImpl.StoreCtx.VerifyResult == 1;
142 CheckValidationResult (
143 validator, targetHost, serverMode, certificates,
144 wantsChain, chain, chainImpl.StoreCtx,
145 success, ref errors, ref status11);
149 using (var store = new MonoBtlsX509Store ())
150 using (var nativeChain = MonoBtlsProvider.GetNativeChain (certificates))
151 using (var param = GetVerifyParam (targetHost, serverMode))
152 using (var storeCtx = new MonoBtlsX509StoreCtx ()) {
153 SetupCertificateStore (store, validator.Settings, serverMode);
155 storeCtx.Initialize (store, nativeChain);
157 storeCtx.SetVerifyParam (param);
159 var ret = storeCtx.Verify ();
161 var success = ret == 1;
163 if (wantsChain && chain == null) {
164 chain = GetManagedChain (nativeChain);
167 CheckValidationResult (
168 validator, targetHost, serverMode, certificates,
169 wantsChain, null, storeCtx,
170 success, ref errors, ref status11);
175 internal static bool ValidateCertificate (MonoBtlsX509Chain chain, MonoBtlsX509VerifyParam param)
177 using (var store = new MonoBtlsX509Store ())
178 using (var storeCtx = new MonoBtlsX509StoreCtx ()) {
179 SetupCertificateStore (store);
181 storeCtx.Initialize (store, chain);
184 storeCtx.SetVerifyParam (param);
186 var ret = storeCtx.Verify ();
192 void CheckValidationResult (
193 ICertificateValidator validator, string targetHost, bool serverMode,
194 X509CertificateCollection certificates, bool wantsChain,
195 X509Chain chain, MonoBtlsX509StoreCtx storeCtx,
196 bool success, ref MonoSslPolicyErrors errors, ref int status11)
199 errors = MonoSslPolicyErrors.RemoteCertificateChainErrors;
200 status11 = unchecked((int)0x800B010B);
204 internal static void SetupCertificateStore (MonoBtlsX509Store store, MonoTlsSettings settings, bool server)
206 if (settings?.CertificateSearchPaths == null)
207 AddTrustedRoots (store, settings, server);
210 SetupCertificateStore (store);
213 if (settings?.CertificateSearchPaths == null) {
214 SetupCertificateStore (store);
218 foreach (var path in settings.CertificateSearchPaths) {
219 if (string.Equals (path, "@default", StringComparison.Ordinal)) {
220 AddTrustedRoots (store, settings, server);
221 AddUserStore (store);
222 AddMachineStore (store);
223 } else if (string.Equals (path, "@user", StringComparison.Ordinal))
224 AddUserStore (store);
225 else if (string.Equals (path, "@machine", StringComparison.Ordinal))
226 AddMachineStore (store);
227 else if (string.Equals (path, "@trusted", StringComparison.Ordinal))
228 AddTrustedRoots (store, settings, server);
229 else if (path.StartsWith ("@pem:", StringComparison.Ordinal)) {
230 var realPath = path.Substring (5);
231 if (Directory.Exists (realPath))
232 store.AddDirectoryLookup (realPath, MonoBtlsX509FileType.PEM);
233 } else if (path.StartsWith ("@der:", StringComparison.Ordinal)) {
234 var realPath = path.Substring (5);
235 if (Directory.Exists (realPath))
236 store.AddDirectoryLookup (realPath, MonoBtlsX509FileType.ASN1);
238 if (Directory.Exists (path))
239 store.AddDirectoryLookup (path, MonoBtlsX509FileType.PEM);
245 internal static void SetupCertificateStore (MonoBtlsX509Store store)
248 store.SetDefaultPaths ();
249 store.AddAndroidLookup ();
251 AddUserStore (store);
252 AddMachineStore (store);
257 static void AddUserStore (MonoBtlsX509Store store)
259 var userPath = MonoBtlsX509StoreManager.GetStorePath (MonoBtlsX509StoreType.UserTrustedRoots);
260 if (Directory.Exists (userPath))
261 store.AddDirectoryLookup (userPath, MonoBtlsX509FileType.PEM);
264 static void AddMachineStore (MonoBtlsX509Store store)
266 var machinePath = MonoBtlsX509StoreManager.GetStorePath (MonoBtlsX509StoreType.MachineTrustedRoots);
267 if (Directory.Exists (machinePath))
268 store.AddDirectoryLookup (machinePath, MonoBtlsX509FileType.PEM);
272 static void AddTrustedRoots (MonoBtlsX509Store store, MonoTlsSettings settings, bool server)
274 if (settings?.TrustAnchors == null)
276 var trust = server ? MonoBtlsX509TrustKind.TRUST_CLIENT : MonoBtlsX509TrustKind.TRUST_SERVER;
277 store.AddCollection (settings.TrustAnchors, trust);
280 public static string GetSystemStoreLocation ()
283 return "/system/etc/security/cacerts";
285 return MonoBtlsX509StoreManager.GetStorePath (MonoBtlsX509StoreType.MachineTrustedRoots);
289 public static X509Certificate CreateCertificate (byte[] data, MonoBtlsX509Format format, bool disallowFallback = false)
291 using (var impl = new X509CertificateImplBtls (data, format, disallowFallback)) {
292 return new X509Certificate (impl);
296 public static X509Certificate2 CreateCertificate2 (byte[] data, MonoBtlsX509Format format, bool disallowFallback = false)
298 using (var impl = new X509CertificateImplBtls (data, format, disallowFallback)) {
299 return new X509Certificate2 (impl);
303 public static X509Certificate2 CreateCertificate2 (byte[] data, string password, bool disallowFallback = false)
305 using (var impl = new X509CertificateImplBtls (disallowFallback)) {
306 impl.Import (data, password, X509KeyStorageFlags.DefaultKeySet);
307 return new X509Certificate2 (impl);
311 public static X509Certificate CreateCertificate (MonoBtlsX509 x509)
313 using (var impl = new X509CertificateImplBtls (x509, true))
314 return new X509Certificate (impl);
317 public static X509Chain CreateChain ()
319 using (var impl = new X509ChainImplBtls ())
320 return new X509Chain (impl);
323 public static X509Chain GetManagedChain (MonoBtlsX509Chain chain)
325 var impl = new X509ChainImplBtls (chain);
326 return new X509Chain (impl);
329 public static MonoBtlsX509 GetBtlsCertificate (X509Certificate certificate)
331 var impl = certificate.Impl as X509CertificateImplBtls;
333 return impl.X509.Copy ();
335 return MonoBtlsX509.LoadFromData (certificate.GetRawCertData (), MonoBtlsX509Format.DER);
338 public static MonoBtlsX509Chain GetNativeChain (X509CertificateCollection certificates)
340 var chain = new MonoBtlsX509Chain ();
342 foreach (var cert in certificates) {
343 using (var x509 = GetBtlsCertificate (cert))
344 chain.AddCertificate (x509);