System security cryptography error

User411292362 posted
  • Remove From My Forums
  • Question

  • User411292362 posted

    Dear Experts

    Thanx for reply.I gor idea from that links.One more doubt when i decrypt the ecncrypt string using auto generate key its showing error as

    System.Security.Cryptography.CryptographicException: Bad Data.

    I give the my function

    private
    void DecryptFunctionINGData()

    {

    try

    {

    string strEncrypt =
    «9FEDAB741C3B085859D22FB1DDE46F32»;
    // Encrypt string

    TripleDESCryptoServiceProvider tdes =
    new TripleDESCryptoServiceProvider();

    tdes.Key = Encoding.UTF8.GetBytes(«NlQr7tt/AJE/i/mVmI6TRg==»);
    // AutoGenerate Key


    tdes.Mode = CipherMode.ECB;

    tdes.Padding = PaddingMode.PKCS7;


    ICryptoTransform crypt = tdes.CreateDecryptor();

    byte[] cipher = FromHex(strEncrypt);

    byte[] plain = crypt.TransformFinalBlock(cipher, 0, cipher.Length);
    // Here Error showing


    Response.Write(«Decryped string : « + System.Text.Encoding.UTF8.GetString(plain) +
    «<br/>»);

    }

    catch (Exception ex)

    {

    Response.Write(ex.Message);

    }

    }

    I would appreciate some help

    Thanx & Regards

    Mani

Answers

  • User717210030 posted

    Hi,thanigaimaniveena

    Do you know how the data encrypted by java: code and the name of Cryptography Arithmetic ?

    Both c# and java implement the same arithmetic, but with some kind of difference in using of parameter.

    Here is a example, data encrypt by c# and encrypted by java:

    Data encrypted by c#

       
    public static string Encrypt(string message, string key)
        {
            DES des 
    = new DESCryptoServiceProvider();
            des.Key 
    = Encoding.UTF8.GetBytes(key);
            des.IV 
    = Encoding.UTF8.GetBytes(key);byte[] bytes = Encoding.UTF8.GetBytes(message);
            
    byte[] resultBytes = des.CreateEncryptor().TransformFinalBlock(bytes, 0, bytes.Length);return Convert.ToBase64String(resultBytes);
        } 

     Data encrupted by Java:

       public static byte[] desEncrypt(String message, String key) throws Exception {
            Cipher cipher 
    = Cipher.getInstance(«DES/CBC/PKCS5Padding«);

            DESKeySpec desKeySpec 

    = new DESKeySpec(key.getBytes(«UTF-8«));

            SecretKeyFactory keyFactory 

    = SecretKeyFactory.getInstance(«DES«);
            SecretKey secretKey 
    = keyFactory.generateSecret(desKeySpec);
            IvParameterSpec iv 
    = new IvParameterSpec(key.getBytes(«UTF-8»));
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
    return cipher.doFinal(message.getBytes(«UTF-8«));
        }

     Check this difference between the different ways;

    Regards

    • Marked as answer by

      Thursday, October 7, 2021 12:00 AM


Offline

yamaoto

 


#1
Оставлено
:

9 октября 2014 г. 12:36:14(UTC)

yamaoto

Статус: Новичок

Группы: Участники

Зарегистрирован: 09.10.2014(UTC)
Сообщений: 3
Российская Федерация
Откуда: Якутск

При попытке получения доступа к секретному ключу происходит ошибка:
System.Security.Cryptography.CryptographicException: Неправильный параметр набора ключей.
at CryptoPro.Sharpei.CPUtils.CreateProvHandle(CspParameters parameters, Boolean randomKeyContainer)

А именно в том случае если код запущен в сервисе WCF внутри ASP.NET сайта.

Тот же код запущенный от имени пользователя с консоли работает прекрасно

Пробовал получить доступ как из хранилища сертификатов стандартными как средствами

Код:


var store = new X509Store(StoreName.My, location);
store.Open(OpenFlags.ReadOnly);
var certs = store.Certificates.Cast<X509Certificate2>().Where(crt =>crt.SerialNumber ==serialNumber.Trim()).ToList();
var cert = certs[0];
if (!CryptoHelper.IsGostCertificate(cert))
	throw new SecurityException(string.Format("Certificate '{0}' is cant' use for GOST algorithms", cert.Subject));
