首页 > 解决方案 > 获取 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; }
    }
}

标签: c#certificateazure-notificationhub

解决方案


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

值得注意的是,管理功能以前在 NotificationHubs SDK 本身的 v1 中。它在 v2 中被删除,以防止混淆有多种方法来做同样的事情。然而,普遍的需求是恢复管理功能,以便有一个包可以与通知中心的所有东西一起使用。在创作时,这些更改正在审核中


推荐阅读