xamarin.android プロジェクトでhttps://github.com/chkr1011/MQTTnetライブラリを使用して mtls 接続を確立しようとしています。このプロセス中に、conResult.Wait(TimeSpan.FromSeconds(60)); の呼び出し時に Init メソッドでこのような例外が発生しました。
{MQTTnet.Exceptions.MqttCommunicationException: 認証に失敗しました。内部例外を参照してください。---> System.Security.Authentication.AuthenticationException: 認証に失敗しました。内部例外を参照してください。---> System.Security.Cryptography.CryptographicException: Caught…} {System.Security.Cryptography.CryptographicException: で未処理の例外をキャッチしましたMonoBtlsSslCtx.ProcessHandshake
。---> System.NullReferenceException: オブジェクト参照がオブジェクトのインスタンスに設定されていません。Mono.Btls.MonoBtlsSsl.SetPrivateKey で (Mono.Btls.…}
これは私のコードです:
public void Init()
{
ILoadCertificate certificateService = DependencyService.Get<ILoadCertificate>();
var cert = certificateService.LoadPemCertificate("certificate", "private_key");
Console.WriteLine(cert.GetRSAPrivateKey());
string clientId = Guid.NewGuid().ToString();
string mqttURI = "";
int mqttPort = 8883;
var factory = new MqttFactory();
var mqttClient = factory.CreateMqttClient();
bool disableServerValidation = true;
var tlsParameters = new MqttClientOptionsBuilderTlsParameters
{
UseTls = true,
Certificates = new[] { new X509Certificate(cert.Export(X509ContentType.Cert)) },
IgnoreCertificateChainErrors = disableServerValidation,
AllowUntrustedCertificates = disableServerValidation,
SslProtocol = System.Security.Authentication.SslProtocols.Tls12,
CertificateValidationHandler = (o) =>
{
return true;
},
};
var connectOptions = new MqttClientOptionsBuilder()
.WithProtocolVersion(MQTTnet.Formatter.MqttProtocolVersion.V311)
.WithClientId(clientId)
.WithTcpServer(mqttURI, mqttPort)
.WithCommunicationTimeout(new TimeSpan(0, 2, 30))
.WithCleanSession()
.WithTls(tlsParameters)
.Build();
var conResult = mqttClient.ConnectAsync(connectOptions);
conResult.ContinueWith(r =>
{
Console.WriteLine(r.Result.ResultCode);
Console.WriteLine(r.Exception.StackTrace);
});
conResult.Wait(TimeSpan.FromSeconds(60));
var t = mqttClient.PublishAsync("events/test", "test");
t.ContinueWith(r =>
{
Console.WriteLine(r.Result.PacketIdentifier);
Console.WriteLine(r.Exception.StackTrace);
});
t.Wait();
}
//This methods is used to construct certificate:
public X509Certificate2 GetCertificate(string pemCert, string pemKey)
{
string fileNameCert = Path.Combine(Environment
.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), pemCert);
var pem = File.ReadAllText(fileNameCert);
string fileNameKey = Path.Combine(Environment
.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), pemKey);
var key = File.ReadAllText(fileNameKey);
var keyPair = (AsymmetricCipherKeyPair)new PemReader(new StringReader(key))
.ReadObject();
var cert = (Org.BouncyCastle.X509.X509Certificate)new PemReader(new
StringReader(pem)).ReadObject();
var builder = new Pkcs12StoreBuilder();
builder.SetUseDerEncoding(true);
var store = builder.Build();
var certEntry = new X509CertificateEntry(cert);
store.SetCertificateEntry("", certEntry);
store.SetKeyEntry("", new AsymmetricKeyEntry(keyPair.Private), new[] { certEntry });
byte[] data;
using (var ms = new MemoryStream())
{
store.Save(ms, Array.Empty<char>(), new SecureRandom());
data = ms.ToArray();
}
return new X509Certificate2(data);
}
public byte[] GetBytesFromPEM(string pemString, string type)
{
string header; string footer;
switch (type)
{
case "cert":
header = "-----BEGIN CERTIFICATE-----";
footer = "-----END CERTIFICATE-----";
break;
case "key":
header = "-----BEGIN RSA PRIVATE KEY-----";
footer = "-----END RSA PRIVATE KEY-----";
break;
default:
return null;
}
int start = pemString.IndexOf(header) + header.Length;
int end = pemString.IndexOf(footer, start) - start;
return Convert.FromBase64String(pemString.Substring(start, end));
}
いくつかのオプションがあります:
- GetCertificate メソッドでの証明書の作成に問題がある可能性があります。
- MqttNet ライブラリ自体に問題があります。次のようなトピックが見つかったため、ライブラリが xamarin.android の証明書で機能しないと思われます: https://github.com/xamarin/xamarin-android/issues/4481、https://github.com/chkr1011/MQTTnet /issues/883 .
この方法で証明書を作成しようとしましたが、xamarin は rsa.ImportRSAPrivateKey をサポートしていません。System.PlatformNotSupportedException: このプラットフォームでは操作がサポートされていません。
string fileNameCert = Path.Combine(Environment
.GetFolderPath(Environment
.SpecialFolder.LocalApplicationData), certificatePath);
using var publicKey = new X509Certificate2(fileNameCert);
string fileNameKey = Path.Combine(Environment
.GetFolderPath(Environment
.SpecialFolder.LocalApplicationData), privateKeyPath);
using var rsa = RSA.Create();
byte[] keyBuffer =
GetBytesFromPEM(File.ReadAllText(fileNameKey), "key");
int o;
rsa.ImportRSAPrivateKey(keyBuffer, out o);
var keyPair = publicKey.CopyWithPrivateKey(rsa);
return new X509Certificate2(keyPair.Export(X509ContentType.Pkcs12));