if (!cert.HasPrivateKey)
{
	throw new SecurityException(string.Format("Certificate '{0} has no private key", cert.Subject));
}
var gostCert = cert.PrivateKey as Gost3410CryptoServiceProvider;

так и через Gost3410CryptoServiceProvider

Код:


var parameters = new CspParameters(75, cryptoProviderName, containerName);
parameters.Flags = CspProviderFlags.UseExistingKey | CspProviderFlags.NoPrompt | CspProviderFlags.UseMachineKeyStore;
parameters.KeyPassword = password;

var provider = new Gost3410CryptoServiceProvider(parameters);

Сертификаты установлены в хранилище компьютера (проверял кучу раз)
Ключ лежит в eToken Java (так же пробовал скопировать его в хранилище реестра криптопро — тот же эффект)
Пробовал запуск пула приложений IIS от имени администратора которым заходил сам, от имени NETWORK_SERVICE, и стандартным пользователем пула

в ветке реестра нет чего-либо

Код:

HKEY_LOCAL_MACHINESOFTWARECrypto ProSettingsKeys

а в ветке

Код:

HKEY_LOCAL_MACHINESOFTWARECrypto ProSettingsUsersS-1-5-21-***-500

есть разделы KeyDevices и Random
Третий день уже разбираюсь в чем суть, голова уже не варит d'oh!

Параметры системы:

  • Приложение работает на .Net 4.0
  • Windows Server 2008R2 Standard x64
  • IIS 7.5
  • КриптоПро CSP 3.6.7777 (версия ядра СКЗИ 3.6.5365 КС1)
  • КриптоПро .Net 1.0.5320.0
  • Лицензии КриптоПро все тестовые, до декабря
  • На сервере присутствует VipNet и SecretNet


Вверх


Offline

Андрей Писарев

 


#2
Оставлено
:

9 октября 2014 г. 13:15:47(UTC)

Андрей *

Статус: Сотрудник

Группы: Участники

Зарегистрирован: 26.07.2011(UTC)
Сообщений: 11,756
Мужчина
Российская Федерация

Сказал «Спасибо»: 451 раз
Поблагодарили: 1840 раз в 1423 постах

Криптопровайдер от ОАО «Информационные Технологии и Коммуникационные Системы» — обязателен?

Техническую поддержку оказываем тут
Наша база знаний


Вверх

WWW


Offline

yamaoto

 


#3
Оставлено
:

9 октября 2014 г. 17:35:43(UTC)

yamaoto

Статус: Новичок

Группы: Участники

Зарегистрирован: 09.10.2014(UTC)
Сообщений: 3
Российская Федерация
Откуда: Якутск

Автор: Андрей * Перейти к цитате

Криптопровайдер от ОАО «Информационные Технологии и Коммуникационные Системы» — обязателен?

Да, обязателен.

Проблемы могут быть из-за его несовместимости с КриптоПро?


Вверх


Offline

yamaoto

 


#4
Оставлено
:

13 октября 2014 г. 11:16:31(UTC)

yamaoto

Статус: Новичок

Группы: Участники

Зарегистрирован: 09.10.2014(UTC)
Сообщений: 3
Российская Федерация
Откуда: Якутск

Попробовали запустить на новой машине MS WS2008R2 Standard x64 en без VipNet’a SecretNet
выходит таже самая ошибка

Код:


System.Security.Cryptography.CryptographicException: Keyset does not exist
at CryptoPro.Sharpei.CPUtils.CreateProvHandle(CspParameters parameters, Boolean randomKeyContainer)
at CryptoPro.Sharpei.CPUtils.GetKeyPairHelper(CPCspAlgorithmType keyType, CspParameters parameters, Boolean randomKeyContainer, Int32 dwKeySize, SafeProvHandleCP& safeProvHandle, SafeKeyHandleCP& safeKeyHandle)
at CryptoPro.Sharpei.Gost3410CryptoServiceProvider.GetKeyPair()

ключ находится в реестре компьютера, сертификат тоже установлен в «личное» компьютера
ну и как было в предыдущей машине — этот же код запущенный вне IIS работает безупречно.

