I'm new to the vsphere api and I'm trying to change the network settings of a virtual machine from dynamic ip to static ip but I can't find the setting.
Here's the code I have so far, it connects to vsphere, finds the virtual machine, and changes the name of the VM.
I assume there is a setting in the VirtualMachineConfigSpec that will also change the network settings, but I can't find it.
VimClient vimClient = new VimClient();
ServiceContent serviceContent = vimClient.Connect("https://[MY ADDRESS]/sdk");
UserSession us = vimClient.Login("[USERNAME]","[PASSWORD]");
ManagedObjectReference _svcRef = new ManagedObjectReference();
_svcRef.Type = "ServiceInstance";
_svcRef.Value = "ServiceInstance";
NameValueCollection filterForVM = new NameValueCollection();
filterForVM.Add("Name","[VIRTUAL MACHINE NAME]");
VirtualMachine vm = (VirtualMachine)vimClient.FindEntityView(typeof(VirtualMachine),null,filterForVM,null);
VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec();
vmConfigSpec.Name = "[NEW NAME]"; // change the VM name
vmConfigSpec.???? // how to set the ip address
vm.ReconfigVM_Task(vmConfigSpec);
vimClient.Disconnect();
VMware API don’t have a setting to set IP address on virtual machine guest OS, because the IP address setting depends on version guest OS. You could use two ways to do it:
1) You could use GuestOperationsManager from VMware vSphere API to launch the script of IP address setting on guest OS.
Prerequisites:
You should write scripts of IP address setting for each supported OS (Linux, Windows, etc.)
VMware Tools must be installed on each supported VM (for use GuestOperationsManager).
Update2. The following simplified example of running a script on the guest OS. This example does not include error handling, getting logs of the script, VM power on, etc.
using System;
using System.IO;
using System.Net;
using Vim25Api;
namespace RunScriptOnGuestOsTest
{
class Program
{
static void Main(string[] args)
{
var program = new Program();
program.RunScriptInGuestOs(
"https://10.1.1.10/sdk",
"root",
"vmware",
"c:\\temp\\test.bat",
"vm-73",
"Administrator",
"P#ssword",
"c:\\test.bat",
String.Empty);
}
public void RunScriptInGuestOs(string vCenterUrl, string vCenterUserName, string vCenterPassword, string scriptFilePatch, string vmKey, string username, string password, string destinationFilePath, string arguments)
{
var service = CreateVimService(vCenterUrl, 600000, true);
var serviceContent = RetrieveServiceContent(service);
service.Login(serviceContent.sessionManager, vCenterUserName, vCenterPassword, null);
byte[] dataFile;
using (var fileStream = new FileStream(scriptFilePatch, FileMode.Open, FileAccess.Read))
{
dataFile = new byte[fileStream.Length];
fileStream.Read(dataFile, 0, dataFile.Length);
}
FileTransferToGuest(service, vmKey, username, password, destinationFilePath, dataFile);
RunProgramInGuest(service, vmKey, username, password, destinationFilePath, arguments);
}
private static VimService CreateVimService(string url, int serviceTimeout, bool trustAllCertificates)
{
ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;
return new VimService
{
Url = url,
Timeout = serviceTimeout,
CookieContainer = new CookieContainer()
};
}
private ServiceContent RetrieveServiceContent(VimService service)
{
var serviceInstance = new ManagedObjectReference
{
type = "ServiceInstance",
Value = "ServiceInstance"
};
var content = service.RetrieveServiceContent(serviceInstance);
if (content.sessionManager == null)
{
throw new ApplicationException("Session manager is null.");
}
return content;
}
private void FileTransferToGuest(VimService service, string vmKey, string username, string password, string fileName, byte[] fileData)
{
var auth = new NamePasswordAuthentication { username = username, password = password, interactiveSession = false };
var vmRef = new ManagedObjectReference { type = "VirtualMachine", Value = vmKey };
var fileMgr = new ManagedObjectReference { type = "GuestFileManager", Value = "guestOperationsFileManager" };
var posixFileAttributes = new GuestPosixFileAttributes();
posixFileAttributes.ownerId = 1;
posixFileAttributes.groupId = 1;
posixFileAttributes.permissions = (long)0777; //execution file
var requestUrl = service.InitiateFileTransferToGuest(fileMgr, vmRef, auth, fileName, posixFileAttributes, fileData.Length, true);
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(requestUrl);
request.ContentType = "application/octet-stream";
request.Method = "PUT";
request.ContentLength = fileData.Length;
Stream requestStream = request.GetRequestStream();
requestStream.Write(fileData, 0, fileData.Length);
requestStream.Close();
request.GetResponse();
}
private void RunProgramInGuest(VimService service, string vmKey, string username, string password, string programPath, string arguments)
{
var auth = new NamePasswordAuthentication { username = username, password = password, interactiveSession = false };
var vmRef = new ManagedObjectReference { type = "VirtualMachine", Value = vmKey };
var progSpec = new GuestProgramSpec { programPath = programPath, arguments = arguments };
var processMgr = new ManagedObjectReference { type = "GuestProcessManager", Value = "guestOperationsProcessManager" };
var result = service.StartProgramInGuest(processMgr, vmRef, auth, progSpec);
}
}
}
2) You could use DHCP server to manage distributing IP addresses. Using VMware API, you can get MAC address of virtual machine. Next you should set up DHCP server for distributing desired IP addresses on obtained MAC addresses.
Prerequisites:
Each VM must be set up to obtain IP address from DHCP server.
DHCP server which can be configured to fit your needs.
Related
I am working on an asp.net web application where I have to create users directly in Tableau Server using Rest API and C#. The users are local users and not AD or SAML users.
I have coded the part however, I am getting Unauthorised as the result. Below is my code.
public static string site_id { get; set; }
public static string token { get; set; }
static void Main(string[] args)
{
CallWebAPIAsync().Wait();
}
static async Task CallWebAPIAsync()
{
using (var client = new HttpClient())
{
client.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite);
client.BaseAddress = new Uri("https://serverurl.com");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/octet-stream"));
SignIn(client);
Users(client, site_id, token);
CreateUser(client, site_id, token);
}
Console.Read();
}
public class CustomXmlMediaTypeFormatter : XmlMediaTypeFormatter
{
public CustomXmlMediaTypeFormatter()
{
UseXmlSerializer = true;
WriterSettings.OmitXmlDeclaration = false;
}
}
public static tableauCredentialsType SignIn(HttpClient client)
{
var name = "Administrator";
var password = "Password";
var site = string.Empty;
var tableauCredentialsType = new tsRequest()
{
Item = new tableauCredentialsType
{
name = name,
password = password,
site = new siteType() { contentUrl = site }
}
};
var httpContent = new ObjectContent<tsRequest>(tableauCredentialsType, new CustomXmlMediaTypeFormatter());
var httpResponseMessage = client.PostAsync("/api/3.2/auth/signin", httpContent).Result;
if (httpResponseMessage.StatusCode != System.Net.HttpStatusCode.OK)
throw new Exception(string.Format("Unable to login to the server", site, name));
var responseLogin = httpResponseMessage.Content.ReadAsAsync<tsResponse>(new List<MediaTypeFormatter>() { new CustomXmlMediaTypeFormatter() }).Result;
var resultTableauCredentialsType = ((tableauCredentialsType)responseLogin.Items[0]);
site_id = resultTableauCredentialsType.site.id;
token = resultTableauCredentialsType.token;
return resultTableauCredentialsType;
}
public static userType CreateUser(HttpClient client, string siteid, string auth_token)
{
var name = "user#domain.com";
var userType = new tsRequest()
{
Item = new userType
{
name = name,
siteRole = siteRoleType.ExplorerCanPublish,
authSetting = siteUserAuthSettingType.ServerDefault
}
};
var httpContent = new ObjectContent<tsRequest>(userType, new CustomXmlMediaTypeFormatter());
client.DefaultRequestHeaders.Add("x-tableau-auth", auth_token);
var uri = string.Format("/api/3.2/sites/{0}/users", siteid);
var httpResponseMessage = client.PostAsync(uri, httpContent).Result;
if (httpResponseMessage.StatusCode != System.Net.HttpStatusCode.OK)
throw new Exception(string.Format("Unable to create user in the server", siteid, name));
var responseLogin = httpResponseMessage.Content.ReadAsAsync<tsResponse>(new List<MediaTypeFormatter>() { new CustomXmlMediaTypeFormatter() }).Result;
var resultTableauCredentialsType = ((userType)responseLogin.Items[0]);
return resultTableauCredentialsType;
}
I was able to successfully login into the server and was also able to provide the x-tableau-auth to my client headers.
The code is working fine except the post sync method in Create user method. Once the code executes, it throws UnAuthorized error as the response from the server. My user is the administrator in Tableau Server and when I query the workbooks and users in Tableau Server with the same x-tableau-auth, it's working fine.
I am not good in API programming and need some help in solving out this issue. Any help is highly appreciated.
I'm using the below code for sharepoint authentication. It works perfectly fine on a local machine, but when I deploy this package on server it is giving 401 error.
class Program
{
static void Main(string[] args)
{
//Delcaring Required variable (Actual variable values have been changed to dummy one)
var webUri = new Uri("https://blahblah.sharepoint.com");
string proxy = "http://abc.abc.com:1234/";
const string userName = "abc#blahblah.com";
const string password = "123";
string SalesPOV_GUID = "6319bd1f-7f-4ffd-95dc-a992afc4da10";
string Clients_GUID = "ojfoeiawe-3abcc6-41cd-be23-cf6043671d53";
//Creating a secured sring for SharepointOnline Credentials
var securePassword = new SecureString();
foreach (var c in password)
{
securePassword.AppendChar(c);
}
//Setting up credentials for Sharepoint
var credentials = new SharePointOnlineCredentials(userName, securePassword);
//Makiing a Call
using (var client = new WebClient())
{
try
{
//setting up proxy
System.Net.WebProxy wp = new System.Net.WebProxy();
Uri newUri = new Uri(proxy);
wp.Address = newUri;
client.Proxy = wp;
}
catch (WebException e) //In package we need to fail the package on catching exception.
{
string pageContent = new StreamReader(e.Response.GetResponseStream()).ReadToEnd().ToString();
}
client.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
client.Credentials = credentials;
}
}
}
Check if your sharepoint website is set to "pass-through" authentication for the physical path.
I need to remove a saved wifi profilefrom code, so that the SoftAP is enabled again. According to the ms docs, there is no way to remove a profile, only disconnect. Is this not possible?
Ms docs for wifi
https://learn.microsoft.com/en-us/uwp/api/windows.devices.wifi.wifiadapter
Device Portal API
https://learn.microsoft.com/de-ch/windows/mixed-reality/device-portal-api-reference#wifi-management
Here is my working code for disconnecting from a wifi using device portal API
// API creds
string username = "Administrator";
string password = "p#ssw0rd
// API request URIs
string apiUri = "http://192.168.1.15:8080/api/wifi/network";
// WiFi details
string wifiInterface = string.Empty;
string wifiProfile = string.Empty;
// WiFi access
WiFiAccessStatus wifiAccess = await WiFiAdapter.RequestAccessAsync();
if (wifiAccess == WiFiAccessStatus.Allowed)
{
// Get WiFi adapter
IReadOnlyList<WiFiAdapter> wifiAdapterResult = await WiFiAdapter.FindAllAdaptersAsync();
WiFiAdapter wifiAdapter = wifiAdapterResult[0];
// Get conn profile / details
ConnectionProfile profile = await wifiAdapter.NetworkAdapter.GetConnectedProfileAsync();
wifiInterface = profile.NetworkAdapter.NetworkAdapterId.ToString();
wifiProfile = profile.ProfileName;
}
// API creds
PasswordCredential credentials = new PasswordCredential("login", username, password);
// HttpClient filter
HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter();
filter.CookieUsageBehavior = HttpCookieUsageBehavior.NoCookies;
filter.CacheControl.ReadBehavior = HttpCacheReadBehavior.MostRecent;
filter.CacheControl.WriteBehavior = HttpCacheWriteBehavior.NoCache;
filter.ServerCredential = credentials;
// HttpClient
HttpClient client = new HttpClient(filter);
apiUri = apiUri + "?interface=" + wifiInterface + "&op=disconnect" + "&createprofile=no";
// Request
HttpRequestMessage request = new HttpRequestMessage();
request.Method = new HttpMethod("POST");
request.RequestUri = new Uri(apiUri);
// Send request
try
{
// Response
HttpResponseMessage response = await client.SendRequestAsync(request);
// Again
if (response.Content.ToString().Contains("Authorization Required"))
{
response = await client.SendRequestAsync(request);
}
}
catch
{
// Dispose
client.Dispose();
filter.Dispose();
}
But for deleting a wifi profile, i get 404 not found back from the API. According to the API docs linked above, the request should be ok. Here is my code for deleting a wifi profile
// API creds
string username = "Administrator";
string password = "p#ssw0rd
// API request URIs
string apiUri = "http://192.168.1.15:8080/api/wifi/network";
// WiFi details
string wifiInterface = string.Empty;
string wifiProfile = string.Empty;
// WiFi access
WiFiAccessStatus wifiAccess = await WiFiAdapter.RequestAccessAsync();
if (wifiAccess == WiFiAccessStatus.Allowed)
{
// Get WiFi adapter
IReadOnlyList<WiFiAdapter> wifiAdapterResult = await WiFiAdapter.FindAllAdaptersAsync();
WiFiAdapter wifiAdapter = wifiAdapterResult[0];
// Get conn profile / details
ConnectionProfile profile = await wifiAdapter.NetworkAdapter.GetConnectedProfileAsync();
wifiInterface = profile.NetworkAdapter.NetworkAdapterId.ToString();
wifiProfile = profile.ProfileName;
}
// API creds
PasswordCredential credentials = new PasswordCredential("login", username, password);
// HttpClient filter
HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter();
filter.CookieUsageBehavior = HttpCookieUsageBehavior.NoCookies;
filter.CacheControl.ReadBehavior = HttpCacheReadBehavior.MostRecent;
filter.CacheControl.WriteBehavior = HttpCacheWriteBehavior.NoCache;
filter.ServerCredential = credentials;
// HttpClient
HttpClient client = new HttpClient(filter);
apiUri = apiUri + "?interface=" + wifiInterface + "&profile=" + wifiProfile;
// Request
HttpRequestMessage request = new HttpRequestMessage();
request.Method = new HttpMethod("DELETE")
request.RequestUri = new Uri(apiUri);
// Send request
try
{
// Response
HttpResponseMessage response = await client.SendRequestAsync(request);
// Again
if (response.Content.ToString().Contains("Authorization Required"))
{
response = await client.SendRequestAsync(request);
}
}
catch
{
// Dispose
client.Dispose();
filter.Dispose();
}
Edit//
To close this problem, since build 17763, there is a new method for deleting WiFi profiles directly from code available
bool canDelete = wifiProfile.CanDelete;
if (canDelete)
{
ConnectionProfileDeleteStatus deleteStatus = await wifiProfile.TryDeleteAsync();
}
You may be able to call netsh from your program.
netsh wlan delete <profile name> should get you there.
After trying for hours, finally found a solution! For those who are interested, you have to call the "run command" API, which allows you to run certain windows commands
string deleteCommand = "netsh wlan delete profile name=*";
string cmdApi = string.Format("http://192.168.1.15:8080/api/iot/processmanagement/runcommand?command={0}&runasdefaultaccount={1}", GetBase64String(deleteCommand), GetBase64String("no"));
The really important thing to note here is that you have to encode the command as a base64 string, otherwise it won't work!
private string GetBase64String(string stringToConvert)
{
return Convert.ToBase64String(Encoding.UTF8.GetBytes(stringToConvert));
}
With this code, it is finally possible for me to delete either certain wifi profiles, or in the example case above, every saved profile.
Thanks a lot Andy for finding this. I was doing it with command line before but this does work. I added some supporting code around it to help others where I had some issues such as get, delete or post. If it works then I restart IoT to be back in Onboarding mode. Maybe someone will find this helpful.
Logging into the portal as admin may or may not be required but that is how I do it. The if (interfaceGUID != null) was assigned by a previous Api request and can be removed for testing.
private string password = "yourpassword";
private string localhost = "127.0.0.1";
private async Task DeleteProfile()
{
try
{
using (HttpClient client = new HttpClient())
{
if (interfaceGUID != null)
{
string deleteCommand = "netsh wlan delete profile name=*";
using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, string.Format("http://{0}:8080/api/iot/processmanagement/runcommand?command={1}&runasdefaultaccount={2}", localhost, Convert.ToBase64String(Encoding.UTF8.GetBytes(deleteCommand)), Convert.ToBase64String(Encoding.UTF8.GetBytes("no")))))
{
request.Headers.Authorization = CreateBasicCredentials("Administrator");
using (HttpResponseMessage response = await client.SendAsync(request))
{
if (response.IsSuccessStatusCode == true)
{
ShutdownManager.BeginShutdown(Windows.System.ShutdownKind.Restart, TimeSpan.FromSeconds(1));
}
else
{
Debug.WriteLine("Could not delete the profiles. " + response.ReasonPhrase.ToString());
}
}
}
}
client.Dispose();
}
}
catch (Exception ex)
{
Debug.WriteLine("Could not delete the profiles. " + ex.InnerException.ToString());
}
}
private AuthenticationHeaderValue CreateBasicCredentials(string userName)
{
string toEncode = userName + ":" + password;
Encoding encoding = Encoding.GetEncoding("iso-8859-1");
byte[] toBase64 = encoding.GetBytes(toEncode);
string parameter = Convert.ToBase64String(toBase64);
return new AuthenticationHeaderValue("Basic", parameter);
}
Been working on Windows device portal API recently and came across this post. The reason your code got 404 response is because in the API URI, the &profile= expects a Base64 value instead of a text string that you're using. Once you encode the profile name to Base64, it should work.
I believe this isn't explicitly stated in MS's device portal documentation, as I only discovered this by using web browser debugger to inspect Windows Device Portal web page when deleting a WIFI profile.
To close this problem, since build 17763, there is a new method for deleting WiFi profiles directly from code available
bool canDelete = wifiProfile.CanDelete;
if (canDelete)
{
ConnectionProfileDeleteStatus deleteStatus = await wifiProfile.TryDeleteAsync();
}
For an app with some kind of chat based features I want to add push notification support for receiving new messages.
What I want to do is use the new token based authentication (.p8 file) from Apple, but I can't find much info about the server part.
I came across the following post:
How to use APNs Auth Key (.p8 file) in C#?
However the answer was not satisfying as there was not much detail about how to:
establish a connection with APNs
use the p8 file (except for some kind of encoding)
send data to the Apple Push Notification Service
You can't really do this on raw .NET Framework at the moment. The new JWT-based APNS server uses HTTP/2 only, which .NET Framework does not yet support.
.NET Core's version of System.Net.Http, however, does, provided you meet the following prerequisites:
On Windows, you must be running Windows 10 Anniversary Edition (v1607) or higher, or the equivalent build of Windows Server 2016 (I think).
On Linux, you must have a version of libcurl that supports HTTP/2.
On macOS, you have to compile libcurl with support for HTTP/2, then use the DYLD_INSERT_LIBRARIES environment variable in order to load your custom build of libcurl.
You should be able to use .NET Core's version of System.Net.Http in the .NET Framework if you really want.
I have no idea what happens on Mono, Xamarin or UWP.
There are then three things you have to do:
Parse the private key that you have been given. This is currently an ECDSA key, and you can load this into a System.Security.Cryptography.ECDsa object.
On Windows, you can use the CNG APIs. After parsing the base64-encoded DER part of the key file, you can then create a key with new ECDsaCng(CngKey.Import(data, CngKeyBlobFormat.Pkcs8PrivateBlob)).
On macOS or Linux there is no supported API and you have to parse the DER structure yourself, or use a third-party library.
Create a JSON Web Token / Bearer Token. If you use the System.IdentityModel.Tokens.Jwt package from NuGet, this is fairly simple. You will need the Key ID and Team ID from Apple.
public static string CreateToken(ECDsa key, string keyID, string teamID)
{
var securityKey = new ECDsaSecurityKey(key) { KeyId = keyID };
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.EcdsaSha256);
var descriptor = new SecurityTokenDescriptor
{
IssuedAt = DateTime.Now,
Issuer = teamID,
SigningCredentials = credentials
};
var handler = new JwtSecurityTokenHandler();
var encodedToken = handler.CreateEncodedJwt(descriptor);
return encodedToken;
}
Send an HTTP/2 request. This is as normal, but you need to do two extra things:
Set yourRequestMessage.Version to new Version(2, 0) in order to make the request using HTTP/2.
Set yourRequestMessage.Headers.Authorization to new AuthenticationHeaderValue("bearer", token) in order to provide the bearer authentication token / JWT with your request.
Then just put your JSON into the HTTP request and POST it to the correct URL.
Because Token (.p8) APNs only works in HTTP/2, thus most of the solutions only work in .net Core. Since my project is using .net Framework, some tweak is needed. If you're using .net Framework like me, please read on.
I search here and there and encountered several issues, which I managed to fix and pieced them together.
Below is the APNs class that actually works. I created a new class library for it, and placed the .P8 files within the AuthKeys folder of the class library. REMEMBER to right click on the .P8 files and set it to "Always Copy". Refer Get relative file path in a class library project that is being referenced by a web project.
After that, to get the location of the P8 files, please use AppDomain.CurrentDomain.RelativeSearchPath for web project or AppDomain.CurrentDomain.BaseDirectory for win application. Refer Why AppDomain.CurrentDomain.BaseDirectory not contains "bin" in asp.net app?
To get the token from the P8, you'll need to use the BouncyCastle class, please download it from Nuget.
using Jose;
using Newtonsoft.Json;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Security.Cryptography;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace PushLibrary
{
public class ApplePushNotificationPush
{
//private const string WEB_ADDRESS = "https://api.sandbox.push.apple.com:443/3/device/{0}";
private const string WEB_ADDRESS = "https://api.push.apple.com:443/3/device/{0}";
private string P8_PATH = AppDomain.CurrentDomain.RelativeSearchPath + #"\AuthKeys\APNs_AuthKey.p8";
public ApplePushNotificationPush()
{
}
public async Task<bool> SendNotification(string deviceToken, string title, string content, int badge = 0, List<Tuple<string, string>> parameters = null)
{
bool success = true;
try
{
string data = System.IO.File.ReadAllText(P8_PATH);
List<string> list = data.Split('\n').ToList();
parameters = parameters ?? new List<Tuple<string, string>>();
string prk = list.Where((s, i) => i != 0 && i != list.Count - 1).Aggregate((agg, s) => agg + s);
ECDsaCng key = new ECDsaCng(CngKey.Import(Convert.FromBase64String(prk), CngKeyBlobFormat.Pkcs8PrivateBlob));
string token = GetProviderToken();
string url = string.Format(WEB_ADDRESS, deviceToken);
HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, url);
httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
httpRequestMessage.Headers.TryAddWithoutValidation("apns-push-type", "alert"); // or background
httpRequestMessage.Headers.TryAddWithoutValidation("apns-id", Guid.NewGuid().ToString("D"));
//Expiry
//
httpRequestMessage.Headers.TryAddWithoutValidation("apns-expiration", Convert.ToString(0));
//Send imediately
httpRequestMessage.Headers.TryAddWithoutValidation("apns-priority", Convert.ToString(10));
//App Bundle
httpRequestMessage.Headers.TryAddWithoutValidation("apns-topic", "com.xxx.yyy");
//Category
httpRequestMessage.Headers.TryAddWithoutValidation("apns-collapse-id", "test");
//
var body = JsonConvert.SerializeObject(new
{
aps = new
{
alert = new
{
title = title,
body = content,
time = DateTime.Now.ToString()
},
badge = 1,
sound = "default"
},
acme2 = new string[] { "bang", "whiz" }
});
httpRequestMessage.Version = new Version(2, 0);
using (var stringContent = new StringContent(body, Encoding.UTF8, "application/json"))
{
//Set Body
httpRequestMessage.Content = stringContent;
Http2Handler.Http2CustomHandler handler = new Http2Handler.Http2CustomHandler();
handler.SslProtocols = System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls;
//handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true;
//Continue
using (HttpClient client = new HttpClient(handler))
{
HttpResponseMessage resp = await client.SendAsync(httpRequestMessage).ContinueWith(responseTask =>
{
return responseTask.Result;
});
if (resp != null)
{
string apnsResponseString = await resp.Content.ReadAsStringAsync();
handler.Dispose();
}
handler.Dispose();
}
}
}
catch (Exception ex)
{
success = false;
}
return success;
}
private string GetProviderToken()
{
double epochNow = (int)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
Dictionary<string, object> payload = new Dictionary<string, object>()
{
{ "iss", "YOUR APPLE TEAM ID" },
{ "iat", epochNow }
};
var extraHeaders = new Dictionary<string, object>()
{
{ "kid", "YOUR AUTH KEY ID" },
{ "alg", "ES256" }
};
CngKey privateKey = GetPrivateKey();
return JWT.Encode(payload, privateKey, JwsAlgorithm.ES256, extraHeaders);
}
private CngKey GetPrivateKey()
{
using (var reader = File.OpenText(P8_PATH))
{
ECPrivateKeyParameters ecPrivateKeyParameters = (ECPrivateKeyParameters)new PemReader(reader).ReadObject();
var x = ecPrivateKeyParameters.Parameters.G.AffineXCoord.GetEncoded();
var y = ecPrivateKeyParameters.Parameters.G.AffineYCoord.GetEncoded();
var d = ecPrivateKeyParameters.D.ToByteArrayUnsigned();
return EccKey.New(x, y, d);
}
}
}
}
Secondly, if you noticed, I am using the custom WinHTTPHandler to make the code to support HTTP/2 based on How to make the .net HttpClient use http 2.0?. I am creating this using another class library, remember to download WinHTTPHandler from Nuget.
public class Http2CustomHandler : WinHttpHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
request.Version = new Version("2.0");
return base.SendAsync(request, cancellationToken);
}
}
After that, just call the "SendNotification" on the ApplePushNotificationPush class and you should get the message on your iPhone.
private string GetToken()
{
var dsa = GetECDsa();
return CreateJwt(dsa, "keyId", "teamId");
}
private ECDsa GetECDsa()
{
using (TextReader reader = System.IO.File.OpenText("AuthKey_xxxxxxx.p8"))
{
var ecPrivateKeyParameters =
(ECPrivateKeyParameters)new Org.BouncyCastle.OpenSsl.PemReader(reader).ReadObject();
var q = ecPrivateKeyParameters.Parameters.G.Multiply(ecPrivateKeyParameters.D).Normalize();
var qx = q.AffineXCoord.GetEncoded();
var qy = q.AffineYCoord.GetEncoded();
var d = ecPrivateKeyParameters.D.ToByteArrayUnsigned();
// Convert the BouncyCastle key to a Native Key.
var msEcp = new ECParameters {Curve = ECCurve.NamedCurves.nistP256, Q = {X = qx, Y = qy}, D = d};
return ECDsa.Create(msEcp);
}
}
private string CreateJwt(ECDsa key, string keyId, string teamId)
{
var securityKey = new ECDsaSecurityKey(key) { KeyId = keyId };
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.EcdsaSha256);
var descriptor = new SecurityTokenDescriptor
{
IssuedAt = DateTime.Now,
Issuer = teamId,
SigningCredentials = credentials,
};
var handler = new JwtSecurityTokenHandler();
var encodedToken = handler.CreateEncodedJwt(descriptor);
return encodedToken;
}
It have tried the above on ASP.NET CORE 2.1 and 2.2 to no avail. The response I always got was "The message received was unexpected or badly formatted" with HttpVersion20 enabled, which made me doubt whether http2 implementation is concrete.
Below is what worked on ASP.NET CORE 3.0;
var teamId = "YOURTEAMID";
var keyId = "YOURKEYID";
try
{
//
var data = await System.IO.File.ReadAllTextAsync(Path.Combine(_environment.ContentRootPath, "apns/"+config.P8FileName));
var list = data.Split('\n').ToList();
var prk = list.Where((s, i) => i != 0 && i != list.Count - 1).Aggregate((agg, s) => agg + s);
//
var key = new ECDsaCng(CngKey.Import(Convert.FromBase64String(prk), CngKeyBlobFormat.Pkcs8PrivateBlob));
//
var token = CreateToken(key, keyId, teamId);
//
var deviceToken = "XXXXXXXXXXXXXXXXXXXXXXXXXXXX";
var url = string.Format("https://api.sandbox.push.apple.com/3/device/{0}", deviceToken);
var request = new HttpRequestMessage(HttpMethod.Post, url);
//
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
//
request.Headers.TryAddWithoutValidation("apns-push-type", "alert"); // or background
request.Headers.TryAddWithoutValidation("apns-id", Guid.NewGuid().ToString("D"));
//Expiry
//
request.Headers.TryAddWithoutValidation("apns-expiration", Convert.ToString(0));
//Send imediately
request.Headers.TryAddWithoutValidation("apns-priority", Convert.ToString(10));
//App Bundle
request.Headers.TryAddWithoutValidation("apns-topic", "com.xx.yy");
//Category
request.Headers.TryAddWithoutValidation("apns-collapse-id", "test");
//
var body = JsonConvert.SerializeObject(new
{
aps = new
{
alert = new
{
title = "Test",
body = "Sample Test APNS",
time = DateTime.Now.ToString()
},
badge = 1,
sound = "default"
},
acme2 = new string[] { "bang", "whiz" }
})
//
request.Version = HttpVersion.Version20;
//
using (var stringContent = new StringContent(body, Encoding.UTF8, "application/json"))
{
//Set Body
request.Content = stringContent;
_logger.LogInformation(request.ToString());
//
var handler = new HttpClientHandler();
//
handler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls;
//
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true;
//Continue
using (HttpClient client = new HttpClient(handler))
{
//
HttpResponseMessage resp = await client.SendAsync(request).ContinueWith(responseTask =>
{
return responseTask.Result;
//
});
//
_logger.LogInformation(resp.ToString());
//
if (resp != null)
{
string apnsResponseString = await resp.Content.ReadAsStringAsync();
//
handler.Dispose();
//ALL GOOD ....
return;
}
//
handler.Dispose();
}
}
}
catch (HttpRequestException e)
{
_logger.LogError(5, e.StackTrace, e);
}
For CreateToken() Refer Above Recommended solution by yaakov,
I has a problem like you. And i seen #gorniv answer. So it's work with me!
May be you can use: https://www.nuget.org/packages/Apple.Auth.Signin for it!
Goodluck!
Various articles (1, 2) I discovered make this look easy enough:
WebRequest request = HttpWebRequest.Create(url);
var credentialCache = new CredentialCache();
credentialCache.Add(
new Uri(url), // request url
"Digest", // authentication type
new NetworkCredential("user", "password") // credentials
);
request.Credentials = credentialCache;
However, this only works for URLs without URL parameters. For example, I can download http://example.com/test/xyz.html just fine, but when I attempt to download http://example.com/test?page=xyz, the result is a 400 Bad Request message with the following in the server's logs (running Apache 2.2):
Digest: uri mismatch - </test> does not match request-uri </test?page=xyz>
My first idea was that the digest specification requires URL parameters to be removed from the digest hash -- but removing the parameter from the URL passed to credentialCache.Add() didn't change a thing. So it must be the other way around and somewhere in the .NET framework is wrongly removing the parameter from the URL.
You said you removed the querystring paramters, but did you try going all the way back to just the host? Every single example of CredentialsCache.Add() I've seen seems to use only the host, and the docs for CredentialsCache.Add() list the Uri parameter as "uriPrefix", which seems telling.
In other words, try this out:
Uri uri = new Uri(url);
WebRequest request = WebRequest.Create(uri);
var credentialCache = new CredentialCache();
credentialCache.Add(
new Uri(uri.GetLeftPart(UriPartial.Authority)), // request url's host
"Digest", // authentication type
new NetworkCredential("user", "password") // credentials
);
request.Credentials = credentialCache;
If this works, you will also have to make sure that you don't add the same "authority" to the cache more than once... all requests to the same host should be able to make use of the same credential cache entry.
Code taken from this post has worked perfectly for me Implement Digest authentication via HttpWebRequest in C#
I had following issue, when ever I browser the feed url in a browser it asked for username and password and worked fine, however any of the above code samples were not working, on inspecting Request/Response Header (in web developer tools in firefox) i could see header having Authorization of type digest.
Step 1 Add:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using System.Net;
using System.IO;
namespace NUI
{
public class DigestAuthFixer
{
private static string _host;
private static string _user;
private static string _password;
private static string _realm;
private static string _nonce;
private static string _qop;
private static string _cnonce;
private static DateTime _cnonceDate;
private static int _nc;
public DigestAuthFixer(string host, string user, string password)
{
// TODO: Complete member initialization
_host = host;
_user = user;
_password = password;
}
private string CalculateMd5Hash(
string input)
{
var inputBytes = Encoding.ASCII.GetBytes(input);
var hash = MD5.Create().ComputeHash(inputBytes);
var sb = new StringBuilder();
foreach (var b in hash)
sb.Append(b.ToString("x2"));
return sb.ToString();
}
private string GrabHeaderVar(
string varName,
string header)
{
var regHeader = new Regex(string.Format(#"{0}=""([^""]*)""", varName));
var matchHeader = regHeader.Match(header);
if (matchHeader.Success)
return matchHeader.Groups[1].Value;
throw new ApplicationException(string.Format("Header {0} not found", varName));
}
private string GetDigestHeader(
string dir)
{
_nc = _nc + 1;
var ha1 = CalculateMd5Hash(string.Format("{0}:{1}:{2}", _user, _realm, _password));
var ha2 = CalculateMd5Hash(string.Format("{0}:{1}", "GET", dir));
var digestResponse =
CalculateMd5Hash(string.Format("{0}:{1}:{2:00000000}:{3}:{4}:{5}", ha1, _nonce, _nc, _cnonce, _qop, ha2));
return string.Format("Digest username=\"{0}\", realm=\"{1}\", nonce=\"{2}\", uri=\"{3}\", " +
"algorithm=MD5, response=\"{4}\", qop={5}, nc={6:00000000}, cnonce=\"{7}\"",
_user, _realm, _nonce, dir, digestResponse, _qop, _nc, _cnonce);
}
public string GrabResponse(
string dir)
{
var url = _host + dir;
var uri = new Uri(url);
var request = (HttpWebRequest)WebRequest.Create(uri);
// If we've got a recent Auth header, re-use it!
if (!string.IsNullOrEmpty(_cnonce) &&
DateTime.Now.Subtract(_cnonceDate).TotalHours < 1.0)
{
request.Headers.Add("Authorization", GetDigestHeader(dir));
}
HttpWebResponse response;
try
{
response = (HttpWebResponse)request.GetResponse();
}
catch (WebException ex)
{
// Try to fix a 401 exception by adding a Authorization header
if (ex.Response == null || ((HttpWebResponse)ex.Response).StatusCode != HttpStatusCode.Unauthorized)
throw;
var wwwAuthenticateHeader = ex.Response.Headers["WWW-Authenticate"];
_realm = GrabHeaderVar("realm", wwwAuthenticateHeader);
_nonce = GrabHeaderVar("nonce", wwwAuthenticateHeader);
_qop = GrabHeaderVar("qop", wwwAuthenticateHeader);
_nc = 0;
_cnonce = new Random().Next(123400, 9999999).ToString();
_cnonceDate = DateTime.Now;
var request2 = (HttpWebRequest)WebRequest.Create(uri);
request2.Headers.Add("Authorization", GetDigestHeader(dir));
response = (HttpWebResponse)request2.GetResponse();
}
var reader = new StreamReader(response.GetResponseStream());
return reader.ReadToEnd();
}
}
}
Step 2: Call new method
DigestAuthFixer digest = new DigestAuthFixer(domain, username, password);
string strReturn = digest.GrabResponse(dir);
if Url is: http://xyz.rss.com/folder/rss
then
domain: http://xyz.rss.com (domain part)
dir: /folder/rss (rest of the url)
you could also return it as stream and use XmlDocument Load() method.
The solution is to activate this parameter in apache:
BrowserMatch "MSIE" AuthDigestEnableQueryStringHack=On
More info : http://httpd.apache.org/docs/2.0/mod/mod_auth_digest.html#msie
Then add this property in your code for the webrequest object:
request.UserAgent = "MSIE"
it work very well for me
I think the second URL points to dynamic page and you should first call it using GET to get the HTML and then to download it. No experience in this field though.
In earlier answers everybody use the obsolete WEbREquest.Create method.
So here is my async solution what up to date for recently trending's:
public async Task<string> LoadHttpPageWithDigestAuthentication(string url, string username, string password)
{
Uri myUri = new Uri(url);
NetworkCredential myNetworkCredential = new NetworkCredential(username, password);
CredentialCache myCredentialCache = new CredentialCache { { myUri, "Digest", myNetworkCredential } };
var request = new HttpClient(new HttpClientHandler() { Credentials = myCredentialCache, PreAuthenticate = true});
var response = await request.GetAsync(url);
var responseStream = await response.Content.ReadAsStreamAsync();
StreamReader responseStreamReader = new StreamReader(responseStream, Encoding.Default);
string answer = await responseStreamReader.ReadToEndAsync();
return answer;
}