Skip to content

Commit 37eb6e0

Browse files
committed
Fix APN JWT signing on Windows Server/IIS and bump to v5.0.1
Extract EC private key from PKCS#8 envelope and use ImportECPrivateKey instead of ImportPkcs8PrivateKey to avoid CNG failure when Load User Profile is disabled.
1 parent ad8881b commit 37eb6e0

2 files changed

Lines changed: 24 additions & 5 deletions

File tree

CorePush/Apple/ApnSender.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Concurrent;
33
using System.Collections.Generic;
4+
using System.Formats.Asn1;
45
using System.Net.Http;
56
using System.Net.Http.Headers;
67
using System.Security.Cryptography;
@@ -131,16 +132,31 @@ private string CreateJwtToken()
131132
var unsignedJwtData = $"{headerBase64}.{payloadBase64}";
132133
var unsignedJwtBytes = Encoding.UTF8.GetBytes(unsignedJwtData);
133134

134-
var privateKeyBytes = Convert.FromBase64String(CryptoHelper.CleanP8Key(settings.P8PrivateKey));
135+
var pkcs8Bytes = Convert.FromBase64String(CryptoHelper.CleanP8Key(settings.P8PrivateKey));
136+
var ecPrivateKey = ExtractEcPrivateKey(pkcs8Bytes);
135137

136138
using var dsa = ECDsa.Create();
137-
dsa.ImportPkcs8PrivateKey(privateKeyBytes, out _);
139+
dsa.ImportECPrivateKey(ecPrivateKey, out _);
138140

139141
var signature = dsa.SignData(unsignedJwtBytes, HashAlgorithmName.SHA256);
140142
var signatureBase64 = Base64UrlEncode(signature);
141143
return $"{unsignedJwtData}.{signatureBase64}";
142144
}
143145

146+
/// <summary>
147+
/// Extracts the inner EC private key (SEC 1 / RFC 5915) from a PKCS#8 envelope.
148+
/// Using ImportECPrivateKey instead of ImportPkcs8PrivateKey avoids the CNG PKCS#8 import
149+
/// path which fails on Windows Server/IIS when "Load User Profile" is disabled.
150+
/// </summary>
151+
internal static byte[] ExtractEcPrivateKey(byte[] pkcs8PrivateKey)
152+
{
153+
var reader = new AsnReader(pkcs8PrivateKey, AsnEncodingRules.DER);
154+
var pkcs8 = reader.ReadSequence();
155+
pkcs8.ReadEncodedValue(); // version
156+
pkcs8.ReadSequence(); // algorithm identifier
157+
return pkcs8.ReadOctetString(); // SEC 1 EC private key
158+
}
159+
144160
private static string Base64UrlEncode(string str)
145161
{
146162
var bytes = Encoding.UTF8.GetBytes(str);

CorePush/CorePush.csproj

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
<Summary>Server Side library for sending ✅Web, ✅Android and ✅iOS Push Notifications</Summary>
1111
<Authors>andrei-m-code</Authors>
1212

13-
<AssemblyVersion>5.0.0</AssemblyVersion>
14-
<FileVersion>5.0.0</FileVersion>
15-
<Version>5.0.0</Version>
13+
<AssemblyVersion>5.0.1</AssemblyVersion>
14+
<FileVersion>5.0.1</FileVersion>
15+
<Version>5.0.1</Version>
1616

1717
<PackageProjectUrl>https://github.com/andrei-m-code/CorePush</PackageProjectUrl>
1818
<RepositoryUrl>https://github.com/andrei-m-code/CorePush</RepositoryUrl>
@@ -25,6 +25,9 @@
2525
<PackageTags>push-notifications android-push-notifications ios-push-notifications web-push web-push-notifications apn fcm firebase</PackageTags>
2626

2727
<PackageReleaseNotes>
28+
v5.0.1
29+
- Fix APN JWT signing failure on Windows Server/IIS when "Load User Profile" is disabled by extracting EC private key from PKCS#8 envelope
30+
2831
v5.0.0
2932
- Remove BouncyCastle dependency in favor of built-in .NET cryptography
3033
- Upgrade to .NET 10

0 commit comments

Comments
 (0)