I'm using Unity and want to constantly receive mqtt-data that I can attach to a game object. So I'm trying to enable a connection like this:
async void testconnect()
{
const string server = "somewebadress.com";
const string clientId = "Test";
var caCert = new X509Certificate2(#"ca.crt");
var clientCert = new X509Certificate2(#"cert.pfx", "test");
var source = new CancellationTokenSource();
var token = source.Token;
var factory = new MqttFactory();
var mqttClient = factory.CreateMqttClient();
var mqttOptions = new MqttClientOptionsBuilder()
.WithTcpServer(server, 8883)
.WithClientId(clientId)
.WithTls(new MqttClientOptionsBuilderTlsParameters
{
UseTls = true,
AllowUntrustedCertificates = true,
Certificates = new List<X509Certificate> { caCert, clientCert }
})
.Build();
mqttClient.ConnectAsync(mqttOptions, token).Wait(token);
}
But I'm receiving the following exception:
NotImplementedException: The method or operation is not implemented.
Mono.Unity.UnityTlsContext.ProcessHandshake ()
I've tried to find it out by myself, but I have no clue how to implement the ProcessHandshake() function.
Related
I'm trying to establish mtls connection using https://github.com/chkr1011/MQTTnet library in my xamarin.android project. During this process I encountered with such an exception in Init method on calling conResult.Wait(TimeSpan.FromSeconds(60));
{MQTTnet.Exceptions.MqttCommunicationException: Authentication failed, see inner exception. ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception. ---> System.Security.Cryptography.CryptographicException: Caught…}
{System.Security.Cryptography.CryptographicException: Caught unhandled exception in MonoBtlsSslCtx.ProcessHandshake. ---> System.NullReferenceException: Object reference not set to an instance of an object.
at Mono.Btls.MonoBtlsSsl.SetPrivateKey (Mono.Btls.…}
This is my code:
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));
}
I have several options:
Maybe there is some issue with constructing certificate in GetCertificate method;
There is issue with MqttNet library itself. I suspect that the library does not work with certificates in xamarin.android, because i found such topics: https://github.com/xamarin/xamarin-android/issues/4481, https://github.com/chkr1011/MQTTnet/issues/883.
I tried to construct certificate this way, but xamarin does not support rsa.ImportRSAPrivateKey. I have got: System.PlatformNotSupportedException: Operation is not supported on this platform.
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));
I am attempting to upgrade to newest APNS connectivity functionality but when I submit a request I get a http 400 bad request result with no reason. What am I missing / doing wrong?
Method for sending below:
public async void SendAsync(string deviceToken, string p8privateKey, string p8privateKeyId, string teamId)
{
var path = $"/3/device/{deviceToken}";
var obj = new
{
aps = new
{
alert = "test00001"
}
};
var json = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
var request = new HttpRequestMessage(HttpMethod.Post, new Uri("https://api.development.push.apple.com:443" + path))
{
Version = new Version(2, 0)
};
string jwToken = CreateJwtToken(p8privateKey, p8privateKeyId, teamId);
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", jwToken);
request.Headers.TryAddWithoutValidation(":method", "POST");
request.Headers.TryAddWithoutValidation(":path", path);
request.Headers.Add("apns-topic", "com.the-app");
request.Content = new StringContent(json);
string sReq = JsonConvert.SerializeObject(request);
string jsonContent = request.Content.ReadAsStringAsync().Result;
WinHttpHandler handler = new WinHttpHandler();
HttpClient http = new HttpClient(handler);
var t = await http.SendAsync(request);
}
Here are the other used methods:
private static string CreateJwtToken(string p8privateKeyId, string p8privateKey, string teamId)
{
var header = JsonHelper.Serialize(new { alg = "ES256", kid = p8privateKeyId });
var payload = JsonHelper.Serialize(new { iss = teamId, iat = ToEpoch(DateTime.UtcNow) });
var key = CngKey.Import(Convert.FromBase64String(p8privateKey), CngKeyBlobFormat.Pkcs8PrivateBlob);
using (var dsa = new ECDsaCng(key))
{
dsa.HashAlgorithm = CngAlgorithm.Sha256;
var headerBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(header));
var payloadBasae64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(payload));
var unsignedJwtData = $"{headerBase64}.{payloadBasae64}";
var signature = dsa.SignData(Encoding.UTF8.GetBytes(unsignedJwtData));
return $"{unsignedJwtData}.{Convert.ToBase64String(signature)}";
}
}
private static int ToEpoch(DateTime time)
{
var span = DateTime.UtcNow - new DateTime(1970, 1, 1);
return Convert.ToInt32(span.TotalSeconds);
}
I have limited to the payload to what I think is the bear minimum requirements. I am new to jwt/http2 and APNS.
I could very well be missing something simple.
Hy I have written an Console Application and i want to connect to the Identity-Server. With the NamedPipeServerStream i want to get Result. However, it always tries to connect to the Server.
Here is my code:
var options = new OidcClientOptions
{
Authority = authority,
ClientId = clientId,
ClientSecret = clientSecret,
Scope = scope,
RedirectUri = redirectUrl,
ResponseMode = OidcClientOptions.AuthorizeResponseMode.Redirect
};
_oidcClient = new OidcClient(options);
var state = await _oidcClient.PrepareLoginAsync();
var callbackManager = new CallbackManager(state.State);
Process.Start(state.StartUrl);
var response = await callbackManager.RunServer();
var result = await _oidcClient.ProcessResponseAsync(response, state);
Here is my Callbackmanager
public async Task<string> RunServer(CancellationToken? token = null)
{
token = CancellationToken.None;
using (var server = new NamedPipeServerStream(_name, PipeDirection.In))
{
//here it spos and waits for the Connection
await server.WaitForConnectionAsync(token.Value);
using (var sr = new StreamReader(server))
{
var msg = await sr.ReadToEndAsync();
return msg;
}
}
}
the solution was to add an Flow:
Flow = OidcClientOptions.AuthenticationFlow.AuthorizationCode
now it works
Now I'm using C# MVC to implement an API, and the API data is from remote MongoDB. I found when I was testing my API, it had MongoDB connection timeout error appeared sometimes. Does anyone know how to solve this problem?
This is the connection code
var settings = new MongoClientSettings
{
Credentials = new[] { MongoCredential.CreateMongoCRCredential("database", "user", "pw") },
Server = new MongoServerAddress("XXX.XXX.XXX", XXXXX),
ConnectTimeout = TimeSpan.FromSeconds(1800),
MaxConnectionIdleTime = TimeSpan.FromSeconds(1800),
};
//Get a Reference to the Client Object
var mongoClient = new MongoClient(settings);
var mongoServer = mongoClient.GetServer();
var database = mongoServer.GetDatabase("MongoDB");
Now I changed my code as follow, but I still encounter the same problem
var settings = new MongoClientSettings
{
Credentials = new[] { MongoCredential.CreateMongoCRCredential("database", "user", "pw") },
Server = new MongoServerAddress("XXX.XXX.XXX.XXX", XXXXX),
ConnectTimeout = TimeSpan.FromSeconds(1800),
MaxConnectionIdleTime = TimeSpan.FromSeconds(1800)
};
var mongoClient = new MongoClient(settings);
var database = mongoClient.GetDatabase(ConfigurationManager.AppSettings["mongoDBdatabase"]);
return database;
and get the data by the following code:
var database = Mongo.TestConnectDB();
var collection = database.GetCollection<BsonDocument>("User");
var builder = Builders<BsonDocument>.Filter;
var filter = builder.Eq("_id", ObjectId.Parse(id));
var cursor = await collection.FindAsync(filter);
var friendList = new List<MongoModels.User.Circle.Friend>();
while (await cursor.MoveNextAsync())
{
var user = cursor.Current;
friendList=BsonSerializer.Deserialize<MongoModels.User>(user.First()).circle.friend;
}
I have a web application and web api services that authenticate through ADFS. They are contained in the same IIS application, and the web app makes calls back to the web api services without a problem.
I'm now trying to call the same services from a different application, but am having trouble passing the token. I am able to authenticate and retrieve a SAML token with the following code:
var stsEndpoint = "https://MyAdfsServer/adfs/services/trust/13/UsernameMixed";
var reliantPartyUri = "https://MyDomain/AppRoot/";
var factory = new Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannelFactory(
new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
new EndpointAddress(stsEndpoint));
factory.TrustVersion = System.ServiceModel.Security.TrustVersion.WSTrust13;
// Username and Password here...
factory.Credentials.UserName.UserName = #"Domain\UserName";
factory.Credentials.UserName.Password = "Password";
var rst = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
AppliesTo = new EndpointAddress(reliantPartyUri),
KeyType = KeyTypes.Bearer,
};
var channel = factory.CreateChannel();
var token = channel.Issue(rst) as GenericXmlSecurityToken;
var saml = token.TokenXml.OuterXml;
However, I'm not sure how to pass the saml in to the web api call. I've tried this:
using (var handler = new HttpClientHandler()
{
ClientCertificateOptions = ClientCertificateOption.Automatic,
AllowAutoRedirect = false
})
{
using (var client = new HttpClient(handler))
{
client.BaseAddress = new Uri("https://MyDomain/AppRoot/api/");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("SAML", saml);
HttpResponseMessage response = client.GetAsync("MyService/Get/").Result;
// Get the results...
var result = response.Content.ReadAsStringAsync().Result;
var status = response.StatusCode;
}
}
This is returning a status code of 302 and trying to redirect me to the ADFS server for authentication. Is there another way to pass the SAML token to the web api service?
(SET)
string samlString = "blah blah blah";
byte[] bytes = Encoding.UTF8.GetBytes(samlString);
string base64SamlString = Convert.ToBase64String(bytes);
myHttpClient.DefaultRequestHeaders.Add("X-My-Custom-Header", base64SamlString);
(GET)
IEnumerable<string> headerValues = request.Headers.GetValues("X-My-Custom-Header");
if (null != headerValues)
{
var encoding = Encoding.GetEncoding("iso-8859-1");
string samlToken = encoding.GetString(Convert.FromBase64String(headerValues.FirstOrDefault()));
}
When you want to access a resource which is protected via SSO (like ADFS, I assume), I found it easiest to use following approach: Show a WebBrowser element, let user enter credentials, then grab global cookies, and pass them into new HttpClient which performs the actual HTTP operation.
Here a complete code sample which downloads all build statuses from a SAML-protected Jenkins server:
private void Start()
{
var t = new Thread(ThreadProc);
t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();
}
public async void ThreadProc()
{
try
{
var urlBase = "https://JENKINS/";
var url = urlBase + "job/JOBNAME/api/json?depth=1&tree=lastBuild[timestamp],builds[number,result,timestamp,url,actions[lastBuiltRevision[SHA1,branch[name]],totalCount,failCount,skipCount],building,duration]";
var form = new Form();
var browser = new System.Windows.Forms.WebBrowser();
browser.SetBounds(0, 0, 400, 400);
form.Size = new System.Drawing.Size(400, 400);
form.Controls.AddRange(new Control[] { browser });
form.FormBorderStyle = FormBorderStyle.FixedDialog;
form.StartPosition = FormStartPosition.CenterScreen;
form.MinimizeBox = false;
form.MaximizeBox = false;
// Navigate to base URL. It should internally forward to login form. After logging in, close browser window.
browser.Navigate(urlBase);
form.ShowDialog();
var cookieString = GetGlobalCookies(urlBase);
var cookieContainer = new System.Net.CookieContainer();
using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
using (var client = new HttpClient(handler) { BaseAddress = new Uri(urlBase, UriKind.Absolute) })
{
cookieContainer.SetCookies(client.BaseAddress, cookieString);
var response = await client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
var responseStream = await response.Content.ReadAsStreamAsync();
using (var reader = new System.IO.StreamReader(responseStream))
{
var responseString = await reader.ReadToEndAsync();
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
[System.Runtime.InteropServices.DllImport("wininet.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)]
private static extern bool InternetGetCookieEx(string pchURL, string pchCookieName,
System.Text.StringBuilder pchCookieData, ref uint pcchCookieData, int dwFlags, IntPtr lpReserved);
private const int INTERNET_COOKIE_HTTPONLY = 0x00002000;
public string GetGlobalCookies(string uri)
{
uint uiDataSize = 2048;
var sbCookieData = new System.Text.StringBuilder((int)uiDataSize);
if (InternetGetCookieEx(uri, null, sbCookieData, ref uiDataSize,
INTERNET_COOKIE_HTTPONLY, IntPtr.Zero)
&&
sbCookieData.Length > 0)
{
return sbCookieData.ToString().Replace(";", ",");
}
return null;
}