Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Delphi Новый топик    Ответить
 Microsoft CrytoAPI и RSA  [new]
alekcvp
Member

Откуда:
Сообщений: 1103
Никто не знает, как работает функция CryptDecrypt в MS CryptoAPI?

Проблема в следующем: мне нужно зашифровать некие данные алгоритмом RSA с длиной ключа 1024 бита, чтобы потом их можно было расшифровать через CryptoAPI. На данный момент имею следующий код (вырезал всё лишнее):

В CryptoAPI генерируется ключевая пара RSA и экспортируется публичный ключ
...
CryptGenKey(FProvider, AT_KEYEXCHANGE, 1024 shl 16, FUserKeyPair); 
CryptExportKey(FUserKeyPair, 0, PUBLICKEYBLOB, 0, @Key[0], BlobSize);
...

Потом выгруженный ключ делится на экспоненту и модуль:
  procedure ExtractPublicKey(Blob: PByte; BlobSize: Integer; E, N: TBigNum);
  var
    PubKeyHdr: TPublicKeyStruc;
    RsaPubKey: TRsaPubKey;
    ModSize: Integer;
  begin
    if BlobSize < SizeOf(RsaPubKey) + SizeOf(PubKeyHdr) then
      raise Exception.Create('Invalid size of PUBLICKEYBLOB.');
    Move(Blob^, PubKeyHdr, SizeOf(PubKeyHdr));
    Inc(Blob, SizeOf(PubKeyHdr));
    Move(Blob^, RsaPubKey, SizeOf(RsaPubKey));
    Inc(Blob, SizeOf(RsaPubKey));
    ModSize := RsaPubKey.bitlen shr 3;
    if ModSize > BlobSize - SizeOf(RsaPubKey) - SizeOf(PubKeyHdr) then
      raise Exception.Create('Invalid size of PUBLICKEYBLOB.');
    E.CopyDigit(RsaPubKey.pubexp);
    N.CopyBytes(Blob, ModSize);
  end;

Т.к. в описании функции CryptDecrypt сказано что она использует PKCS #1 Padding, то реализовал его таким образом:
  function PKCS1Padding(Data: Pointer; Size: Integer; BitLen: Integer): TBytes;
  var
    Bound: Integer;
    Index: Integer;
  begin
    if (BitLen mod 8 > 0) or (Size + 11 > BitLen shr 3) then
      raise Exception.Create('Invalid bit length or data is too large.');
    Bound := BitLen shr 3;
    SetLength(Result, Bound);
    PWord(Result)^ := $0200;
    Dec(Bound, Size);
    for Index := 2 to Bound - 1 do
      Result[Index] := 1 + Byte(Random(255));
    Result[Bound] := $00;
    Move(Data^, Result[Bound + 1], Size);
  end;

Cамо шифрование выглядит как-то так:
var
  Message: string;
  PubKey: TBytes;
  Binary: TBytes;
  MsgNum: TBigNum;
...
  CAPI.PublicKeyExport(PubKey); // CAPI - самописная обёртка над CryptoAPI
  // RSA - класс RSA из библиотеки uCrypto: https://github.com/arkusuma/uCrypto
  ExtractPublicKey(PByte(PubKey), Length(PubKey), RSA.E, RSA.N); 
  Binary := PKCS1Padding(PByte(Message), Length(Message) * SizeOf(Char), 1024);
  MsgNum := TBigNum.CreateBytes(PByte(Binary), Length(Binary));
  try
    Assert(MsgNum.Compare(RSA.N) < 0, 'M must be in range of 0..N-1');
    RSA.Encrypt(MsgNum); // inline-функция для вызова MsgNum.PowMod(E, N);
    ... 

И в итоге при расшифровке текста в этот момент мы получаем NTE_BAD_DATA:
  ...
  if not CryptDecrypt(FUserKeyPair, 0, TRUE, 0, Data, DataSize) then
    RaiseLastOSErrorEx(sFailedDecryption);
  ...


Шифрование и расшифровка что в uCrypto что в CryptoAPI работают нормально, если расшифровывать в той же библиотеке, в какой и шифруется. А вот как их заставить работать между собой? Возможно кто-нибудь знает какие-то нюансы, которые я упустил?

P.S: Шифрование требуется произовдить без CryptoAPI, т.к. в итоге данные могут шифроваться там, где его нет (linux, etc), в данный момент это не рабочее приложение, а испытательный стенд, где я пытаюсь понять как оно вообще работает.
8 май 18, 14:49    [21396005]     Ответить | Цитировать Сообщить модератору
 Re: Microsoft CrytoAPI и RSA  [new]
alekcvp
Member

Откуда:
Сообщений: 1103
Мда, разобрался, данные после PKCS1Padding() нужно было перевернуть...
8 май 18, 15:08    [21396080]     Ответить | Цитировать Сообщить модератору
 Re: Microsoft CrytoAPI и RSA  [new]
makhaon
Member

Откуда: A galaxy far far away
Сообщений: 2843
alekcvp,

дописал бы...
8 май 18, 15:52    [21396238]     Ответить | Цитировать Сообщить модератору
 Re: Microsoft CrytoAPI и RSA  [new]
alekcvp
Member

Откуда:
Сообщений: 1103
makhaon
alekcvp,
дописал бы...


Там нечего дописывать, одну строчку поменять (BE = Big-Endian):
MsgNum := TBigNum.CreateBytesBE(PByte(Binary), Length(Binary));

После этого всё работает. Просто я когда экспериментировал модуль пробовал так загружать, не помогало. А оказывается надо было сообщение перевернуть, т.к. оно по-умолчанию в Big-Endian формате.
8 май 18, 21:38    [21396937]     Ответить | Цитировать Сообщить модератору
Все форумы / Delphi Ответить