скриншот сертификатов:
скрин

проверка сертификата:
скрин2

Код:


Check container succeed        	no errors were detected
Private key container          	
  name                         	websrv3
  unique name                  	REGISTRY\websrv3
  FQCN                         	\.REGISTRYwebsrv3
  container integrity check    	succeed
Exchange key                   	available
  public key export            	succeed
  public key import            	succeed
  signing                      	succeed
  verifying                    	succeed
  exchange key agreement       	succeed
  key export                   	allowed
  algorithm                    	GOST R 34.10-2001 DH
                               	GOST R 34.10-2001, default exchange parameters
                               	GOST R 34.11-94, default parameters
  certificate in container     	match private key
  certificate in store         	My
                               	  E=websrv3, CN=websrv3, OU=websrv3, O=websrv3, L=websrv3, S=websrv3, C=RU
                               		    REGISTRY\websrv3; Crypto-Pro GOST R 34.10-2001 Cryptographic Service Provider; 75
                               	REQUEST
                               	  E=websrv3, CN=websrv3, OU=websrv3, O=websrv3, L=websrv3, C=RU
                               		    REGISTRY\websrv3; Crypto-Pro GOST R 34.10-2001 Cryptographic Service Provider; 75
  certificate name             	websrv3
  subject                      	E=websrv3, CN=websrv3, OU=websrv3, O=websrv3, L=websrv3, S=websrv3, C=RU
  issuer                       	E=support@cryptopro.ru, C=RU, L=Moscow, O=CRYPTO-PRO LLC, CN=CRYPTO-PRO Test Center 2
  valid from                   	Sunday, October 12, 2014 11:07:33 PM
  valid to                     	Monday, October 12, 2015 11:17:33 PM
  serial number                	1200 005E 3F83 E3E1 9085 A458 8F00 0000 005E 3F
Signature key                  	not available
  keys loading                 	succeed

Вверх


Offline

Максим Коллегин

 


#5
Оставлено
:

16 октября 2014 г. 12:45:12(UTC)

Максим Коллегин

Статус: Сотрудник

Группы: Администраторы

Зарегистрирован: 12.12.2007(UTC)
Сообщений: 6,255
Мужчина
Откуда: КРИПТО-ПРО

Сказал «Спасибо»: 21 раз
Поблагодарили: 660 раз в 583 постах

Дайте учетке IIS полный доступ к контейнеру ключей. Обсуждалось неоднократно.

Знания в базе знаний, поддержка в техподдержке


Вверх

WWW


Offline

Максим Коллегин

 


#6
Оставлено
:

13 июля 2015 г. 14:14:59(UTC)

Максим Коллегин

Статус: Сотрудник

Группы: Администраторы

Зарегистрирован: 12.12.2007(UTC)
Сообщений: 6,255
Мужчина
Откуда: КРИПТО-ПРО

Сказал «Спасибо»: 21 раз
Поблагодарили: 660 раз в 583 постах

Написал статью в FAQ

Знания в базе знаний, поддержка в техподдержке


Вверх

WWW


Offline

STP

 


#7
Оставлено
:

24 февраля 2016 г. 10:36:23(UTC)

STP

Статус: Новичок

Группы: Участники

Зарегистрирован: 24.02.2016(UTC)
Сообщений: 2
Российская Федерация
Откуда: Москва

Коллеги, у меня аналогичная проблема
При попытке доступа из webAPP получаю ошибку «Неправильный параметр набора ключей«
Всё настроено по FAQ

ПО:
Win2008R2
КП CSP 3.6.6497 КС2
Rutoken

Замечания:
Поигравшись выяснил, что есть баг в КП CSP 3.6 КС2 при назначении прав на закрытые ключи.
Так как КС2 хранит ключи в службе хранения ключей, назначаю права через оснастку Сертифиакты-ЛокальныйКомпьютер-сертификат-«управление закрытыми ключами». Но после нажатия кнопок «Применить» или «ок» слетают заданные права (при повторном открытии окна с параметрами доступа там дефолтные настройки). При этом если не закрывать оснастку Сертификаты, то webApp начинает работать как должен.

Какие могут быть мыли как это исправить?


Вверх


Offline

Максим Коллегин

 


#8
Оставлено
:

