Search code examples
c#encryptioncryptographypublic-key-encryptioncng

System.Security.Cryptography.CryptographicException: The requested operation is not supported. windows server 2012


I'm tiring to generate the jwt token for calling apns. here is my code:

    var header = new Dictionary<string, object>()
    {
        { "kid" , keyID }
    };
    var payload = new Dictionary<string, object>()
    {
        { "iss", teamID },
        { "iat", DateTimeOffset.Now.ToUnixTimeSeconds().ToString() }
    };

    var privateKey = GetApnsPrivateKey(authKeyPath);
    var token = JWT.Encode(payload, privateKey, JwsAlgorithm.ES256, header);
public static CngKey GetApnsPrivateKey(string authKeyPath)
{
    using (var reader = new StreamReader(new FileStream(authKeyPath, FileMode.Open, FileAccess.Read, FileShare.Read)))
    {
        var 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);

code works on my machine just fine but in the server it throw this exception on GetApnsPrivateKey method:

System.Security.Cryptography.CryptographicException: The requested operation is not supported

after little search on the forums i found out its because of CNG Key Storage Providers: https://msdn.microsoft.com/en-us/library/windows/desktop/aa376242.aspx?f=255&MSPPError=-2147217396 now i wonder is there any way to solve the problem?


Solution

  • The source of the exception is System.Security.Cryptography library inside the jose-jwt, so avoid it. Since BouncyCastle library is already in use here, use it for signing. Also, I use Newtonsoft.Json.JsonConvert(..).

    public string GetApnsToken(string authKeyPath,  
    Dictionary<string, object> payload, 
    Dictionary<string, object> header)
    {
        ECPrivateKeyParameters ecPrivateKeyParameters;
        using (var reader = new StreamReader(new FileStream(authKeyPath, FileMode.Open, FileAccess.Read, FileShare.Read)))
        {
           ecPrivateKeyParameters = (ECPrivateKeyParameters)new PemReader(reader).ReadObject();
         }
         byte[] headerBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(header));
         byte[] payloadBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(payload));
         byte[] bytesToSign = Encoding.UTF8.GetBytes(ConcatTokenPartsWithEncoding(headerBytes, payloadBytes));
    
       var signer = new DsaDigestSigner(new ECDsaSigner(), new Sha256Digest());
       signer.Init(true, ecPrivateKeyParameters);
       signer.BlockUpdate(bytesToSign, 0, bytesToSign.Length);
       byte[] signBytes = signer.GenerateSignature();
    
       return ConcatTokenPartsWithEncoding(headerBytes, payloadBytes, signBytes);
    
    }
    
    public static string ConcatTokenPartsWithEncoding(params byte[][] parts)
    {
    var builder = new StringBuilder();
    foreach (var part in parts)
    {
     //encode base64 for Url
     var base64Str = Convert.ToBase64String(part);
     base64Str = base64Str.Split('=')[0]; // Remove any trailing '='s
     base64Str = base64Str.Replace('+', '-'); 
     base64Str = base64Str.Replace('/', '_');
     builder.Append(base64Str).Append(".");
    }
    builder.Remove(builder.Length - 1, 1); 
    return builder.ToString();
    }