私たちがやろうとしていること
MQTT メッセージを暗号化しています。MQTTnet を使用しています。これは TLS 暗号化をサポートしているため、既存のソリューションでこの機能を有効にするかどうかは問題です。UWP Xamarin アプリケーションと .NET コンソール アプリケーションはどちらも、MQTT のすべてに同じ .NET 標準 DLL を使用します。ブローカーには Mosquitto を使用していましたが、MQTTnet サーバーを使用する単純なアプリケーションに切り替える予定です。
これが問題です
ブローカー側とコンソール アプリケーションの 1 つに TLS 暗号化を正常に追加しましたが、UWP Xamarin アプリケーションで問題が発生しています。現在の状態では、「提供されたクライアント証明書には、必要な秘密鍵情報がありません」というメッセージとともに例外がスローされています。
私たちが求めているもの
- これが可能であることの確認
- これが機能しない理由の基本的な理解 (つまり、仮定の確認またはその他)
- この問題の解決策
- 確認事項の考え方。
したがって、誰かが「答え」を知っていれば、素晴らしいことです。そうでなければ、アイデア、アドバイス、専門知識、経験の共有など。
私たちの仮定
- これが可能であると仮定しています
- これは MQTTnet のバグや制限ではないようです
- Xamarin UWP アプリケーションを使用しているときにこのエラーが発生する理由は、ディスク アクセスまたはレジストリ アクセスに対する何らかの制限が原因であると想定しています。
- PFX ファイルはコンソール アプリケーションで動作するため、Xamarin UWP アプリケーションでも動作するはずであると想定してきました (ただし、Xamarin UWP アプリケーションはユーザー キーでしかアクセスできないため、どのストアにあるのかを試してみました。お店)
試したこと
- MSDN で仕様書を読みました
- 私たちはブログ記事を読みました
- さまざまなスタック オーバーフローの投稿からのアドバイスを読み、それに従いました。
- さまざまな MQTTnet サポート投稿からのアドバイスを読み、それに従いました
- 2 つの異なるブローカー (Mosquitto と、MQTTnet サーバーを使用するサンプル アプリケーション) を試しました。
- OpenSSL を使用し、OS (Windows 10) を介して手動で証明書を作成しようとしました。より制御可能/決定論的/再現可能な結果を得るには、後者の PS スクリプトの使用に切り替えます
- ユーザーストアとマシンストアの証明書を作成しようとしました
- 証明書をストアにインポートしようとしました (コードを参照)
- X509KeyStorage フラグのさまざまな組み合わせを試しました (つまり、エクスポート可能、永続化、ユーザー キー セットなど)。
- Visual Studio を管理者として実行してみました
- SysInternals ProcMon を使用して、これがどこで失敗しているか (つまり、HD アクセスまたはレジストリ アクセス) を特定しようとしました。
- Xamarin アプリケーションのさまざまな機能を有効にしてみました
便利なリンク
- https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/working-with-certificates
- https://docs.microsoft.com/en-us/troubleshoot/iis/cannot-import-ssl-pfx-local-certificate
- https://github.com/chkr1011/MQTTnet/wiki/Server-and-client-method-documentation
- https://github.com/chkr1011/MQTTnet/issues/115
- https://github.com/chkr1011/MQTTnet/issues/124
- https://paulstovell.com/x509certificate2/
- MQTTnet クライアントがサーバー証明書に接続できない
- Windows で OpenSSL を使用せずに、pfx ファイルまたは証明書ストアから秘密鍵を抽出する
私たちの行動規範の一部
/// <summary>
/// Connect to the MQTT broker using the defined options
/// </summary>
private async Task ConnectAsync()
{
IMqttClientOptions options = CreateMqttClientOptions();
try
{
await m_mqttClient.ConnectAsync(options);
}
catch (Exception ex)
{
m_logger?.LogCritical(ex, "Failed to reconnect - service unavailable");
}
}
/// <summary>
/// Helper function used to create the MQTT client options object. This includes the certificate.
/// </summary>
private IMqttClientOptions CreateMqttClientOptions()
{
string filepath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "MobileTools.2.pfx");
X509Certificate2 certificate = new X509Certificate2(
filepath,
"notactuallymypassword",
X509KeyStorageFlags.Exportable | X509KeyStorageFlags.UserKeySet);
//InstallCertificate(certificate);
// Set-up options.
return new MqttClientOptionsBuilder()
.WithCleanSession(true)
.WithClientId(m_clientID)
.WithTcpServer("NotActuallyDnsName", m_configuration.Port)
.WithTls(new MqttClientOptionsBuilderTlsParameters
{
Certificates = new List<byte[]>
{
certificate.Export(X509ContentType.Cert)
},
CertificateValidationCallback = (X509Certificate xCertificate, X509Chain xChain, SslPolicyErrors sslPolicyErrors, IMqttClientOptions clientOptions) =>
{
return true;
},
UseTls = true
})
.Build();
}
/// <summary>
/// Helper function used to create the MQTT client options object. This includes the certificate.
/// </summary>
private void InstallCertificate(X509Certificate2 certificate)
{
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadWrite);
store.Add(certificate);
store.Close();
}
スタックトレース
The client certificate provided is missing the required private key information. ---> System.ArgumentException: The parameter is incorrect.
The client certificate provided is missing the required private key information.
at Windows.Networking.Sockets.StreamSocketControl.put_ClientCertificate(Certificate value)
at MQTTnet.Implementations.MqttTcpChannel.ConnectAsync(CancellationToken cancellationToken)
at MQTTnet.Internal.MqttTaskTimeout.WaitAsync(Func`2 action, TimeSpan timeout, CancellationToken cancellationToken)
at MQTTnet.Adapter.MqttChannelAdapter.ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at MQTTnet.Adapter.MqttChannelAdapter.WrapException(Exception exception)
at MQTTnet.Adapter.MqttChannelAdapter.ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
at MQTTnet.Client.MqttClient.ConnectAsync(IMqttClientOptions options, CancellationToken cancellationToken)
at MQTTnet.Client.MqttClient.ConnectAsync(IMqttClientOptions options, CancellationToken cancellationToken)
at TS.Orbit.MQTTLib.Client.MqttNetClient.ConnectAsync(IMQTTClientConfiguration configuration)