24 февраля 2016 г. 11:53:57(UTC)

Максим Коллегин

Статус: Сотрудник

Группы: Администраторы

Зарегистрирован: 12.12.2007(UTC)
Сообщений: 6,255
Мужчина
Откуда: КРИПТО-ПРО

Сказал «Спасибо»: 21 раз
Поблагодарили: 660 раз в 583 постах

Автор: STP Перейти к цитате

Поигравшись выяснил, что есть баг в КП CSP 3.6 КС2 при назначении прав на закрытые ключи.
Так как КС2 хранит ключи в службе хранения ключей, назначаю права через оснастку Сертифиакты-ЛокальныйКомпьютер-сертификат-«управление закрытыми ключами». Но после нажатия кнопок «Применить» или «ок» слетают заданные права (при повторном открытии окна с параметрами доступа там дефолтные настройки). При этом если не закрывать оснастку Сертификаты, то webApp начинает работать как должен.

Какие могут быть мыли как это исправить?

Какая версия CSP 3.6? В 3.9 пробовали?

Знания в базе знаний, поддержка в техподдержке


Вверх

WWW


Offline

STP

 


#9
Оставлено
:

24 февраля 2016 г. 12:31:31(UTC)

STP

Статус: Новичок

Группы: Участники

Зарегистрирован: 24.02.2016(UTC)
Сообщений: 2
Российская Федерация
Откуда: Москва

Версия КП CSP 3.6.6497 КС2 (СКЗИ 3.6.5359), данная версия нам задана в рамках большего проекта по внедрению уц.
В 3.9 не пробовали.
На предыдущей инсталляции win2003R2 стоит КП 3.6.6497 KC1, там такой проблемы нет.


Вверх


Offline

alexvg75

 


#10
Оставлено
:

14 июня 2016 г. 11:19:09(UTC)

alexvg75

Статус: Новичок

Группы: Участники

Зарегистрирован: 14.06.2016(UTC)
Сообщений: 1
Российская Федерация

Доброго дня!
Столкнулись с той же проблемой. При запуске вебсервера от локального пользователя подписи успешно добавляются, но если настроить запуск вебсервера как службы, то выдает ошибку.
При попытке следовать по шагам описанным для IIS, на этапе установки прав для доступа к закрытым ключам получаем тоже, что описал STP — права сбрасываются.
Windows 7
КриптоПРО 3.9.8001 КС1
Установлен xampp и приложение, которое должно подписывать документы. Нужен запуск приложения через службу, при этом не важно будет ли это апач или IIS.
Было бы очень замечательно, если бы кто-то смог поделился мыслями, опытом, путями обхода. Как я понимаю вся проблема в правах на закрытый ключ.
Заранее большое спасибо за любые идеи.


Вверх

Пользователи, просматривающие эту тему

Guest

Быстрый переход
 

Вы не можете создавать новые темы в этом форуме.

Вы не можете отвечать в этом форуме.

Вы не можете удалять Ваши сообщения в этом форуме.

Вы не можете редактировать Ваши сообщения в этом форуме.

Вы не можете создавать опросы в этом форуме.

Вы не можете голосовать в этом форуме.

Raymond
Raymond

Raymond

articleArticles 549

imageDiagrams 49

codeCode 3

chat_bubble_outlineThreads 8

commentComments 265

loyaltyKontext Points 6055

account_circle Profile


visibility 4,447

event 2021-08-24
access_time 2 years ago
language English

more_vert

When it comes to protect data, one direct way is to encrypt it by adopting certain cryptographic algorithm. In .NET, there are many encryption or decryption algorithms available to use. Like storing secrets in a container with a key, you don’t want to expose that key to anyone else if you want to keep the secrets. In .NET Core or f.NET 5/6, data protection system provides quite a few approaches to secure the private key.

Different ways to secure the keys

On Azure, we can store the keys in Azure Key Vault; in other applications, we can persist the key to a disk that cannot be accessed by others. You can also upload the keys to a database via EntityFramework via DbContext so that only users who have access to the database can access the private key and use it to encrypt or decrypt messages. 

The following is one example:

services.AddDataProtection()
                .PersistKeysToDbContext<ApplicationDbContext>()
                .SetDefaultKeyLifetime(TimeSpan.FromDays(180));

