簡體   English   中英

訪問XamarinForms中的自簽名X509證書以實現與Mosquitto代理的mqtt TLS連接

[英]Access self signed X509certificates in XamarinForms for mqtt TLS connection to a Mosquitto broker

我希望使用自簽名的x509證書來實現TLS安全,以使用M2MqttDotnetCore客戶端連接到mosquitto mqtt代理的許多現有XamarinForms應用。

為此,我創建了一個簡單的示例XamarinForms pub / sub聊天應用程序,以了解如何保護XamarinForms mqtt客戶端應用程序的安全,該應用程序可以在此GitHub存儲庫中使用。 jhalbrecht / XamarinFormsMqttSample

我在Mosquitto_pub,python和.net控制台應用程序中有示例,這些示例實現了通過TLS和自簽名證書通過端口8883成功連接到mosquitto代理的目標。 XamarinForms UWP應用程序也可以在不安全的情況下工作。 我無法讓Android應用程序在端口8883上與TLS一起使用時 ,Android應用程序確實在端口1883上不安全地工作。這是Visual Studio 2017的運行時日志

[0:] M2Mqtt.Exceptions.MqttConnectionException: Exception connecting to the broker ---> System.AggregateException: One or more errors occurred. ---> System.Security.Authentication.AuthenticationException: A call to SSPI failed, see inner exception. ---> Mono.Btls.MonoBtlsException: Ssl error:1000007d:SSL routines:OPENSSL_internal:CERTIFICATE_VERIFY_FAILED
  at /Users/builder/jenkins/workspace/xamarin-android-d15-9/xamarin-android/external/mono/external/boringssl/ssl/handshake_client.c:1132
  at Mono.Btls.MonoBtlsContext.ProcessHandshake () [0x00038] in <fb6d78e506844b3b96d5b35aa047fbbd>:0 
  at Mono.Net.Security.MobileAuthenticatedStream.ProcessHandshake (Mono.Net.Security.AsyncOperationStatus status) [0x0003e] in <fb6d78e506844b3b96d5b35aa047fbbd>:0 
  at (wrapper remoting-invoke-with-check) Mono.Net.Security.MobileAuthenticatedStream.ProcessHandshake(Mono.Net.Security.AsyncOperationStatus)
  at Mono.Net.Security.AsyncHandshakeRequest.Run (Mono.Net.Security.AsyncOperationStatus status) [0x00006] in <fb6d78e506844b3b96d5b35aa047fbbd>:0 
  at Mono.Net.Security.AsyncProtocolRequest+<ProcessOperation>d__24.MoveNext () [0x000ff] in <fb6d78e506844b3b96d5b35aa047fbbd>:0 
--- End of stack trace from previous location where exception was thrown ---
  at Mono.Net.Security.AsyncProtocolRequest+<StartOperation>d__23.MoveNext () [0x0008b] in <fb6d78e506844b3b96d5b35aa047fbbd>:0 
   --- End of inner exception stack trace ---
  at Mono.Net.Security.MobileAuthenticatedStream+<ProcessAuthentication>d__47.MoveNext () [0x00254] in <fb6d78e506844b3b96d5b35aa047fbbd>:0 
   --- End of inner exception stack trace ---
  at System.Threading.Tasks.Task.ThrowIfExceptional (System.Boolean includeTaskCanceledExceptions) [0x00011] in <d4a23bbd2f544c30a48c44dd622ce09f>:0 
  at System.Threading.Tasks.Task.Wait (System.Int32 millisecondsTimeout, System.Threading.CancellationToken cancellationToken) [0x00043] in <d4a23bbd2f544c30a48c44dd622ce09f>:0 
  at System.Threading.Tasks.Task.Wait () [0x00000] in <d4a23bbd2f544c30a48c44dd622ce09f>:0 
  at M2Mqtt.Net.MqttNetworkChannel.Connect () [0x000a8] in <72fbe921f857483bafbb8b397ec98dd1>:0 
  at M2Mqtt.MqttClient.Connect (System.String clientId, System.String username, System.String password, System.Boolean willRetain, System.Byte willQosLevel, System.Boolean willFlag, System.String willTopic, System.String willMessage, System.Boolean cleanSession, System.UInt16 keepAlivePeriod) [0x0001e] in <72fbe921f857483bafbb8b397ec98dd1>:0 
   --- End of inner exception stack trace ---
  at M2Mqtt.MqttClient.Connect (System.String clientId, System.String username, System.String password, System.Boolean willRetain, System.Byte willQosLevel, System.Boolean willFlag, System.String willTopic, System.String willMessage, System.Boolean cleanSession, System.UInt16 keepAlivePeriod) [0x00037] in <72fbe921f857483bafbb8b397ec98dd1>:0 
  at M2Mqtt.MqttClient.Connect (System.String clientId) [0x00000] in <72fbe921f857483bafbb8b397ec98dd1>:0 
  at MqttDataServices.Services.MqttDataService+<Initialize>d__5.MoveNext () [0x00266] in C:\jstuff\MqttSample\MqttDataServices\Services\MqttDataService.cs:183 

我當前加載和訪問X509證書的方式並不安全,也不是最佳實踐。 有用。 我希望最終學習如何訪問每個移動平台的設備ca密鑰庫。 我使用跨平台插件FilePicker來加載證書,以base64對其進行編碼並保存。

 FileData fileData = await Plugin.FilePicker.CrossFilePicker.Current.PickFile();
 if (fileData == null)
 return; // user canceled file picking

 string fileName = fileData.FileName;
 string content = Convert.ToBase64String(fileData.DataArray, 0, fileData.DataArray.Length,
 Base64FormattingOptions.None);

 string deviceFileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), fileName);
 File.WriteAllText(deviceFileName, content);      

