1 (2014-10-22 18:07:47 отредактировано Alex)

Тема: Ошибка выполнения расшифрования

Добрый день.
При расшифровании большого количества блоков памяти единожды инициализированным объектом crypt возникает ошибка с кодом 3765436456.
При этом если для каждого блока памяти отдельно инициализировать объект crypt, то расшифрование происходит без ошибок.
Версия СКАД Сигнатуры 3.6. Сборка 265.9.

Может кто-нибудь сталкивался с подобной проблемой?

2

Re: Ошибка выполнения расшифрования

Добрый день,

можно увидеть кусок кода где происходит инициализация и расшифрование?

3

Re: Ошибка выполнения расшифрования

Тестовый пример:
сначала шифрую файлы и записываю в каталог. Затем расшифровываю и снова записываю в другой каталог.
При этом в какой-то момент падает ошибка, описанная в сабже. При повторном запуске расшифрование начинается с того файла, на котором произошла ошибка. При этом расшифрование происходит успешно, и в какой-то момент опять падает на другом файле. Таким образом за несколько проходов удается расшифровать все файлы (их порядка 60, размер файлов не более 200 Кб).
Примечание: если сначала вызвать PrepareFiles(), а затем DecryptFiles() в рамках 2-х разных сессий, то ошибки не возникает.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using OivvsLib;
using Validata.PKI;

namespace CrypterAndSender
{
    /// PrepareFiles - просто шифрует и складывает в каталог
    /// DecryptFiles - расшифровывает и складывает в другой каталог
    class Program
    {
        static void Main(string[] args)
        {
            PrepareFiles();
            DecryptFiles();
        }


        static void PrepareFiles()
        {
            FileMsgReader reader = new FileMsgReader("C:\\temp\\files_in\\", "*.ekr");
            Crypter crypter = new Crypter();
            
            while (reader.HasMessage())
            {
                OivvsMessage msg = reader.PickMessage();
                msg = crypter.Encrypt(msg);
                File.WriteAllBytes("C:\\temp\\files_out\\" + msg.Header() + ".ekr", msg.Text);
                reader.RemoveMessage();
             }
            crypter.Close();
        }

        static void DecryptFiles()
        {
            FileMsgReader reader = new FileMsgReader("C:\\temp\\files_out\\", "*.ekr");
            Crypter decrypter = new Crypter();

            while (reader.HasMessage())
            {
                OivvsMessage msg = reader.PickMessage();
                msg = decrypter.Decrypt(msg);
                File.WriteAllBytes("C:\\temp\\files_decrypt\\" + msg.Header() + ".ekr", msg.Text);
                reader.RemoveMessage();
                
            }
            decrypter.Close();
        }       
    }
}

Класс для инкапсуляции функций шифрования СКАД "Сигнатура":

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Validata.PKI;
using System.IO;

namespace OivvsLib
{
    /// <summary>
    /// Класс для инкапсуляции функций шифрования СКАД "Сигнатура"
    /// </summary>
    public class Crypter
    {
        VcertObject vcert;
        uint _errorCode;
        string _bik;
        string _KeyID;
        string _CertSubject;

        /// <summary>
        /// При создании объекта вызывается инициализации криптографического контекста
        /// Если инициализация пройдет успешно функция ErrorCode этого объекта вернет 0
        /// Если при инциализации возникнут ошибки, функция ErrorCode этого объекта будет возвращать код ошибки
        /// Таким образом, объектом можно пользоваться после создания, только если ErrorCode возвращает 0
        /// </summary>
        public Crypter()
        {
            try
            {
                vcert = new VcertObject();
                vcert.Initialize();
                _errorCode = OivvsException.SUCCESS;
            }
            catch (VcertException e)
            {
                _errorCode = e.Error;
            }
            catch (Exception e)
            {
                _errorCode = OivvsException.UNKNOWN_ERROR;
            }
        }

        /// <summary>
        /// Выполняет деинициализацию криптографического контекста
        /// После вызова этой функции пользоватся объектом нельзя
        /// Метод необходимо вызывать после завершения работы с объектом
        /// </summary>

        public void Close()
        {
            vcert.UnInitialize(0);
        }