The payload was invalid error

Exceptions can be thrown out using the above code snippet:

System.Security.Cryptography.CryptographicException: The payload was invalid.
         at Microsoft.AspNetCore.DataProtection.Cng.CbcAuthenticatedEncryptor.DecryptImpl(Byte* pbCiphertext, UInt32 cbCiphertext, Byte* pbAdditionalAuthenticatedData, UInt32 cbAdditionalAuthenticatedData)
         at Microsoft.AspNetCore.DataProtection.Cng.Internal.CngAuthenticatedEncryptorBase.Decrypt(ArraySegment`1 ciphertext, ArraySegment`1 additionalAuthenticatedData)
         at Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.UnprotectCore(Byte[] protectedData, Boolean allowOperationsOnRevokedKeys, UnprotectStatus& status)
         at Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.DangerousUnprotect(Byte[] protectedData, Boolean ignoreRevocationErrors, Boolean& requiresMigration, Boolean& wasRevoked)
         at Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.Unprotect(Byte[] protectedData)

Root causes

There are usually two typical causes for the above exception:

  • Private key is not persisted thus if the value was encrypted with a key from one instance, it cannot be decrypted using the current key.
  • Application name is not set.

Since we are persisting the keys to a database, the latter is the root cause.

Fix the problem

To fix the problem, we just need to call SetApplicationName function to ensure the application name is not consistent among different runs. If it is not specified, a random application name will be used and even if the key is the same the payload cannot be decrypted. 

services.AddDataProtection()
                .PersistKeysToDbContext<ApplicationDbContext>()
                .SetApplicationName("YourAppName")
                .SetDefaultKeyLifetime(TimeSpan.FromDays(180));

References

Configure ASP.NET Core Data Protection | Microsoft Docs

I am getting error when the system tries to decrypt the data.

Below is the code I am using to encrypt and decrypt the data. Is there any issue with the code ?

Can anyone help me out in this issue ?

Any help will be appreciated

Stack Trace:

<?xml version=»1.0″ encoding=»utf-16″?>  

<error    host=»localhost»    type=»System.Security.Cryptography.CryptographicException»    

message=»Bad Data. »    

source=»mscorlib»    

detail=»System.Web.HttpUnhandledException: Exception of type ‘System.Web.HttpUnhandledException’ was thrown.

 System.Exception: Bad Data.

System.Security.Cryptography.CryptographicException: Bad Data.  

at System.Security.Cryptography.CryptographicException.ThrowCryptogaphicException(Int32 hr)  

at System.Security.Cryptography.Utils._DecryptData(SafeKeyHandle hKey, Byte[] data, Int32 ib, Int32 cb, Byte[]&amp; outputBuffer, Int32 outputOffset, PaddingMode PaddingMode, Boolean fDone)  

at System.Security.Cryptography.CryptoAPITransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)  

at System.Security.Cryptography.CryptoStream.FlushFinalBlock()  

CODE:

public static string Decrypt(string stringToDecrypt, string sEncryptionKey)

        {

            byte[] IV = { 10, 20, 30, 40, 50, 60, 70, 80 };

            //Private IV() As Byte = {&H12, &H34, &H56, &H78, &H90, &HAB, &HCD, &HEF}

            byte[] inputByteArray;

            try

            {

                byte[] key = Encoding.UTF8.GetBytes(sEncryptionKey.Substring(0, 8));

                DESCryptoServiceProvider des = new DESCryptoServiceProvider();

                inputByteArray = Convert.FromBase64String(stringToDecrypt);

                MemoryStream ms = new MemoryStream();

                CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(key, IV), CryptoStreamMode.Write);

                cs.Write(inputByteArray, 0, inputByteArray.Length);

                cs.FlushFinalBlock();

                Encoding encoding = Encoding.UTF8;

                return encoding.GetString(ms.ToArray());

            }

            catch (Exception ex)

            {

                throw new Exception(ex.Message, ex);

            }

        }

