System.Security.Cryptography.CryptographicException: 不支持请求的操作。 windows 服务器 2012
System.Security.Cryptography.CryptographicException: The requested operation is not supported. windows server 2012
我厌倦了生成用于调用 apns 的 jwt 令牌。这是我的代码:
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);
代码在我的机器上运行良好,但在服务器中它在 GetApnsPrivateKey
方法上抛出此异常:
System.Security.Cryptography.CryptographicException: The requested operation is not supported
在论坛上搜索了一下后,我发现它是因为 CNG 密钥存储提供商:https://msdn.microsoft.com/en-us/library/windows/desktop/aa376242.aspx?f=255&MSPPError=-2147217396
现在我想知道有什么办法可以解决这个问题吗?
异常的来源是jose-jwt中的System.Security.Cryptography库,所以避免它。
由于 BouncyCastle 库已在此处使用,因此请使用它进行签名。
另外,我使用 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();
}
感谢 Natalya 的 post。我可以这样解决问题:
var dateTime = DateTimeOffset.Now;
var privateKeyAsBytes = Convert.FromBase64String(authKey);
var privateKey = CngKey.Import(privateKeyAsBytes, CngKeyBlobFormat.Pkcs8PrivateBlob);
var header = new Dictionary<string, object>() { { "kid", keyID } };
var payload = new Dictionary<string, object>() { { "iss", teamID }, { "iat", dateTime.ToUnixTimeSeconds().ToString() } };
var token = JWT.Encode(payload, privateKey, JwsAlgorithm.ES256, header);
我厌倦了生成用于调用 apns 的 jwt 令牌。这是我的代码:
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);
代码在我的机器上运行良好,但在服务器中它在 GetApnsPrivateKey
方法上抛出此异常:
System.Security.Cryptography.CryptographicException: The requested operation is not supported
在论坛上搜索了一下后,我发现它是因为 CNG 密钥存储提供商:https://msdn.microsoft.com/en-us/library/windows/desktop/aa376242.aspx?f=255&MSPPError=-2147217396 现在我想知道有什么办法可以解决这个问题吗?
异常的来源是jose-jwt中的System.Security.Cryptography库,所以避免它。 由于 BouncyCastle 库已在此处使用,因此请使用它进行签名。 另外,我使用 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();
}
感谢 Natalya 的 post。我可以这样解决问题:
var dateTime = DateTimeOffset.Now;
var privateKeyAsBytes = Convert.FromBase64String(authKey);
var privateKey = CngKey.Import(privateKeyAsBytes, CngKeyBlobFormat.Pkcs8PrivateBlob);
var header = new Dictionary<string, object>() { { "kid", keyID } };
var payload = new Dictionary<string, object>() { { "iss", teamID }, { "iat", dateTime.ToUnixTimeSeconds().ToString() } };
var token = JWT.Encode(payload, privateKey, JwsAlgorithm.ES256, header);