获取适用于 Azure 通知中心的 APNS 证书



我想创建一个应用,以便我可以审核所有 200+ 个通知中心。这主要用于查找即将过期的 APNS 的证书。

我可以在PowerShell中做得很好,但我无法在C#中做到这一点,因为Fluent SDK没有像PowerShell这样的凭据的方法。

var credentials = SdkContext.AzureCredentialsFactory
.FromServicePrincipal(clientId,
clientSecret,
tenantId,
AzureEnvironment.AzureGlobalCloud);
var azure = Azure
.Configure()
.Authenticate(credentials)
.WithDefaultSubscription();
NotificationHubsManagementClient nhClient = new NotificationHubsManagementClient(credentials);
nhClient.SubscriptionId = subscriptionId;
var nhNamespaces = nhClient.Namespaces.ListAllAsync().Result;
foreach (var nhNamespace in nhNamespaces)
{
var nhHubs = nhClient.NotificationHubs.ListAsync(resourceGroupName, nhNamespace.Name).Result;
foreach (var nhHub in nhHubs)
{
var hub = nhClient.NotificationHubs.GetAsync(resourceGroupName, nhNamespace.Name, nhHub.Name).Result;
//THEY ARE ALWAYS NULL AND THERE IS NO METHOD TO GET THE APNS CREDENTIALS
if (hub.ApnsCredential != null)
{
var apnsCred = hub.ApnsCredential;
Console.WriteLine(apnsCred.ApnsCertificate);
}
}
}

在PowerShell中,我可以调用:

$pnsCred = Get-AzureRmNotificationHubPNSCredentials -ResourceGroup $resourceGroup -Namespace $hubNamespaceName -NotificationHub $hub.Name

我需要一种方法来获取 C# 中的中心凭据。


我几乎最终的解决方案(需要更多的折射和错误处理(:

using Microsoft.Azure.Management.NotificationHubs.Fluent;
using Microsoft.Azure.Management.NotificationHubs.Fluent.Models;
using Microsoft.Azure.Management.ResourceManager.Fluent;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Newtonsoft.Json;
using RestSharp;
using System;
using System.Linq;
using System.Configuration;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using mpu.sql;
using mpu.snow;
using mpu.helpers;
namespace mpu.azure
{
public static class NotificationHubHelper
{
private static string tenantId = ConfigurationManager.AppSettings["nhIda:tenantId"];
private static string clientId = ConfigurationManager.AppSettings["nhIda:clientId"];
private static string clientSecret = ConfigurationManager.AppSettings["nhIda:clientSecret"];
private static string resourceGroupName = ConfigurationManager.AppSettings["nhIda:resourceGroupName"];
private static string subscriptionId = ConfigurationManager.AppSettings["nhIda:subscriptionId"];
private static DateTime expiryCheck = DateTime.Now.AddDays(-30);
public static void CheckHubApnsCerts()
{
var credentials = SdkContext.AzureCredentialsFactory
.FromServicePrincipal(clientId,
clientSecret,
tenantId,
AzureEnvironment.AzureGlobalCloud);
NotificationHubsManagementClient nhClient = new NotificationHubsManagementClient(credentials);
nhClient.SubscriptionId = subscriptionId;
var nhNamespaces = nhClient.Namespaces.ListAllAsync().Result;
nhNamespaces.AsParallel().ForAll(nhNamespace =>
{
var nhHubs = nhClient.NotificationHubs.ListAsync(resourceGroupName, nhNamespace.Name).Result;
nhHubs.AsParallel().ForAll(async (nhHub) =>
{
PnsCredentialResponse pnsCredential = await GetPnsCredential(nhNamespace, nhHub, nhClient.ApiVersion);
if (!string.IsNullOrEmpty(pnsCredential?.properties?.apnsCredential?.properties?.apnsCertificate))
{
var certRawValue = pnsCredential.properties.apnsCredential.properties.apnsCertificate;
var certKey = pnsCredential.properties.apnsCredential.properties.certificateKey;
var cert = new X509Certificate2(Convert.FromBase64String(certRawValue), certKey, X509KeyStorageFlags.MachineKeySet
| X509KeyStorageFlags.PersistKeySet
| X509KeyStorageFlags.Exportable);
Console.ForegroundColor = ConsoleColor.Green;
if (cert.NotAfter <= expiryCheck)
{
Console.ForegroundColor = ConsoleColor.Red;
try
{
var certIncident = SqlHelper.GetRecordByCertThumb(cert.Thumbprint);
if (certIncident == null)
{
var incidentNumber = SnowIncidents.CreateP2Incident($"Notification Hub APNS Certificate Expiring {nhHub.Name}", $"The notification hub APNS certificate for hub {nhHub.Name} is due to expire on {cert.NotAfter}. Please verify in Azure and request a new certificate from the client ASAP.");
if (!string.IsNullOrEmpty(incidentNumber))
SqlHelper.CreateIncidentRecord(cert.Thumbprint, incidentNumber);
}
}
catch
{
EmailHelper.SendCertExpiryEmailForNotificationHub(nhHub.Name, cert.NotAfter);
}
}
Console.WriteLine($"{nhHub.Name} - {cert.NotAfter} - {cert.Thumbprint}");
}
});
});
}
private static async Task<PnsCredentialResponse> GetPnsCredential(NamespaceResourceInner nhNamespace, NotificationHubResourceInner nhHub, string apiVerion)
{
var requestString = $"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.NotificationHubs/namespaces/{nhNamespace.Name}/notificationHubs/{nhHub.Name}/pnsCredentials?api-version={apiVerion}";
var client = new RestClient(requestString);
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", $"Bearer {await GetAccessToken(tenantId, clientId, clientSecret)}");
IRestResponse response = client.Execute(request);
var pnsCredential = JsonConvert.DeserializeObject<PnsCredentialResponse>(response.Content);
return pnsCredential;
}
private static async Task<string> GetAccessToken(string tenantId, string clientId, string clientKey)
{
string authContextURL = "https://login.windows.net/" + tenantId;
var authenticationContext = new AuthenticationContext(authContextURL);
var credential = new ClientCredential(clientId, clientKey);
var result = await authenticationContext
.AcquireTokenAsync("https://management.azure.com/", credential);
if (result == null)
throw new InvalidOperationException("Failed to obtain the JWT token for management.azure.com");
return result.AccessToken;
}
}
public class PnsCredentialResponse
{
public string id { get; set; }
public string name { get; set; }
public string type { get; set; }
public object location { get; set; }
public object tags { get; set; }
public Properties properties { get; set; }
}
public class Properties
{
public Apnscredential apnsCredential { get; set; }
}
public class Apnscredential
{
[JsonProperty("properties")]
public ApnsProperties properties { get; set; }
}
public class ApnsProperties
{
public string endpoint { get; set; }
public string apnsCertificate { get; set; }
public string certificateKey { get; set; }
public string thumbprint { get; set; }
}
}

Azure PowerShell 模块将构建在管理 SDK 之上。最新版本可以在NuGet上找到。这些 SDK 的源代码可以在 GitHub 上找到。如果没记错的话,我相信NamespaceOperations.GetWithHttpMessagesAsync方法就是你要找的。

值得注意的是,管理功能以前在通知中心 SDK 本身的 v1 中。它在 v2 中被取出,以防止对有多种方法做同一件事感到困惑。但是,普遍的需求是断言管理功能被带回来,以便有一个包来处理通知中心的所有内容。在创作时,这些更改正在审核中。