public static string Encrypt(string stringToEncrypt, string sEncryptionKey)

        {

            byte[] IV = { 1, 2, 3, 4, 5, 6, 7, 8 };

            byte[] inputByteArray; //Convert.ToByte(stringToEncrypt.Length)

            try

            {

                byte[] key = Encoding.UTF8.GetBytes(sEncryptionKey.Substring(0, 8));

                DESCryptoServiceProvider des = new DESCryptoServiceProvider();

                inputByteArray = Encoding.UTF8.GetBytes(stringToEncrypt);

                MemoryStream ms = new MemoryStream();

                CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(key, IV), CryptoStreamMode.Write);

                cs.Write(inputByteArray, 0, inputByteArray.Length);

                cs.FlushFinalBlock();

                return Convert.ToBase64String(ms.ToArray());

            }

            catch (Exception ex)

            {

                throw new Exception(ex.Message, ex);

            }

        }

        public static string encryptString(string strToEncrypt)

        {

            UTF8Encoding ue = new UTF8Encoding();

            byte[] bytes = ue.GetBytes(strToEncrypt);

            // encrypt bytes

            MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();

            byte[] hashBytes = md5.ComputeHash(bytes);

            // Convert the encrypted bytes back to a string (base 16)

            string hashString = «»;

            for (int i = 0; i < hashBytes.Length; i++)

            {

                hashString += Convert.ToString(hashBytes[i], 16).PadLeft(2, ‘0’);

            }

            return hashString.PadLeft(32, ‘0’);

        }

Regards,

Parth

In the scenario described in this post, you have encountered an error in your log that says the following:

Signing certificate has not private key or private key is not accessible. Make sure the account running your application has access to the private key

TL;DR; See how to convert your certificate key from CNG to RSA

Or maybe your haven’t seen this error message but have encountered an issue with Identity Server signing it tokens.  If this is the case, I would highly recommend you stop and enable logging before you go any further, restart your application and check your logs for the error message above.  Logging is key, we need to see what is going on in order to know how to fix it! Jump ahead to Enabling Logging in Identity Server V3 then come back to find out what the error is all about.

Assumptions

Now, this article assumes you have checked some of the obvious things.  Run through the following list and if the answer is “Yes!” for every question then you may proceed 🙂

  • Have you checked that the certificate you are using actually has a private key?
  • Have you checked that the password you are using to access the private key is correct?
  • Have you checked that the account your application is running under has permissions to access to the private key?

OK, so you have answered yes to all of these questions – so it is maybe beginning to be more likely that you are falling foul of a Cng Key issue.

What is a CNG Key?

Certificates in Windows are stored using Storage Providers. Windows has two of these providers, that are not compatible. The old style “Cryptographic Service Providers” or CSP in short and the new style “Cryptography API: Next Generation” or CNG. The CNG providers have been around since Windows Vista, and although it is more secure and easier to use many facets of software are still not compatible with CNG providers.  This appears to also include the .NET Framework.

A possible workaround to this may be to use CryptoAPI/CNG API directly to deal with CNG keys.  But if we want an easier and pure .NET solution which understands CNG, we need to find another solution (details to follow!).

How was my PFX file generated?

In this case, I used the powershell module New-SelfSignedCertificate to generate a self-signed certificate and then exported that certificate to a PFX file via the Management Console.

Proving out the issue

So let’s investigate what’s causing the “Signing certificate has not private key or private key is not accessible” error.

To help me figure this out, I created the following test stub which I used to load a certificate (from an embedded resource in this case) and attempt to access the private key.

using System;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using IdentityServer3.Core.Logging;
using Security.Cryptography.X509Certificates;

public class Certificate
{
    private readonly static ILog Logger = LogProvider.For<Certificate>();
    public static X509Certificate2 Load()
    {
        var assembly = typeof(Certificate).Assembly;
        using (var stream = assembly.GetManifestResourceStream("TestApp.MyCert.pfx"))
        {
            var file = Path.Combine(Path.GetTempPath(), "TestApp-" + Guid.NewGuid());
            Logger.DebugFormat("Loading certificate from {0}", file);
            try
            {
                File.WriteAllBytes(file, ReadStream(stream));
                var cert = new X509Certificate2(file, "MyReallyStrongPassword", X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.Exportable);

                Logger.DebugFormat("Certificate Has Private Key: {0}", cert.HasPrivateKey);
                Logger.DebugFormat("Is Private Access Allowed: {0}", IsPrivateAccessAllowed(cert));

                return cert;
            }
            finally
            {
                File.Delete(file);
            }
        }
    }

