Ключевое слово в защите информации
КЛЮЧЕВОЕ СЛОВО
в защите информации
Получить ГОСТ TLS-сертификат для домена (SSL-сертификат)
Добро пожаловать, Гость! Чтобы использовать все возможности Вход или Регистрация.

Уведомление

Icon
Error

7 Страницы<1234>»
Опции
К последнему сообщению К первому непрочитанному
Offline Максим Коллегин  
#11 Оставлено : 18 апреля 2018 г. 18:13:15(UTC)
Максим Коллегин

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

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

Сказал «Спасибо»: 32 раз
Поблагодарили: 704 раз в 613 постах
Ключ получается нормально, но ХML ожидает шифрование в CBC. А CryptEncryptMessage шифрует CFB.
Если указать вот так, то проходит почти расшифровывает.
Код:
EncryptedXml exml = new EncryptedXml(Doc);
exml.Mode = System.Security.Cryptography.CipherMode.CFB;
exml.Padding = System.Security.Cryptography.PaddingMode.None;

Но спотыкается на падинге - не нравится символ 0x6.

Отредактировано пользователем 18 апреля 2018 г. 18:36:51(UTC)  | Причина: Не указана

Знания в базе знаний, поддержка в техподдержке
Offline arkinform  
#12 Оставлено : 18 апреля 2018 г. 18:53:39(UTC)
arkinform

Статус: Участник

Группы: Участники
Зарегистрирован: 10.12.2012(UTC)
Сообщений: 17

Поблагодарили: 2 раз в 2 постах
Цитата:
ХML ожидает шифрование в CBC. А CryptEncryptMessage шифрует CFB.

Вот, это уже прогресс с пониманием проблемы. Можно ли использовать функцию CryptEncryptMessage для шифрования CBC? Если нет, то какие функции проще всего использовать для шифрования CBC?

Цитата:
Но спотыкается на падинге - не нравится символ 0x6

Я использовал паддинг, попробуйте System.Security.Cryptography.PaddingMode = PKCS7
Offline Максим Коллегин  
#13 Оставлено : 18 апреля 2018 г. 19:02:15(UTC)
Максим Коллегин

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

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

Сказал «Спасибо»: 32 раз
Поблагодарили: 704 раз в 613 постах
Для CFB паддинг не разрешает сам .NET.
У нас нет даже оид-идентификатора для ГОСТ 28147 CBC, поэтому в текущий момент CryptEncryptMessage использовать не получится.
Знания в базе знаний, поддержка в техподдержке
Offline Максим Коллегин  
#14 Оставлено : 18 апреля 2018 г. 19:08:35(UTC)
Максим Коллегин

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

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

Сказал «Спасибо»: 32 раз
Поблагодарили: 704 раз в 613 постах
Вижу несколько вариантов решения исходной задачи на уровне CryptoApi
1. Для нативного xmldsig мы используем xmlsec\mscrypto, но xmlenc в нём ни разу не использовали - не факт, что он живой - можете попробовать.
2. Завести новый никем не поддерживаемый идентификатор для ГОСТ CMS с CBC и поддержать работу с ним в нашем криптопровайдере (в 5.0)
3. Собрать xml по вашему сценарию из поста #7
Знания в базе знаний, поддержка в техподдержке
Offline arkinform  
#15 Оставлено : 18 апреля 2018 г. 20:05:35(UTC)
arkinform

Статус: Участник

Группы: Участники
Зарегистрирован: 10.12.2012(UTC)
Сообщений: 17

Поблагодарили: 2 раз в 2 постах
Цитата:
1. Для нативного xmldsig мы используем xmlsec\mscrypto, но xmlenc в нём ни разу не использовали - не факт, что он живой - можете попробовать.

Этот вариант мне не очень нравится, потому что используется какая-то отдельная open source библиотека без полноценной поддержки, нужно собирать самому их исходников и т.п. Последние сборки libxml ftp://ftp.zlatkovic.com/libxml/ от 2011 года. Также мне нужна сборка под Win64, не уверен, что она вообще живая.

Цитата:
2. Завести новый никем не поддерживаемый идентификатор для ГОСТ CMS с CBC и поддержать работу с ним в нашем криптопровайдере (в 5.0)

Мне помимо КриптоПро нужна еще поддержка VipNet + этот вариант явно не самый быстрый, но для будущих поколений было бы полезно :)

Цитата:
3. Собрать xml по вашему сценарию из поста #7

