Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: WinAPI и системное программирование > Ошибка использования метода CryptDecrypt


Автор: domcom 13.5.2010, 15:54
Привет!
У меня следующая задача.
Есть программа KeyGen, генерирующая строку, которая содержит некую лицензионную информацию. Есть также другая программа П, которая должна эту строку использовать. Строка должна быть зашифрована после генерации, чтобы злоумышленник не смог её самостоятельно изменять, но программа П должна уметь её расшифровать.

Исходники программы П общедоступны, а исходники программы KeyGen закрыты.

Для этого я хочу использовать асимметричный алгоритм шифрования RSA. В закрытых исходниках KeyGen будет прописан (прямо как строка) закрытый пароль, а открытый будет в исходниках программы П.

Я пытаюсь использовать CryptoAPI, но у меня проблемы с использованием CryptDecrypt. И вообще, я не уверен, правильно ли я все делаю!

Код

{ Создаем контекст криптопровайдера. Если такой уже есть - заменяем его }
function GetProvider: HCRYPTPROV;
begin
  CryptAcquireContext(@Result, ContName, nil, PROV_RSA_FULL,
    CRYPT_DELETEKEYSET);
  if not CryptAcquireContext(@Result, ContName, nil, PROV_RSA_FULL,
    CRYPT_NEWKEYSET) then RaiseLastOSError;
end;

{Этот метод вообще не должен быть ни в KeyGen, ни в П. Он просто генерирует пару ключей и возвращает их как строки, которые я потом и пропишу в исходниках обеих программ. Тут я его привожу, чтобы было видно, что я делаю это правильно.}
procedure GenerateKeys(out PrivateKey, PublicKey: string);
var
  Prov: HCRYPTPROV;
  ExchangeKey: HCRYPTKEY;
begin
  Prov := GetProvider;
  try
    if not CryptGenKey(Prov, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, @ExchangeKey) then
      RaiseLastOSError;
    try
      PrivateKey := ExportKey(ExchangeKey, PRIVATEKEYBLOB);
      PublicKey := ExportKey(ExchangeKey, PUBLICKEYBLOB);
    finally
      if not CryptDestroyKey(ExchangeKey) then
        RaiseLastOSError;
    end;
  finally
    if not CryptReleaseContext(Prov, 0) then
      RaiseLastOSError;
  end;
end;

{Экспотрирует либо открытый ключ, либо пару, в зависимости от параметра BlobType. }
function ExportKey(ExchangeKey: HCRYPTKEY; BlobType: Cardinal): string;
var
  Stream: TMemoryStream;
  BufSize: DWORD;
begin
  if not CryptExportKey(ExchangeKey, 0, BlobType, 0, nil, @BufSize) then
    RaiseLastOSError;
  Stream := TMemoryStream.Create;
  try
    Stream.SetSize(BufSize);
    if not CryptExportKey(ExchangeKey, 0, BlobType, 0, PByte(Stream.Memory),
      @BufSize) then RaiseLastOSError;
    SetLength(Result, BufSize);
    Stream.Position := 0;
    Stream.Read(PChar(Result)^, BufSize);
  finally
    Stream.Free;
  end;
end;


{Импорт ключа. Метод "преобразует" жестко прописанную строку в ключ}
function ImportKey(Prov: HCRYPTPROV; Key: string): HCRYPTKEY;
var
  Stream: TMemoryStream;
begin
  Stream:=TMemoryStream.Create;
  try
    Stream.Write(PChar(Key)^, Length(Key));
    Stream.Position := 0;
    if not CryptImportKey(Prov, PByte(Stream.Memory), Stream.Size, 0, 0,
      @Result) then RaiseLastOSError;
  finally
    Stream.Free;
  end;
end;


{Зашифровать лицензионную строку. Метод из KeyGen.}
function Encode(Str: string): string;
var
  Prov: HCRYPTPROV;
  PrivateKey: HCRYPTKEY;
  BufLen: DWORD;
  DataLen: DWORD;
begin
  Prov := GetProvider;
  try
    PrivateKey := ImportKey(Prov, FPrivateKey); // FPrivateKey - это и есть закрытый ключ. Получен в GenerateKeys
    try
      Result := Str;
      DataLen := Length(Result);
      BufLen := DataLen;
      if not CryptEncrypt(PrivateKey, 0, True, 0, nil, @BufLen, 0) then
        RaiseLastOSError;
      SetLength(Result, BufLen);
      if not CryptEncrypt(PrivateKey, 0, True, 0, PByte(Result), @DataLen,
        BufLen) then RaiseLastOSError;
    finally
      if not CryptDestroyKey(PrivateKey) then
        RaiseLastOSError;
    end;
  finally
    if not CryptReleaseContext(Prov, 0) then
      RaiseLastOSError;
  end;
end;

{Расшифровать лицензионную строка. Метод из приложения П.}
function Decode(Str: string): string;
var
  Prov: HCRYPTPROV;
  PublicKey: HCRYPTKEY;
  BufLen: DWORD;
begin
  Prov := GetProvider;
  try
    PublicKey := ImportKey(Prov, FPublicKey); // FPublicKey - это открытый ключ. Получен в GenerateKeys
    try
      Result := Str;
      BufLen := Length(Result);
      if not CryptDecrypt(PublicKey, 0, True, 0, PByte(Result), @BufLen) then
        RaiseLastOSError;
    finally
      if not CryptDestroyKey(PublicKey) then
        RaiseLastOSError;
    end;
  finally
    if not CryptReleaseContext(Prov, 0) then
      RaiseLastOSError;
  end;
end;


И вот на первом методе CryptDecrypt происходит ошибка:

---------------------------
Project1
---------------------------
System Error.  Code: -2146893811.

Ключ не существует.
---------------------------
ОК   
---------------------------


Не подскажете ли, где я что делаю не так?
Спасибо!

Автор: domcom 14.5.2010, 10:31
Да, решение нашел сам.

GetProvider нужно было переписать так:

Код

function GetProvider: HCRYPTPROV;
begin
  if not CryptAcquireContext(@Result, ContName, nil, PROV_RSA_FULL, 0) then
    RaiseLastOSError;
end;



И еще немного изменить Decode:

Код

function Decode(Str: string): string;
var
  Prov: HCRYPTPROV;
  PublicKey: HCRYPTKEY;
  BufLen: DWORD;
begin
  Prov := GetProvider;
  try
    PublicKey := ImportKey(Prov, FPublicKey);
    try
      Result := Str;
      BufLen := Length(Result);
      if not CryptDecrypt(PublicKey, 0, True, 0, PByte(Result), @BufLen) then
        RaiseLastOSError;
      SetLength(Result, BufLen);
    finally
      if not CryptDestroyKey(PublicKey) then
        RaiseLastOSError;
    end;
  finally
    if not CryptReleaseContext(Prov, 0) then
      RaiseLastOSError;
  end;
end;


Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)