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.Net.Security;
36 using System.Security.Cryptography.X509Certificates;
37 using System.Security.Authentication;
39 #if MONO_SECURITY_ALIAS
40 using MonoSecurity::Mono.Security.Interface;
41 using MX = MonoSecurity::Mono.Security.X509;
43 using Mono.Security.Interface;
44 using MX = Mono.Security.X509;
47 using MNS = Mono.Net.Security;
51 class MonoBtlsProvider : MonoTlsProvider
53 static readonly Guid id = new Guid ("432d18c9-9348-4b90-bfbf-9f2a10e1f15b");
55 public override Guid ID {
58 public override string Name {
59 get { return "btls"; }
62 internal MonoBtlsProvider ()
64 if (!MNS.MonoTlsProviderFactory.IsBtlsSupported ())
65 throw new NotSupportedException ("BTLS is not supported in this runtime.");
68 public override bool SupportsSslStream {
72 public override bool SupportsMonoExtensions {
76 public override bool SupportsConnectionInfo {
80 public override SslProtocols SupportedProtocols {
81 get { return SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls; }
84 public override IMonoSslStream CreateSslStream (
85 Stream innerStream, bool leaveInnerStreamOpen,
86 MonoTlsSettings settings = null)
88 return SslStream.CreateMonoSslStream (innerStream, leaveInnerStreamOpen, this, settings);
91 internal override IMonoSslStream CreateSslStreamInternal (
92 SslStream sslStream, Stream innerStream, bool leaveInnerStreamOpen,
93 MonoTlsSettings settings)
95 return new MonoBtlsStream (
96 innerStream, leaveInnerStreamOpen, sslStream, settings, this);
99 internal override bool HasNativeCertificates {
103 internal override X509Certificate2Impl GetNativeCertificate (
104 byte[] data, string password, X509KeyStorageFlags flags)
106 var impl = new X509CertificateImplBtls (false);
107 impl.Import (data, password, flags);
111 internal override X509Certificate2Impl GetNativeCertificate (
112 X509Certificate certificate)
114 var impl = certificate.Impl as X509CertificateImplBtls;
116 return (X509Certificate2Impl)impl.Clone ();
118 var data = certificate.GetRawCertData ();
119 return new X509CertificateImplBtls (data, MonoBtlsX509Format.DER, false);
122 internal static MonoBtlsX509VerifyParam GetVerifyParam (MonoTlsSettings settings, string targetHost, bool serverMode)
124 MonoBtlsX509VerifyParam param;
126 param = MonoBtlsX509VerifyParam.GetSslClient ();
128 param = MonoBtlsX509VerifyParam.GetSslServer ();
130 if (targetHost == null && settings?.CertificateValidationTime == null)
134 var copy = param.Copy ();
135 if (targetHost != null)
136 copy.SetHost (targetHost);
137 if (settings?.CertificateValidationTime != null)
138 copy.SetTime (settings.CertificateValidationTime.Value);
145 internal override bool ValidateCertificate (
146 ICertificateValidator2 validator, string targetHost, bool serverMode,
147 X509CertificateCollection certificates, bool wantsChain, ref X509Chain chain,
148 ref MonoSslPolicyErrors errors, ref int status11)
151 var chainImpl = (X509ChainImplBtls)chain.Impl;
152 var success = chainImpl.StoreCtx.VerifyResult == 1;
153 CheckValidationResult (
154 validator, targetHost, serverMode, certificates,
155 wantsChain, chain, chainImpl.StoreCtx,
156 success, ref errors, ref status11);
160 using (var store = new MonoBtlsX509Store ())
161 using (var nativeChain = MonoBtlsProvider.GetNativeChain (certificates))
162 using (var param = GetVerifyParam (validator.Settings, targetHost, serverMode))
163 using (var storeCtx = new MonoBtlsX509StoreCtx ()) {
164 SetupCertificateStore (store, validator.Settings, serverMode);
166 storeCtx.Initialize (store, nativeChain);
168 storeCtx.SetVerifyParam (param);
170 var ret = storeCtx.Verify ();
172 var success = ret == 1;
174 if (wantsChain && chain == null) {
175 chain = GetManagedChain (nativeChain);
178 CheckValidationResult (
179 validator, targetHost, serverMode, certificates,
180 wantsChain, null, storeCtx,
181 success, ref errors, ref status11);
186 internal static bool ValidateCertificate (MonoBtlsX509Chain chain, MonoBtlsX509VerifyParam param)
188 using (var store = new MonoBtlsX509Store ())
189 using (var storeCtx = new MonoBtlsX509StoreCtx ()) {
191 * We're called from X509Certificate2.Verify() via X509CertificateImplBtls.Verify().
193 * Use the default settings and assume client-mode.
195 SetupCertificateStore (store, MonoTlsSettings.DefaultSettings, false);
197 storeCtx.Initialize (store, chain);
200 storeCtx.SetVerifyParam (param);
202 var ret = storeCtx.Verify ();
208 void CheckValidationResult (
209 ICertificateValidator validator, string targetHost, bool serverMode,
210 X509CertificateCollection certificates, bool wantsChain,
211 X509Chain chain, MonoBtlsX509StoreCtx storeCtx,
212 bool success, ref MonoSslPolicyErrors errors, ref int status11)
215 errors = MonoSslPolicyErrors.RemoteCertificateChainErrors;
216 status11 = unchecked((int)0x800B010B);
220 internal static void SetupCertificateStore (MonoBtlsX509Store store, MonoTlsSettings settings, bool server)
223 * In server-mode, we only add certificates which are explicitly trusted via
224 * MonoTlsSettings.TrustAnchors.
226 * MonoTlsSettings.CertificateSearchPaths is ignored on Android.
231 AddTrustedRoots (store, settings, server);
233 SetupDefaultCertificateStore (store);
236 if (server || settings?.CertificateSearchPaths == null) {
237 AddTrustedRoots (store, settings, server);
239 SetupDefaultCertificateStore (store);
243 foreach (var path in settings.CertificateSearchPaths) {
246 AddTrustedRoots (store, settings, server);
247 AddUserStore (store);
248 AddMachineStore (store);
251 AddTrustedRoots (store, settings, server);
254 AddUserStore (store);
257 AddMachineStore (store);
260 if (path.StartsWith ("@pem:")) {
261 var realPath = path.Substring (5);
262 if (Directory.Exists (realPath))
263 store.AddDirectoryLookup (realPath, MonoBtlsX509FileType.PEM);
265 } else if (path.StartsWith ("@der:")) {
266 var realPath = path.Substring (5);
267 if (Directory.Exists (realPath))
268 store.AddDirectoryLookup (realPath, MonoBtlsX509FileType.ASN1);
271 throw new NotSupportedException (string.Format ("Invalid item `{0}' in MonoTlsSettings.CertificateSearchPaths.", path));
277 static void SetupDefaultCertificateStore (MonoBtlsX509Store store)
280 store.SetDefaultPaths ();
281 store.AddAndroidLookup ();
283 AddUserStore (store);
284 AddMachineStore (store);
289 static void AddUserStore (MonoBtlsX509Store store)
291 var userPath = MonoBtlsX509StoreManager.GetStorePath (MonoBtlsX509StoreType.UserTrustedRoots);
292 if (Directory.Exists (userPath))
293 store.AddDirectoryLookup (userPath, MonoBtlsX509FileType.PEM);
296 static void AddMachineStore (MonoBtlsX509Store store)
298 var machinePath = MonoBtlsX509StoreManager.GetStorePath (MonoBtlsX509StoreType.MachineTrustedRoots);
299 if (Directory.Exists (machinePath))
300 store.AddDirectoryLookup (machinePath, MonoBtlsX509FileType.PEM);
304 static void AddTrustedRoots (MonoBtlsX509Store store, MonoTlsSettings settings, bool server)
306 if (settings?.TrustAnchors == null)
308 var trust = server ? MonoBtlsX509TrustKind.TRUST_CLIENT : MonoBtlsX509TrustKind.TRUST_SERVER;
309 store.AddCollection (settings.TrustAnchors, trust);
312 public static string GetSystemStoreLocation ()
315 return "/system/etc/security/cacerts";
317 return MonoBtlsX509StoreManager.GetStorePath (MonoBtlsX509StoreType.MachineTrustedRoots);
321 public static X509Certificate CreateCertificate (byte[] data, MonoBtlsX509Format format, bool disallowFallback = false)
323 using (var impl = new X509CertificateImplBtls (data, format, disallowFallback)) {
324 return new X509Certificate (impl);
328 public static X509Certificate2 CreateCertificate2 (byte[] data, MonoBtlsX509Format format, bool disallowFallback = false)
330 using (var impl = new X509CertificateImplBtls (data, format, disallowFallback)) {
331 return new X509Certificate2 (impl);
335 public static X509Certificate2 CreateCertificate2 (byte[] data, string password, bool disallowFallback = false)
337 using (var impl = new X509CertificateImplBtls (disallowFallback)) {
338 impl.Import (data, password, X509KeyStorageFlags.DefaultKeySet);
339 return new X509Certificate2 (impl);
343 public static X509Certificate CreateCertificate (MonoBtlsX509 x509)
345 using (var impl = new X509CertificateImplBtls (x509, true))
346 return new X509Certificate (impl);
349 public static X509Chain CreateChain ()
351 using (var impl = new X509ChainImplBtls ())
352 return new X509Chain (impl);
355 public static X509Chain GetManagedChain (MonoBtlsX509Chain chain)
357 var impl = new X509ChainImplBtls (chain);
358 return new X509Chain (impl);
361 public static MonoBtlsX509 GetBtlsCertificate (X509Certificate certificate)
363 var impl = certificate.Impl as X509CertificateImplBtls;
365 return impl.X509.Copy ();
367 return MonoBtlsX509.LoadFromData (certificate.GetRawCertData (), MonoBtlsX509Format.DER);
370 public static MonoBtlsX509Chain GetNativeChain (X509CertificateCollection certificates)
372 var chain = new MonoBtlsX509Chain ();
374 foreach (var cert in certificates) {
375 using (var x509 = GetBtlsCertificate (cert))
376 chain.AddCertificate (x509);