Если вы имеете в виду "использовать низкоуровневые функции шифрования...", то я склоняюсь к этому варианту.
Вы можете дать примеры шифрования CBC низкоуровневыми функциям? Это бы очень помогло.
Offline Максим Коллегин  
#16 Оставлено : 19 апреля 2018 г. 11:30:55(UTC)
Максим Коллегин

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

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

Сказал «Спасибо»: 32 раз
Поблагодарили: 704 раз в 613 постах
Для шифрования CBC ключу 28147 нужно установить:
CryptSetKeyParam( KP_MODE, CRYPT_MODE_CBC)
Знания в базе знаний, поддержка в техподдержке
Offline arkinform  
#17 Оставлено : 24 апреля 2018 г. 20:49:11(UTC)
arkinform

Статус: Участник

Группы: Участники
Зарегистрирован: 10.12.2012(UTC)
Сообщений: 17

Поблагодарили: 2 раз в 2 постах
В итоге я реализовал шифрование XML для сервисов ФСС через низкоуровневые функции Crypto API, поделюсь своим опытом, потому что структурированной информации в интернете по этой теме очень мало.

Далее описываю схему шифрования XML именно для сервисов ФСС (для других, возможно, будет отличаться).

Важные моменты:

- Для шифрования в XML необходимо использовать режим шифрования CBC (высокоуровневые функции CryptEncryptMessage и CryptDecryptMessage этот режим не поддерживают)
- При шифровании необходимо использовать паддинг исходных данных в режиме ISO10126

Зашифрованный XML файл содержит 3 важных сегмента (примеры смотри выше в переписке):

1. Зашифрованные данные (EncryptedData -> KeyInfo -> EncryptedKey -> CipherData)
1.1 "Сырые" зашифрованные данные, закодированные в Base64
1.2 Первые 8 байт - вектор инициализации (IV)