        /// <summary>
        /// Выполняет расшифрование сообщения
        /// Возвращает расшифрованное сообщение. В случае возникшей ошибки ErrorCode этого сообщения содержит код ошибки. 
        /// В случае успешного расшифрования ErrorCode объекта сообщения возвращает 0.
        /// </summary>
        /// <param name="msg">Зашифрованное сообщение, остается неизменным</param>
        /// <returns>Расшифрованное сообщение. Сообщение содержит 0 в ErrorCode, в случае успеха. Или код ошибки в противном случае</returns>
        public OivvsMessage Decrypt(OivvsMessage msg)
        {
            try
            {
                DecryptParameters dParams = new DecryptParameters();
                dParams.Flags = DecryptParameters.DecryptFlags.NoFlag;
                OivvsMessage result = new OivvsMessage(msg.Header());
                result.Text = vcert.DecryptMemory(dParams, msg.Text);
                return result;
            }
            catch (VcertException e)
            {
                return new OivvsMessage(e.Error);
            }
            catch (Exception e)
            {
                return new OivvsMessage(OivvsException.UNKNOWN_ERROR);
            }
        }

        /// <summary>
        /// Зашифровывает сообщение
        /// Не изменяет сообщение, переданное в msg. Возвращает зашифрованное сообщение. 
        /// В случае возникшей ошибки ErrorCode этого сообщения содержит код ошибки. 
        /// В случае успешного расшифрования ErrorCode объекта сообщения возвращает 0.
        /// </summary>
        /// <param name="msg">Сообщение подлежащее шифрованию, остается неизменным</param>
        /// <returns>Зашифрованное сообщение. Сообщение содержит 0 в ErrorCode, в случае успеха. Или код ошибки в противном случае</returns>

        public OivvsMessage Encrypt(OivvsMessage msg)
        {
            try
            {
                EncryptParameters eParams = new EncryptParameters();
                eParams.Flags = EncryptParameters.EncryptFlags.NoFlag;
                FindParameters fParams = new FindParameters();
                fParams.Flags = FindParameters.FindFlags.FindMy;
                CertificateCollection certs = vcert.FindCertificates(fParams);
                foreach (Certificate c in certs)
                {
                    eParams.AddReceiver(c);
                }
                
                OivvsMessage result = new OivvsMessage(msg.Header());
                result.Text = vcert.EncryptMemory(eParams, msg.Text);
                return result;
            }
            catch (VcertException e)
            {
                return new OivvsMessage(e.Error);
            }
            catch (Exception e)
            {
                return new OivvsMessage(OivvsException.UNKNOWN_ERROR);
            }
        }
    }
}

4

Re: Ошибка выполнения расшифрования

Добрый день!

Попробуйте запустить следующий пример:

using System;
using System.IO;
using Validata.PKI;