    private static byte[] ReadStream(Stream input)
    {
        var buffer = new byte[16 * 1024];
        using (var ms = new MemoryStream())
        {
            int read;
            while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
            {
                ms.Write(buffer, 0, read);
            }
            return ms.ToArray();
        }
    }

    public static bool IsPrivateAccessAllowed(X509Certificate2 cert)
    {
        try
        {
            var privateKey = cert.PrivateKey;
            return true;
        }
        catch (CryptographicException ex)
        {
            Logger.ErrorException("Error accessing private key", ex);
            return false;
        }
    }
}

Running this code results in the following entries in my log file

2016-05-11 15:56:45.473 +01:00 [Debug] Loading certificate from "C:WindowsTEMPTestApp-845cfe9c-a0d3-4bc7-bde1-53727c3c81c9"
2016-05-11 15:56:45.495 +01:00 [Debug] Certificate Has Private Key: True
2016-05-11 15:56:45.497 +01:00 [Debug] Is Private Access Allowed: False

Closer inspection of the error thrown when accessing the private key revealed the following exception being thrown.

System.Security.Cryptography.CryptographicException: Invalid provider type specified.
at System.Security.Cryptography.Utils.CreateProvHandle(CspParameters parameters, Boolean randomKeyContainer)
at System.Security.Cryptography.Utils.GetKeyPairHelper(CspAlgorithmType keyType, CspParameters parameters, Boolean randomKeyContainer, Int32 dwKeySize, SafeProvHandle& safeProvHandle, SafeKeyHandle& safeKeyHandle)
at System.Security.Cryptography.RSACryptoServiceProvider.GetKeyPair()
at System.Security.Cryptography.RSACryptoServiceProvider..ctor(Int32 dwKeySize, CspParameters parameters, Boolean useDefaultKeySize)
at System.Security.Cryptography.X509Certificates.X509Certificate2.get_PrivateKey()

Now we seem to be getting somewhere!

GoogleFoo resulted in me discovering Alejandro Magencio’s blog post on this very exception.  His post describes in detail the background to what is going on underneath the covers and why we are getting this exception.

In short, the .NET Framework being used does not like CNG keys.  In order access them we need to either talk directly to the CryptoAPI/CNG API or we could use the CLR Security open source library.

Adding in usage of the CLR Security library allows me to interogate the type of private key that my certificate has.

using System;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using IdentityServer3.Core.Logging;
using Security.Cryptography.X509Certificates;

public class Certificate
{
    private readonly static ILog Logger = LogProvider.For<Certificate>();
    public static X509Certificate2 Load()
    {
        var assembly = typeof(Certificate).Assembly;
        using (var stream = assembly.GetManifestResourceStream("TestApp.MyCert.pfx"))
        {
            var file = Path.Combine(Path.GetTempPath(), "TestApp-" + Guid.NewGuid());
            Logger.DebugFormat("Loading certificate from {0}", file);
            try
            {
                File.WriteAllBytes(file, ReadStream(stream));
                var cert = new X509Certificate2(file, "MyReallyStrongPassword", X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.Exportable);

                Logger.DebugFormat("Certificate Has Private Key: {0}", cert.HasPrivateKey);
                Logger.DebugFormat("HasCngKey: {0}", cert.HasCngKey());
                Logger.DebugFormat("Is Private Access Allowed: {0}", IsPrivateAccessAllowed(cert));

                return cert;
            }
            finally
            {
                File.Delete(file);
            }
        }
    }

    private static byte[] ReadStream(Stream input)
    {
        var buffer = new byte[16 * 1024];
        using (var ms = new MemoryStream())
        {
            int read;
            while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
            {
                ms.Write(buffer, 0, read);
            }
            return ms.ToArray();
        }
    }

    public static bool IsPrivateAccessAllowed(X509Certificate2 cert)
    {
        try
        {
            if (cert.HasCngKey())
            {
                var privateKey = cert.GetCngPrivateKey();
            }
            else
            {
                var privateKey = cert.PrivateKey;
            }
            return true;
        }
        catch (CryptographicException ex)
        {
            Logger.ErrorException("Error accessing private key", ex);
            return false;
        }
    }
}

