X509Certificate2: доступ запрещен при расшифровке с помощью privatekey
У меня есть следующий код, который используется для (en/de) шифрования ключа, который, в свою очередь, используется для шифрования строк, помещаемых в заголовки клиентов, некоторые из которых приложение может кэшировать. (они содержат чувствительную информацию). Приложение в этом случае - ASP .Net core 2.0, работающий на IIS.
Шифрование с использованием открытого ключа работает просто отлично, но возникает исключение, когда я пытаюсь расшифровать.
private string DecryptKey(string key)
{
if (this.Certificate == null || string.IsNullOrEmpty(key))
throw new Exception("A x509 certificate and string for decryption must be provided");
// Get the string as bytes
var encryptedBytes = Convert.FromBase64String(key);
if (!Certificate.HasPrivateKey)
throw new Exception("x509 certificate does not contain a private key for decryption");
using (var rsa = Certificate.GetRSAPrivateKey())
{
var result = rsa.Decrypt(encryptedBytes, RSAEncryptionPadding.OaepSHA512); // **Exception is thrown here**
return ASCIIEncoding.ASCII.GetString(result);
}
}
private string EncryptKey(string key)
{
if (this.Certificate == null || string.IsNullOrEmpty(key))
throw new Exception("A x509 certificate and string for decryption must be provided");
// Get the string as bytes
var bytes = ASCIIEncoding.ASCII.GetBytes(key);
if (!Certificate.HasPrivateKey)
throw new Exception("x509 certificate does not contain a private key for decryption");
using (var rsa = Certificate.GetRSAPrivateKey())
{
var result = rsa.Encrypt(bytes, RSAEncryptionPadding.OaepSHA512);
return Convert.ToBase64String(result);
}
}
/// <summary>
/// Returns a X509Certificate2 with the given name
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
protected X509Certificate2 GetX509Certificate(string name)
{
// Get the certificate store for the current user.
var store = new X509Store(StoreLocation.LocalMachine);
try
{
store.Open(OpenFlags.ReadOnly);
// Place all certificates in an X509Certificate2Collection object.
var certCollection = store.Certificates;
var currentCerts = certCollection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
var signingCert = currentCerts.Find(X509FindType.FindBySubjectDistinguishedName, name, false);
if (signingCert.Count == 0)
return null;
return signingCert[0];
}
finally
{
store.Close();
}
}
Исключение: Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException: "Доступ запрещен"
Вот этот стек:
at System.Security.Cryptography.RSACng.EncryptOrDecrypt(SafeNCryptKeyHandle key, Byte[] input, AsymmetricPaddingMode paddingMode, Void* paddingInfo, EncryptOrDecryptAction encryptOrDecrypt)
at System.Security.Cryptography.RSACng.EncryptOrDecrypt(Byte[] data, RSAEncryptionPadding padding, EncryptOrDecryptAction encryptOrDecrypt)
at System.Security.Cryptography.RSACng.Decrypt(Byte[] data, RSAEncryptionPadding padding)
at VmeApi.Extensions.KeyEncryptService.DecryptKey(String key) in C:\Users\kevom\Source\Repos\VME\VME Management\Management Core Api\Extensions\AppSettings\KeyEncryptService.cs:line 69
at VmeApi.Extensions.KeyEncryptService..ctor(String certName, String encryptedKey) in C:\Users\kevom\Source\Repos\VME\VME Management\Management Core Api\Extensions\AppSettings\KeyEncryptService.cs:line 31
at Management_Core_Api.Startup.ConfigureServices(IServiceCollection services) in C:\Users\kevom\Source\Repos\VME\VME Management\Management Core Api\Startup.cs:line 83
Я попытался предоставить моей учетной записи, учетной записи IUser и сетевой службе полный доступ к закрытому ключу сертификата с помощью mmc и перезапустить ОС, но это ничего не помогло.
1 ответ
Это может быть немного длинно, поэтому я добавлю это как ответ, так как форматирование как комментарий может быть недостаточным,
Попробуйте проверить, доступен ли закрытый ключ, используя следующий код
private static string FindKeyLocation(string keyFileName)
{
string firstLocation =
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
string secondLocation = firstLocation + @"\Microsoft\Crypto\RSA\MachineKeys";
string[] textArray1 = Directory.GetFiles(secondLocation, keyFileName);
if (textArray1.Length > 0)
{
return secondLocation;
}
string thirdLocation =
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string forthLocation = thirdLocation + @"\Microsoft\Crypto\RSA\";
textArray1 = Directory.GetDirectories(forthLocation);
if (textArray1.Length > 0)
{
foreach (string fifthLocation in textArray1)
{
textArray1 = Directory.GetFiles(fifthLocation, keyFileName);
if (textArray1.Length != 0)
{
return fifthLocation;
}
}
}
return "Private key exists but is not accessible";
}
вы будете использовать код, указав в качестве имени файла сертификата
RSACryptoServiceProvider rsa = cert.PrivateKey as RSACryptoServiceProvider;
if (rsa != null)
{
string keyfilepath =
FindKeyLocation(rsa.CspKeyContainerInfo.UniqueKeyContainerName);
}
Этот фрагмент кода, возможно, может пролить некоторый свет на то, почему расшифровка может не работать, а полный метод добавления пользователя в сертификат в коде будет выглядеть примерно так
private static void AddAccessToCertificate(X509Certificate2 cert, string user)
{
RSACryptoServiceProvider rsa = cert.PrivateKey as RSACryptoServiceProvider;
if (rsa != null)
{
string keyfilepath =
FindKeyLocation(rsa.CspKeyContainerInfo.UniqueKeyContainerName);
FileInfo file = new FileInfo(keyfilepath + "\\" +
rsa.CspKeyContainerInfo.UniqueKeyContainerName);
FileSecurity fs = file.GetAccessControl();
NTAccount account = new NTAccount(user);
fs.AddAccessRule(new FileSystemAccessRule(account,
FileSystemRights.FullControl, AccessControlType.Allow));
file.SetAccessControl(fs);
}
}