namespace TestVcpia2Decrypt
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                VcertObject vcertCtx = new VcertObject();
                vcertCtx.Initialize();

                string dir1 = "C:\\TestVcpia\\1\\";
                string dir2 = "C:\\TestVcpia\\2\\";
                string dir3 = "C:\\TestVcpia\\3\\";

                // encryption

                EncryptParameters eParams = new EncryptParameters();
                eParams.Flags = EncryptParameters.EncryptFlags.NoFlag;
                FindParameters fParams = new FindParameters();
                fParams.Flags = FindParameters.FindFlags.FindMy;
                CertificateCollection certs = vcertCtx.FindCertificates(fParams);
                foreach (Certificate c in certs)
                {
                    eParams.AddReceiver(c);
                }
                
                for (int i = 1; i < 61; i++)
                {
                    byte[] fileBytes = File.ReadAllBytes(dir1 + i.ToString() + ".data");
                    byte[] encryptedMem = vcertCtx.EncryptMemory(eParams, fileBytes);
                    File.WriteAllBytes(dir2 + i.ToString() + ".data", encryptedMem);
                }

                // decription

                DecryptParameters dParams = new DecryptParameters();
                dParams.Flags = DecryptParameters.DecryptFlags.NoFlag;

                for (int i = 1; i < 61; i++)
                {
                    byte[] encryptedData = File.ReadAllBytes(dir2 + i.ToString() + ".data");
                    byte[] decryptedData = vcertCtx.DecryptMemory(dParams, encryptedData);
                    File.WriteAllBytes(dir3 + i.ToString() + ".data", decryptedData);
                }
            }
            catch (VcertException ex)
            {
                Console.WriteLine("VcertException: " + ex.Message);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

В папке C:\TestVcpia\1 находится 60 файлов с именами 1.data, 2.data, ... , 60.data размером 200 Кб.
У меня тест проходит без ошибок. Напишите, возникнут ли ошибки у Вас.

5

Re: Ошибка выполнения расшифрования

В написанном вами примере ошибок не возникает.
А вот если вызывать отдельно шифрование, а затем расшифрование, то возникает описанная мной ошибка.

static void Main(string[] args)
        {
            try
            {
                encryption();
                decryption();
                Console.WriteLine("Success");
            }
            catch (VcertException ex)
            {
                Console.WriteLine("VcertException: " + ex.Message);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            Console.ReadLine();
        }

        static void encryption()
        {
            VcertObject vcertCtx = new VcertObject();
            vcertCtx.Initialize();

            string dir1 = "C:\\TestVcpia\\1\\";
            string dir2 = "C:\\TestVcpia\\2\\";

            EncryptParameters eParams = new EncryptParameters();
            eParams.Flags = EncryptParameters.EncryptFlags.NoFlag;
            FindParameters fParams = new FindParameters();
            fParams.Flags = FindParameters.FindFlags.FindMy;
            CertificateCollection certs = vcertCtx.FindCertificates(fParams);
            foreach (Certificate c in certs)
            {
                eParams.AddReceiver(c);
            }

            for (int i = 1; i < 61; i++)
            {
                byte[] fileBytes = File.ReadAllBytes(dir1 + i.ToString() + ".data");
                byte[] encryptedMem = vcertCtx.EncryptMemory(eParams, fileBytes);
                File.WriteAllBytes(dir2 + i.ToString() + ".data", encryptedMem);
            }
        }

        static void decryption()
        {
            VcertObject vcertCtx = new VcertObject();
            vcertCtx.Initialize();

            string dir2 = "C:\\TestVcpia\\2\\";
            string dir3 = "C:\\TestVcpia\\3\\";

            DecryptParameters dParams = new DecryptParameters();
            dParams.Flags = DecryptParameters.DecryptFlags.NoFlag;

            for (int i = 1; i < 61; i++)
            {
                byte[] encryptedData = File.ReadAllBytes(dir2 + i.ToString() + ".data");
                byte[] decryptedData = vcertCtx.DecryptMemory(dParams, encryptedData);
                File.WriteAllBytes(dir3 + i.ToString() + ".data", decryptedData);
            }
        }

6

Re: Ошибка выполнения расшифрования

Нашел вероятную причину такого поведения: по всей видимости в какой-то момент происходит удаление из памяти ключа, вследствие чего расшифрование падает с ошибкой. При инициализации установил флаг NoKeyUnload, после чего все заработало без ошибок.

7

Re: Ошибка выполнения расшифрования

Добрый день! Ошибка воспроизводится, будем разбираться. Спасибо.

8

Re: Ошибка выполнения расшифрования

Проблема в следующем: если с одной и той же базой одновременно работать из нескольких контекстов из одного процесса, то при деинициализации первого контекста ключ будет выгружен, и второй контекст будет выдавать ошибки.

В примере ошибка возникает потому, что деинициализация первого контекста (из функции encrypt) вызывается позже, когда запускается сборщик мусора, а в этот момент второй контекст уже инициализирован. То есть при принудительном вызове сборщика мусора ошибок не будет.

    encryption();
    GC.Collect(); // принудительный вызов сборщика мусора
    decryption();

Вообще при работе с одной базой правильнее использовать один контекст. Дело в том, что при инициализации каждого контекста загружается ключ, и если, например, ключ хранится на токене, то пользователю придется каждый раз при инициализации вводить пин код, а если установлен пароль на ключ, то еще и пароль.