You need to enable JavaScript to run this app. 如何使用C#发送iOS APN通知(含P12证书配置及代码示例)
优惠活动
大模型
产品
解决方案
定价
更多

如何使用C#发送iOS APN通知(含P12证书配置及代码示例)

基于.NET Framework 4.5/4.7 C# Windows Service 发送APN通知完整实现方案

前置准备工作

  • 持有苹果开发者账号生成的APNs认证密钥(.p8格式),相比旧版.p12证书无需定期更新,优先推荐使用
  • 提前记录3个核心参数:密钥ID(Key ID)、开发者团队ID(Team ID)、待推送App的Bundle ID
  • 获取目标设备的推送Token,注意开发环境和生产环境的Token不通用
  • 确认Windows服务运行环境已安装对应版本的.NET Framework 4.5/4.7运行时

依赖安装

通过NuGet安装2个依赖包即可:

  • System.Net.Http.WinHttpHandler:用于支持HTTP/2协议(APNs强制要求使用HTTP/2)
  • System.IdentityModel.Tokens.Jwt:用于生成APNs校验所需的JWT令牌
  • Newtonsoft.Json:用于序列化通知 payload

具体代码实现

1. APNs配置实体类

public class ApnConfig
{
    // 苹果开发者后台下载的p8密钥完整内容
    public string P8PrivateKey { get; set; }
    // 密钥ID
    public string KeyId { get; set; }
    // 开发者团队ID
    public string TeamId { get; set; }
    // App的Bundle ID
    public string BundleId { get; set; }
    // APNs地址:开发环境填"api.sandbox.push.apple.com",生产环境填"api.push.apple.com"
    public string ApnHost { get; set; }
    // 固定端口443
    public int Port { get; set; } = 443;
}

2. JWT令牌生成工具方法

using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;

public static class JwtHelper
{
    public static string GenerateApnJwt(ApnConfig config)
    {
        // 预处理p8密钥,去除格式标记和换行符
        var privateKeyContent = config.P8PrivateKey
            .Replace("-----BEGIN PRIVATE KEY-----", string.Empty)
            .Replace("-----END PRIVATE KEY-----", string.Empty)
            .Replace("\r", string.Empty)
            .Replace("\n", string.Empty)
            .Trim();
        var privateKeyBytes = Convert.FromBase64String(privateKeyContent);
        
        using var ecDsa = ECDsa.Create();
        ecDsa.ImportPkcs8PrivateKey(privateKeyBytes, out _);
        var securityKey = new ECDsaSecurityKey(ecDsa) { KeyId = config.KeyId };
        var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.EcdsaSha256);
        
        var claims = new[]
        {
            new Claim("iss", config.TeamId),
            new Claim("iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString())
        };
        
        // APNs要求JWT有效期最长不超过60分钟
        var jwtToken = new JwtSecurityToken(
            issuer: config.TeamId,
            claims: claims,
            expires: DateTime.UtcNow.AddMinutes(20),
            signingCredentials: signingCredentials
        );
        
        return new JwtSecurityTokenHandler().WriteToken(jwtToken);
    }
}

3. APNs推送核心逻辑

using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using Newtonsoft.Json;

public class ApnPushCore
{
    private readonly ApnConfig _config;
    // 复用HttpClient避免端口耗尽
    private static readonly HttpClient _httpClient = new HttpClient(new WinHttpHandler()
    {
        ServerCertificateValidationCallback = (sender, cert, chain, sslPolicyErrors) => true
    });

    public ApnPushCore(ApnConfig config)
    {
        _config = config;
    }

    public async Task<bool> SendPush(string deviceToken, string title, string body, int badge = 0, string sound = "default")
    {
        try
        {
            var jwtToken = JwtHelper.GenerateApnJwt(_config);
            var requestUrl = $"https://{_config.ApnHost}:{_config.Port}/3/device/{deviceToken}";
            
            using var request = new HttpRequestMessage(HttpMethod.Post, requestUrl);
            // 强制指定HTTP/2协议
            request.Version = new Version(2, 0);
            request.Headers.Authorization = new AuthenticationHeaderValue("bearer", jwtToken);
            request.Headers.TryAddWithoutValidation("apns-topic", _config.BundleId);
            // 推送类型:普通通知填alert,静默通知填background
            request.Headers.TryAddWithoutValidation("apns-push-type", "alert");
            
            // 构造通知payload
            var payload = new
            {
                aps = new
                {
                    alert = new
                    {
                        title = title,
                        body = body
                    },
                    badge = badge,
                    sound = sound
                },
                // 自定义扩展参数可自行添加
                custom_param = ""
            };
            var jsonPayload = JsonConvert.SerializeObject(payload);
            request.Content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
            
            var response = await _httpClient.SendAsync(request);
            return response.IsSuccessStatusCode;
        }
        catch (Exception ex)
        {
            // 自行扩展日志记录逻辑
            Console.WriteLine($"推送失败:{ex.Message}");
            return false;
        }
    }
}

4. Windows Service集成示例

public partial class ApnPushService : ServiceBase
{
    private ApnPushCore _pushCore;
    private Timer _taskTimer;

    public ApnPushService()
    {
        InitializeComponent();
        // 配置信息建议加密存储在配置文件中,不要硬编码
        var apnConfig = new ApnConfig
        {
            P8PrivateKey = "替换为你的p8密钥内容",
            KeyId = "替换为你的密钥ID",
            TeamId = "替换为你的团队ID",
            BundleId = "替换为你的App Bundle ID",
            ApnHost = "api.sandbox.push.apple.com"
        };
        _pushCore = new ApnPushCore(apnConfig);
    }

    protected override void OnStart(string[] args)
    {
        // 定时执行推送任务,间隔可根据业务需求调整
        _taskTimer = new Timer(ExecutePushTask, null, TimeSpan.Zero, TimeSpan.FromSeconds(30));
    }

    private async void ExecutePushTask(object state)
    {
        // 此处替换为业务逻辑:例如从数据库拉取待推送的消息列表、设备Token列表
        var targetDeviceToken = "替换为目标设备的推送Token";
        var pushResult = await _pushCore.SendPush(targetDeviceToken, "测试通知", "这是从Windows服务发出的APN通知", 1);
        // 自行扩展推送结果记录逻辑
    }

    protected override void OnStop()
    {
        _taskTimer?.Dispose();
    }
}

注意事项

  • 严格区分开发环境和生产环境的APNs地址、设备Token,二者不互通,配置错误会直接推送失败
  • Windows Server 2012及以上系统才原生支持HTTP/2,低版本系统需要先安装对应系统补丁
  • p8密钥请妥善保管,不要明文存储在代码或配置文件中
  • 短时间内大量推送重复内容会被苹果服务器拦截,建议合理控制推送频率

内容的提问来源于stack exchange,提问作者Sumesh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.19 17:06:00