2. Зашифрованный сессионный ключ и параметры шифрования (EncryptedData -> KeyInfo -> EncryptedKey -> CipherData)
2.1 Значение представляет собой ASN.1 структуру GostR3410-KeyTransport, закодированную в Base64
2.2 Внутреннюю структуру можно посмотреть с помощью любого ASN.1 просмотровщика (например, на сайте https://lapo.it/asn1js)
- OCTET STRING (32 byte) - зашифрованный сессионный ключ
- OCTET STRING (4 byte) - MAC сессионного ключа
- OCTET STRING (64 byte) - открытый ключ отправителя
- OCTET STRING (8 byte) - синхропосылка (UKM)

3. Сертификат, которым будет зашифровано ответное сообщение (EncryptedData -> KeyInfo -> EncryptedKey -> KeyInfo)
3.1 Сертификат в формате X.509, закодированный в Base64

Принцип шифрования XML:

- Берем исходный XML файл и сертификат получателя, которым будут зашифрованы данные (в моем случае это сертификат ФСС)
- Экспортируем открытый ключ получателя из сертификата ФСС в PUBLICKEYBLOB
- Генерируем эфемерную ключевую пару (закрытый и открытый ключ отправителя)
- Экспортируем открытый ключ отправителя в PUBLICKEYBLOB и сохраняем последние 64 байта (потребуются для записи в структуру GostR3410-KeyTransport)
- Получаем ключ согласования импортом открытого ключа получателя на эфемерном ключе
- Создаем случайный сессионный ключ CALG_G28147
- Экспортируем созданный сессионный ключ в _CRYPT_SIMPLEBLOB на ключе согласования
- Получаем из структуры _CRYPT_SIMPLEBLOB значение зашифрованного сессионного ключа, MAC и синхропосылку (SV)
- Получаем вектор инициализации сессионного ключа
- Устанавливаем режим шифрования CBC и паддинг ISO10126
- Шифруем данные исходного XML файла блоками по 8 байт
- Добавляем к зашифрованным данным в начало вектор инициализации
- Собираем структуру GostR3410-KeyTransport из зашифрованного сессионного ключа, MAC, синхропосылки и открытого ключа отправителя
- Создаем новый XML файл со структурой EncryptedData (см. примеры выше в переписке)

Принцип расшифровки XML:

- Берем зашифрованный XML файл и парсим из него параметры структуры EncryptedData
- Получаем свой закрытый ключ AT_KEYEXCHANGE для расшифровки
- Собираем PUBLICKEYBLOB из статической части и открытого ключа
- Получаем ключ согласования импортом открытого ключа отправителя на своем закрытом ключе
- Собираем структуру _CRYPT_SIMPLEBLOB из статической части, сессионного ключа, MAC и синхропосылки
- Получаем сессионный ключ импортом зашифрованного сессионного ключа на ключе согласования
- Устанавливаем вектор инициализации
- Устанавливаем режим шифрования CBC и паддинг ISO10126
- Расшифровываем данные блоками по 8 байт и получаем исходный XML файл

Пример шифрования буфера низкоуровневыми функциями на Delphi (предполагается, что дальнейшую упаковку параметров в структуру GostR3410-KeyTransport и создание зашифрованного XML читатель сможет выполнить самостоятельно):

Код:

function TCryptoApiController.EncryptData(
      const ACertificate: AnsiString; // сертификат для шифрования в формате DER
      const ASourceData: AnsiString; // исходные данные
      out APublicKey: AnsiString; // открытый ключ отправителя
      out ASessionKey: AnsiString; // зашифрованный сессионный ключ
      out ASessionSV: AnsiString; // синхропосылка (UKM)
      out ASessionMAC: AnsiString; // MAС сессионного ключа
      out AInitVector: AnsiString; // вектор инициализации
      const ACipherMode: TCryptoCipherMode = ccm_CBC; // режим шифрования
      const APaddingMode: TCryptoPaddingMode = cpm_ISO10126 // режим паддинга
    ): AnsiString;
const
  ENCRYPT_BLOCK_LENGTH: DWORD = 8;
var
  FCryptProv: HCRYPTPROV;
  FCertContext: PCCERT_CONTEXT;
  FPublicKey: HCRYPTKEY;
  FSessionKeyBlob: AnsiString;
  FSenderPublicKeyBlob: AnsiString;
  FRecipientPublicKeyBlob: AnsiString;
  FEphemeralKey: HCRYPTKEY;
  FAgreeKey: HCRYPTKEY;
  FKeyLenNeed: DWORD;
  FSessionKey: HCRYPTKEY;
  FSourcePos: DWORD;
  FSourceDataLen: DWORD;
  FEncryptBuffer: AnsiString;
  FEncryptDataLen: DWORD;
  FEncryptBufferLen: DWORD;
  FEncryptFinal: LongBool;
begin
  CryptoInitialize;
  try
    CryptoLogMessage(CLM_ENCRYPT_DATA);

    AcquireContext(@FCryptProv, nil, CRYPT_VERIFYCONTEXT or CRYPT_SILENT, '');
    try

      // получение сертификата
      FCertContext := FCertCreateCertificateContext(PKCS_7_ASN_ENCODING or X509_ASN_ENCODING, @ACertificate[1], Length(ACertificate));

      if FCertContext = nil then
        RaiseLastCryptoApiError;

      try
        // импорт информации по открытому ключу
        if FCryptImportPublicKeyInfoEx(FCryptProv, PKCS_7_ASN_ENCODING or X509_ASN_ENCODING, @FCertContext.pCertInfo.SubjectPublicKeyInfo, 0, 0, nil, @FPublicKey) then
        try

          // экспорт открытого ключа получателя в BLOB
          if FCryptExportKey(FPublicKey, 0, PUBLICKEYBLOB, 0, nil, @FKeyLenNeed) then
          begin
            SetLength(FRecipientPublicKeyBlob, FKeyLenNeed);

            if not FCryptExportKey(FPublicKey, 0, PUBLICKEYBLOB, 0, @FRecipientPublicKeyBlob[1], @FKeyLenNeed) then
              RaiseLastCryptoApiError;
          end;

        finally
          if not FCryptDestroyKey(FPublicKey) then
            RaiseLastCryptoApiError;
        end

        else
          RaiseLastCryptoApiError;

      finally
        if FCertContext <> nil then
          FCertFreeCertificateContext(FCertContext);
      end;

      // генерация эфемерной ключевой пары
      if FCryptGenKey(FCryptProv, CALG_DH_EL_EPHEM, CRYPT_EXPORTABLE, @FEphemeralKey) then
      try

        // экспорт открытого ключа отправителя в BLOB
        if FCryptExportKey(FEphemeralKey, 0, PUBLICKEYBLOB, 0, nil, @FKeyLenNeed) then
        begin
          SetLength(FSenderPublicKeyBlob, FKeyLenNeed);

          if not FCryptExportKey(FEphemeralKey, 0, PUBLICKEYBLOB, 0, @FSenderPublicKeyBlob[1], @FKeyLenNeed) then
            RaiseLastCryptoApiError;

          // получаем значение открытого ключа отправителя из PUBLICKEYBLOB
          APublicKey := Copy(FSenderPublicKeyBlob, Length(FSenderPublicKeyBlob) - 64 + 1, 64);
        end;

        // получение ключа согласования импортом открытого ключа получателя
        // на эфемерном ключе
        if FCryptImportKey(FCryptProv, @FRecipientPublicKeyBlob[1], Length(FRecipientPublicKeyBlob), FEphemeralKey, 0, @FAgreeKey) then
        try

          // установление PRO_EXPORT алгоритма ключа согласования
          if not FCryptSetKeyParam(FAgreeKey, KP_ALGID, @CALG_PRO_EXPORT, 0) then
            RaiseLastCryptoApiError;

          // создание случайного сессионного ключа
          if FCryptGenKey(FCryptProv, CALG_G28147, CRYPT_EXPORTABLE, @FSessionKey) then
          try

            // экспорт сессионного ключа в BLOB
            if FCryptExportKey(FSessionKey, FAgreeKey, SIMPLEBLOB, 0, nil, @FKeyLenNeed) then
            begin
              SetLength(FSessionKeyBlob, FKeyLenNeed);

              // экспорт сессионного ключа на ключе согласования
              if not FCryptExportKey(FSessionKey, FAgreeKey, SIMPLEBLOB, 0, @FSessionKeyBlob[1], @FKeyLenNeed) then
                RaiseLastCryptoApiError;

              (*
              FSessionKeyBlob = CRYPT_SIMPLEBLOB
                bType               : offset 1,  size 1
                bVersion            : offset 2,  size 1
                reserved            : offset 3,  size 2
                aiKeyAlg            : offset 5,  size 4
                Magic               : offset 9,  size 4
                EncryptKeyAlgId     : offset 13, size 4
                bSV (bUKM)          : offset 17, size 8
                bEncryptedKey       : offset 25, size 32
                bMacKey             : offset 57, size 4
                bEncryptionParamSet : offset 61
              end;
              *)

              ASessionSV  := Copy(FSessionKeyBlob, 17, 8);
              ASessionKey := Copy(FSessionKeyBlob, 25, 32);
              ASessionMAC := Copy(FSessionKeyBlob, 57, 4);
            end

            else
              RaiseLastCryptoApiError;

            // получение вектора инициализации
            if FCryptGetKeyParam(FSessionKey, KP_IV, nil, @FKeyLenNeed, 0) then
            begin
              SetLength(AInitVector, FKeyLenNeed);

              if not FCryptGetKeyParam(FSessionKey, KP_IV, @AInitVector[1], @FKeyLenNeed, 0) then
                RaiseLastCryptoApiError;

            end else
              RaiseLastCryptoApiError;

            // зашифрованные данные
            Result := '';

            // режим шифрования CBC
            if not FCryptSetKeyParam(FSessionKey, KP_MODE, @CryptoCipherModeConst[ACipherMode], 0) then
              RaiseLastCryptoApiError;

            // режим паддинга
            if not FCryptSetKeyParam(FSessionKey, KP_PADDING, @CryptoPaddingModeConst[APaddingMode], 0) then
              RaiseLastCryptoApiError;

            FSourceDataLen := Length(ASourceData);
            FSourcePos := 1;

            // шифрование блоками по 8 байт
            while FSourcePos <= FSourceDataLen do
            begin
              // временный буфер с исходными данными для шифрования
              FEncryptBuffer := Copy(ASourceData, FSourcePos, ENCRYPT_BLOCK_LENGTH);
              FEncryptBufferLen := Length(FEncryptBuffer);
              FEncryptDataLen := FEncryptBufferLen;

              // последний блок данных
              FEncryptFinal := (FSourcePos + ENCRYPT_BLOCK_LENGTH) > FSourceDataLen;

              // получение размера буфера с учетом возможного паддинга
              if not FCryptEncrypt(FSessionKey, 0, FEncryptFinal, 0, nil, FEncryptBufferLen, 0) then
                RaiseLastCryptoApiError;

              // установка размера буфера
              SetLength(FEncryptBuffer, FEncryptBufferLen);

              // шифрование буфера данных
              if not FCryptEncrypt(FSessionKey, 0, FEncryptFinal, 0, @FEncryptBuffer[1], FEncryptDataLen, FEncryptBufferLen) then
                RaiseLastCryptoApiError;

              // накопление зашифрованных данных
              Result := Result + FEncryptBuffer;

              // переход к следующему блоку
              FSourcePos := FSourcePos + ENCRYPT_BLOCK_LENGTH;
            end

          finally
            if not FCryptDestroyKey(FSessionKey) then
              RaiseLastCryptoApiError;
          end

          else
            RaiseLastCryptoApiError;

        finally
          if not FCryptDestroyKey(FAgreeKey) then
            RaiseLastCryptoApiError;
        end

        else
          RaiseLastCryptoApiError;

      finally
        if not FCryptDestroyKey(FEphemeralKey) then
          RaiseLastCryptoApiError;
      end

      else
        RaiseLastCryptoApiError;

    finally
      ReleaseContext(FCryptProv);
    end

  finally
    CryptoFinalize;
  end;
end;



Пример расшифровки буфера низкоуровневыми функциями на Delphi (предполагается, что предварительный парсинг зашифрованного XML и распаковку параметров из структуры GostR3410-KeyTransport читатель сможет выполнить самостоятельно):

Код:

function TCryptoApiController.DecryptData(
      const AKeyID: AnsiString; // идентификатор контейнера
      const AUserPIN: AnsiString;  // PIN код
      const AEncryptedData: AnsiString; // зашифрованные данные
      const APublicKey: AnsiString; // открытый ключ отправителя
      const ASessionKey: AnsiString; // зашифрованный сессионный ключ
      const ASessionSV: AnsiString; // синхропосылка (UKM)
      const ASessionMAC: AnsiString; // MAС сессионного ключа
      const AInitVector: AnsiString; // вектор инициализации
      const ACipherMode: TCryptoCipherMode = ccm_CBC; // режим шифрования
      const APaddingMode: TCryptoPaddingMode = cpm_ISO10126 // режим паддинга
    ): AnsiString;
const
  DECRYPT_BLOCK_LENGTH: DWORD = 8;
var
  FCryptProv: HCRYPTPROV;
  FPublicKeyBlob: AnsiString;
  FSessionKeyBlob: AnsiString;
  FPrivateKey: HCRYPTKEY;
  FAgreeKey: HCRYPTKEY;
  FSessionKey: HCRYPTKEY;
  FSourcePos: DWORD;
  FSourceDataLen: DWORD;
  FDecryptBuffer: AnsiString;
  FDecryptBufferLen: DWORD;
  FDecryptFinal: LongBool;
begin
  if AKeyID = '' then
    RaiseCryptoException(CLE_CKA_ID_IS_EMPTY);

  CryptoInitialize;
  try
    CryptoLogMessage(CLM_DECRYPT_DATA);

    AcquireContext(@FCryptProv, PWideChar(WideString(AKeyID)), CRYPT_SILENT, AUserPIN);
    try

      // получение закрытого ключа
      if FCryptGetUserKey(FCryptProv, AT_KEYEXCHANGE, @FPrivateKey) then
      try

        // сборка PublicKey BLOB из статической части и открытого ключа
        FPublicKeyBlob :=
          #$06#$20#$00#$00#$23#$2E#$00#$00#$4D#$41#$47#$31#$00#$02#$00#$00#$30#$12 +
          #$06#$07#$2A#$85#$03#$02#$02#$24#$00#$06#$07#$2A#$85#$03#$02#$02#$1E#$01 +
          APublicKey;

        // получение ключа согласования импортом открытого ключа отправителя
        // на закрытом ключе
        if FCryptImportKey(FCryptProv, @FPublicKeyBlob[1], Length(FPublicKeyBlob), FPrivateKey, 0, @FAgreeKey) then
        try

          // установление PRO_EXPORT алгоритма ключа согласования
          if not FCryptSetKeyParam(FAgreeKey, KP_ALGID, @CALG_PRO_EXPORT, 0) then
            RaiseLastCryptoApiError;

          (*
          FSessionKeyBlob = CRYPT_SIMPLEBLOB
            bType               : offset 1,  size 1
            bVersion            : offset 2,  size 1
            reserved            : offset 3,  size 2
            aiKeyAlg            : offset 5,  size 4
            Magic               : offset 9,  size 4
            EncryptKeyAlgId     : offset 13, size 4
            bSV (bUKM)          : offset 17, size 8
            bEncryptedKey       : offset 25, size 32
            bMacKey             : offset 57, size 4
            bEncryptionParamSet : offset 61
          end;
          *)

          // сборка SessionKey BLOB из статической части и параметров сессионного ключа
          FSessionKeyBlob :=
            #$01#$20#$00#$00#$1E#$66#$00#$00#$FD#$51#$4A#$37#$1E#$66#$00#$00 +
            ASessionSV + ASessionKey + ASessionMAC +
            #$30#$09#$06#$07 + // ASN.1 Sequence + OID Header
            #$2A#$85#$03#$02#$02#$1F#$01; // OID_GOST_R28147_89_CryptoPro_A_ParamSet 1.2.643.2.2.31.1

          // получение сессионного ключа импортом зашифрованного сессионного ключа
          // на ключе согласования
          if FCryptImportKey(FCryptProv, @FSessionKeyBlob[1], Length(FSessionKeyBlob), FAgreeKey, 0, @FSessionKey) then
          try
            // расшифрованные данные
            Result := '';

            // установка вектора инициализации
            if not FCryptSetKeyParam(FSessionKey, KP_IV, @AInitVector[1], 0) then
              RaiseLastCryptoApiError;

            // режим шифрования CBC
            if not FCryptSetKeyParam(FSessionKey, KP_MODE, @CryptoCipherModeConst[ACipherMode], 0) then
              RaiseLastCryptoApiError;

            // режим паддинга
            if not FCryptSetKeyParam(FSessionKey, KP_PADDING, @CryptoPaddingModeConst[APaddingMode], 0) then
              RaiseLastCryptoApiError;

            FSourceDataLen := Length(AEncryptedData);
            FSourcePos := 1;

            // расщифровка блоками по 8 байт
            while FSourcePos <= FSourceDataLen do
            begin
              // временный буфер с исходными данными для расшифровки
              FDecryptBuffer := Copy(AEncryptedData, FSourcePos, DECRYPT_BLOCK_LENGTH);
              FDecryptBufferLen := Length(FDecryptBuffer);

              // последний блок данных
              FDecryptFinal := (FSourcePos + DECRYPT_BLOCK_LENGTH) > FSourceDataLen;

              // расшифровка буфера данных
              if not FCryptDecrypt(FSessionKey, 0, FDecryptFinal, 0, @FDecryptBuffer[1], FDecryptBufferLen) then
                RaiseLastCryptoApiError;

              // обновление размера буфера на реальный размер расшифрованных данных
              SetLength(FDecryptBuffer, FDecryptBufferLen);

              // накопление рашифрованных данных
              Result := Result + FDecryptBuffer;

              // переход к следующему блоку
              FSourcePos := FSourcePos + DECRYPT_BLOCK_LENGTH;
            end

          finally
            if not FCryptDestroyKey(FSessionKey) then
              RaiseLastCryptoApiError;
          end

          else
            RaiseLastCryptoApiError;

        finally
          if not FCryptDestroyKey(FAgreeKey) then
            RaiseLastCryptoApiError;
        end

        else
          RaiseLastCryptoApiError;

      finally
        if not FCryptDestroyKey(FPrivateKey) then
          RaiseLastCryptoApiError;
      end

      else
        RaiseLastCryptoApiError;

    finally
      ReleaseContext(FCryptProv);
    end

  finally
    CryptoFinalize;
  end;
end;

thanks 1 пользователь поблагодарил arkinform за этот пост.
GreyGoblin оставлено 28.09.2018(UTC)
Offline arkinform  
#18 Оставлено : 27 апреля 2018 г. 18:35:24(UTC)
arkinform

Статус: Участник

Группы: Участники
Зарегистрирован: 10.12.2012(UTC)
Сообщений: 17

Поблагодарили: 2 раз в 2 постах
Замечания по шифрованию XML через VipNet CSP

Описанные мной принципы шифрования и примеры в целом работоспособны в VipNet CSP, но есть одна проблема - судя по всему VipNet CSP не поддерживает режим паддинга ISO10126_PADDING. Для шифрования запроса в ФСС это обходится указанием KP_PADDING = PKCS5_PADDING, сервис ФСС принимает такие запросы, а вот с расшифровкой сложнее. Зашифрованный ответ от ФСС не расшифровывается через VipNet CSP из-за ошибки вызова CryptDecrypt на последнем блоке (Final = true). Сами данные успешно расшифровываются, то функция явно выдает ошибку на этапе обработки паддинга. Аналогичная проблема описана на форуме в теме https://www.cryptopro.ru...aspx?g=posts&m=53984 , но правильнее не просто игнорировать данную ошибку, а обработать паддинг вручную:
Код:

...
// расшифровка буфера данных
if not FCryptDecrypt(FSessionKey, 0, FDecryptFinal, 0, @FDecryptBuffer[1], FDecryptBufferLen) then
begin
  // ручная обработка паддинга для VipNet CSP
  // https://www.cryptopro.ru/forum2/default.aspx?g=posts&m=53984
  if (ProviderType = cpt_CSP_VipNet_GOST) and FDecryptFinal and
     (APaddingMode in [cpm_PKCS5, cpm_ISO10126, cpm_X923]) and (FDecryptBufferLen = DECRYPT_BLOCK_LENGTH) then
  begin
    // количество байт паддинга в последнем байте блока
    FDecryptBufferLen := FDecryptBufferLen - Ord(FDecryptBuffer[FDecryptBufferLen]);
  end

  else
    RaiseLastCryptoApiError;
end;
...
thanks 1 пользователь поблагодарил arkinform за этот пост.
Андрей * оставлено 27.04.2018(UTC)
Offline not_x  
#19 Оставлено : 29 мая 2018 г. 5:06:31(UTC)
not_x

Статус: Участник

Группы: Участники
Зарегистрирован: 27.06.2017(UTC)
Сообщений: 16
Российская Федерация

В КриптоПро CSP 3.6.7777 ISO10126_PADDING почему-то выдаёт ошибку на последнем блоке (CryptEncrypt( ..., ..., FEncryptFinal=1,...), ссоветственно использовалось PKCS5_PADDING (выставлено по умолчанию http://cpdn.cryptopro.ru...0278e45eb1994c38161.html )

Шифрование/расшифрование можно проводить не деля данные по блокам. Соответсвенно, вместо:


...
// шифрование блоками по 8 байт
while FSourcePos <= FSourceDataLen do
begin
...
end

Достаточно примерно так:
FEncryptBuffer := Copy(ASourceData, 1, FSourceDataLen)

// получение размера буфера с учетом возможного паддинга всего буфера
if not FCryptEncrypt(FSessionKey, 0, 1, 0, nil, FSourceDataLen, 0) then
RaiseLastCryptoApiError;

// установка размера буфера
SetLength( FEncryptBuffer, FSourceDataLen);

// шифрование буфера данных
if not FCryptEncrypt(FSessionKey, 0, 1, 0, @FEncryptBuffe[1], FSourceDataLen, FSourceDataLen) then
RaiseLastCryptoApiError;



При расшифровании вместо:
...
// расщифровка блоками по 8 байт
while FSourcePos <= FSourceDataLen do
begin
...
end

соотвественно:
FEncryptBuffer := Copy(ASourceData, 1, FSourceDataLen)

if not FCryptDecrypt(FSessionKey, 0, 1, 0, @FDecryptBuffer[1], FSourceDataLe) then
RaiseLastCryptoApiError;

SetLength(FDecryptBufferr, FSourceDataLe);

Отредактировано пользователем 29 мая 2018 г. 6:44:53(UTC)  | Причина: Не указана

Offline not_x  
#20 Оставлено : 29 мая 2018 г. 10:30:26(UTC)
not_x

Статус: Участник

Группы: Участники
Зарегистрирован: 27.06.2017(UTC)
Сообщений: 16
Российская Федерация

В КриптоПро CSP 3.6.7777 ISO10126_PADDING дополнение (padding) при шифрование нужно формировать вручную и добавлять к данным (соответственно CryptEncrypt для "получения размера буфера с учетом возможного паддинга" бессмысленно - значения SourceDataLen всегда = 0). Похоже, что аналогичная картина в VipNet CSP
RSS Лента  Atom Лента
Пользователи, просматривающие эту тему
7 Страницы<1234>»
Быстрый переход  
Вы не можете создавать новые темы в этом форуме.
Вы не можете отвечать в этом форуме.
Вы не можете удалять Ваши сообщения в этом форуме.
Вы не можете редактировать Ваши сообщения в этом форуме.
Вы не можете создавать опросы в этом форуме.
Вы не можете голосовать в этом форуме.