Which results in the following entries in my log file.

2016-05-11 15:56:45.473 +01:00 [Debug] Loading certificate from "C:WindowsTEMPTestApp-845cfe9c-a0d3-4bc7-bde1-53727c3c81c9"
2016-05-11 15:56:45.495 +01:00 [Debug] Certificate Has Private Key: True
2016-05-11 15:56:45.495 +01:00 [Debug] HasCngKey: True
2016-05-11 15:56:45.497 +01:00 [Debug] Is Private Access Allowed: True

This is great, but I would prefer to not have to code my way out of this.  I would rather have a certificate in a format that can be readily accessed by my .NET code.  So, let’s convert my certificate private key from CNG to RSA.

Converting your certificate key from CNG to RSA

Steps:

  1. Extract your public key and full certificate chain from your PFX file
  2. Extract the CNG private key
  3. Convert the private key to RSA format
  4. Merge public keys with RSA private key to a new PFX file

After changing your application to use the new PFX you just created, you should find that your issues have been resolved.

Now let’s see how to carry out these steps using OpenSSL (Get OpenSSL for Windows from here)

1. Extract your public key and full certificate chain from your PFX file

OpenSSL pkcs12 -in “yourcertificate.pfx” -nokeys -out “yourcertificate.cer” -passin “pass:myreallystrongpassword”

2. Extract the CNG private key

OpenSSL pkcs12 -in “yourcertificate.pfx” -nocerts –out “yourcertificate.pem” -passin “pass:myreallystrongpassword” -passout “pass:myreallystrongpassword”

3. Convert the private key to RSA format

OpenSSL rsa -inform PEM -in “yourcertificate.pem” -out “yourcertificate.rsa” -passin “pass:myreallystrongpassword” -passout “pass:myreallystrongpassword”

4. Merge public keys with RSA private key to a new PFX file

OpenSSL pkcs12 -export -in “yourcertificate.cer” -inkey “yourcertificate.rsa” -out “yourcertificate-converted.pfx” -passin “pass:myreallystrongpassword” -passout “pass:myreallystrongpassword”

Enabling Logging in Identity Server

Identity Server v3 provides a wealth of information to developers through it’s two logging features.  Development time logging provides really low level details to assist developers diagnose issues during development and testing.  Production-time logging comes in the form of a number of events being raised to assist in system monitoring and fault diagnosis.   The reason there are two levels of logging is that it is possible that sensitive information may be exposed through development logging and this facility should therefor always be disabled when the system is in production.

To enable logging with Identity Server v3,

  1. Configure the logging framework of choice. 

For example, you could configure Serilog via the following statement:

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Debug()
    .WriteTo.Trace()
    .CreateLogger();
  1. Configure Identity Server Logging settings

Here is a full list of the logging settings available for Identity Server V3.

var options = new IdentityServerOptions {
                    LoggingOptions = new LoggingOptions()
                    {
                        EnableHttpLogging = true,
                        EnableKatanaLogging = true,
                        EnableWebApiDiagnostics = true,
                        WebApiDiagnosticsIsVerbose = true
                    },
                    EventsOptions = new EventsOptions()
                    {
                        RaiseInformationEvents = true,
                        RaiseErrorEvents = true,
                        RaiseFailureEvents = true,
                        RaiseSuccessEvents = true,
                    }
                };
  1. Enable Diagnostic Logging in your application configuration file

For example

<system.diagnostics>
  <trace autoflush="true" indentsize="4">
    <listeners>
      <add name="myTraceListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="Trace.log" />
      <remove name="Default" />
    </listeners>
  </trace>
</system.diagnostics>

As is also recommended by the developers of Identity Server, I use BareTail to provide me with a great view of my log files.

For more details on Identity Server logging configuration, see the official Identity Server v3 documentation

Now that you have configured logging, build and rerun your application and confirm that a log file is being created.  Then check the log file for error.  You may now wish to return to reading about the certificate error upon which this post is based.

Понравилась статья? Поделить с друзьями:
  • System error code 123
  • System error code 1223
  • System error code 12157
  • System error code 121 превышен таймаут семафора the bat
  • System error code 12057 rarus