我已經通過Twitter與Xamarin的一些人取得了聯系。 我在上述存儲庫中有一個未解決的問題,討論的問題是Microsoft的@baulig相信給了我答案,但是我目前不知道如何實現它。

我只是看了證書驗證代碼,它的本質是

var certStore = KeyStore.GetInstance ("AndroidCAStore"); certStore.Load(null);

這是入口點: https : //github.com/mono/mono/blob/master/mcs/class/System/Mono.Btls/MonoBtlsX509LookupAndroid.cs ,它將此代碼稱為https://github.com/mono/ mono / blob / master / mcs / class / System / System / AndroidPlatform.cs#L101 ,然后在此處調用xamarin-android代碼: https : //github.com/xamarin/xamarin-android/blob/master/src/Mono .Android / Android.Runtime / AndroidEnvironment.cs

KeyStore應該是此類: https : //developer.xamarin.com/api/type/Java.Security.KeyStore/

因此,您應該能夠通過Java.Security.KeyStore做到這Java.Security.KeyStore

  • 在AndroidManifest.xml中需要授予什么權限?
  • 我可以研究什么術語來正確訪問平台的密鑰庫?

初始過帳后的添加

  • 2019年2月27日(MST)2:51 PM
    MqttDataService.cs添加了證書和mqtt客戶端創建
 X509Certificate caCert = X509Certificate.CreateFromCertFile(Path.Combine(filesDirectoryBasePath, "ca.crt"));
 string thePfxPathOnDevice = Path.Combine(filesDirectoryBasePath, "xamarinclient.pfx");
 string theBase64EncodedPfx = File.ReadAllText(thePfxPathOnDevice);

 byte[] certificate = Convert.FromBase64String(theBase64EncodedPfx);
 X509Certificate2 clientCert = new X509Certificate2(certificate, "xamarin");
 _client = new MqttClient(
     GetHostName(_xpdSetting.MqttBrokerAddress),
     Int32.Parse(_xpdSetting.MqttBrokerTlsPort),
     _xpdSetting.UseTls,
     caCert,
     clientCert,
     MqttSslProtocols.TLSv1_2
     //MyRemoteCertificateValidationCallback
     );

由於您使用的是.Net / Mono Socket (通過M2MqttDotnetCore),因此只需使用證書固定即可,而只需處理RemoteCertificateValidationCallback 因此,不會干擾Android的受信任商店等。

Android上的SslStream使用情況:

注意:Android上的SslStream存在問題 ,對象分配可能會發瘋……我相信(?)對此有未解決的問題。 (我不得不多次使用Java的SSLSocket來解決此問題)

啟用本機TLS 1.2+

在此處輸入圖片說明

  • 通過Android項目構建選項使用BoringSSL

將證書添加到Android的Asset目錄:

├── Assets
│   └── sushihangover.cert
  • 這是您的cert / .pem文件( 不是您的密鑰!

  • 確保這是一個沒有Unicode BOM表頭的ascii文件

  • 通過openssl示例(只需將其更改為主機和安全端口)

     echo -n | openssl s_client -connect 10.1.10.250:5001 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' 

RemoteCertificateValidationCallback實現

注意 :以下代碼可以在NetStd2.0或Xamarin.Android中使用

X509Certificate sushihangoverCert; // Class level var

bool CertificateValidation(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors certificateErrors)
{
    if (sushihangoverCert == null)
    {
        // There is no non-async version of OpenAppPackageFileAsync (via Xamarin.Essential) 😡 Why!!!
        using (var waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset))
        {
            Task.Run(async () =>
            {
                using (var assetStream = await Xamarin.Essentials.FileSystem.OpenAppPackageFileAsync("sushihangover.cert"))
                using (var memStream = new MemoryStream())
                {
                    assetStream.CopyTo(memStream);
                    sushihangoverCert = new X509Certificate(memStream.ToArray());
                    waitHandle.Set();
                }
            });
            waitHandle.WaitOne();
        }
    }
    return sushihangoverCert.Equals(certificate) ? true : false;
}

SSLStream用法示例:

注意:這是使用自簽名證書連接到NetCore Web API端口的

using (var tcpClient = new TcpClient("10.1.10.250", 5001))
using (var ssl = new SslStream(tcpClient.GetStream(), false, new RemoteCertificateValidationCallback(CertificateValidation)))
{
    ssl.AuthenticateAsClient("10.1.10.250", null, System.Security.Authentication.SslProtocols.Tls12, false);
    if (ssl.CanWrite)
    {
        var send = Encoding.ASCII.GetBytes("GET /api/item HTTP/1.1\r\nhost: 10.1.10.250\r\n\r\n");
        await ssl.WriteAsync(send, 0, send.Length);
        var buffer = new byte[4096];
        var count = await ssl.ReadAsync(buffer, 0, buffer.Length);
        Console.WriteLine(Encoding.UTF8.GetString(buffer, 0, count));
    }
    else
        throw new SocketException();
}

服務器證書不匹配錯誤:

如果您的服務器證書(是否為自簽名)與您要固定的證書不匹配,您將收到:

{Mono.Btls.MonoBtlsException: Ssl error:1000007d:SSL routines:OPENSSL_internal:CERTIFICATE_VERIFY_FAILED

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM