Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Delphi Новый топик    Ответить
Топик располагается на нескольких страницах: [1] 2 3 4 5 6   вперед  Ctrl      все
 Как сделать цифровую подпись на основе сертификата  [new]
LimonFX
Member

Откуда:
Сообщений: 77
Сорри несколько за глупый вопрос. Просто проект нужно сдать в понедельник. Время поджимает и реально не успеваю.
Что нужно:
на основе сертификата (выбирается вручную из установленных) подписать файл цифровой подписью, а потом проверить эту самую подпись. Стало быть как сделать? Если кто-нибудь кинет рабочим примером на Дельфи, буду очень благодарен.
2 сен 07, 11:16    [4609924]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
Вам ответил на форуме КриптоПро Кирил Соболев... а это наверное единственный человек... отвечающий на вопросы сторонних разработчиков... использующих продукты борланда
И ответил правильно... дал сылку на sample в си-исходниках... казалось бы, что еще требуется специалисту... вы же начали клянчить код на паскале...
Что могу добавить еще по теме... вам требуется научиться пользоваться минимальным набором функций "Simplified Message Functions" из крипто раздела msdn

зы: или назвать сумму вечнозеленых... с которой вы готовы расстаться... чтобы другие люди писали за вас код
2 сен 07, 13:31    [4610051]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
LimonFX
Member

Откуда:
Сообщений: 77
На форуме КриптоПро вопрос задавал человек, который ушел в отпуск. А эту задачу теперь повесили на меня. Я с такими вещами никогда не работал и поэтому решил написать здесь. CryptoAPI я сейчас активно изучаю, но и еще хотелось бы посмотреть на код-пример.
2 сен 07, 14:17    [4610103]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
тогда остается только повторить... что исходники примеров растут тут:
http://www.cryptopro.ru/cryptopro/products/csp/20/sample-2-0.zip
раздел msdn по CryptoAPI можно почитать тут:
http://msdn2.microsoft.com/en-us/library/aa380252.aspx

CryptoAPI message functions consist of two groups of functions: low-level message functions and simplified message functions.

Simplified message functions are at a higher level and wrap several low-level message functions and certificate functions into single functions that perform a specific task in a specific manner. These functions reduce the number of function calls needed to accomplish a task, thereby simplifying CryptoAPI use. For an overview of simplified messages, see Simplified Messages.

а из них только вот эти две:
CryptSignMessage
CryptVerifyDetachedMessageSignature (CryptVerifyMessageSignature)

код за вас я писать не буду, но на конкретные вопросы (подкрепленные самостоятельно написанным кодом) отвечу
2 сен 07, 14:49    [4610137]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
LimonFX
Member

Откуда:
Сообщений: 77
Добрался до функции
function CertFindCertificateInStore(hCertStore :HCERTSTORE;
dwCertEncodingType :DWORD;
dwFindFlags :DWORD;
dwFindType :DWORD;
const pvFindPara :PVOID;
pPrevCertContext :PCCERT_CONTEXT
):PCCERT_CONTEXT ; stdcall;

Не моуг понять что должно быть указано в параметре pvFindPara? Знаю что это должно быть название сертификата. Пробовал искать его в личных сертификатах, но где оно там написано не понял. Там есть инфа о том кому выдали сертификат, кто выдал, когда выдали и т.д.
3 сен 07, 11:53    [4611814]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
MAX2002
Member

Откуда:
Сообщений: 180
может эта статейка тебе поможет?
3 сен 07, 14:46    [4613069]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
LimonFX
Добрался до функции
function CertFindCertificateInStore(hCertStore :HCERTSTORE;
dwCertEncodingType :DWORD;
dwFindFlags :DWORD;
dwFindType :DWORD;
const pvFindPara :PVOID;
pPrevCertContext :PCCERT_CONTEXT
):PCCERT_CONTEXT ; stdcall;

Не моуг понять что должно быть указано в параметре pvFindPara? Знаю что это должно быть название сертификата. Пробовал искать его в личных сертификатах, но где оно там написано не понял. Там есть инфа о том кому выдали сертификат, кто выдал, когда выдали и т.д.


Вот это уже более конструктивный разговор....
http://msdn2.microsoft.com/en-us/library/aa376064.aspx
Я не совсем понял, что подразумевается под под "названием сертификата", у него куча атрибутов, но предположим, что это CERT_FIND_SUBJECT_STR для dwFindType... msdn однозначно говорит, что pvFindPara для CERT_FIND_SUBJECT_STR должен быть data type: null-terminated Unicode string... т.е.

var
  wsFind : WideString;
  ...
begin
  ...
  wsFind := 'find';
  m_pHandle := CertFindCertificateInStore(...., CERT_FIND_SUBJECT_STR, PWideString( wsFind ), ... );
  ...
3 сен 07, 15:19    [4613307]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
LimonFX
Member

Откуда:
Сообщений: 77
C предыдущим вопросом я уже разобрался - сертификат находит. Теперь другая проблема. Функция CertNameToStr возвращает не нулевое значение, а вот Cert_name почему-то пустой.

........
  hStoreHandle := CertOpenSystemStore(0, PChar(CERT_STORE_NAME));
........
  enctype := PKCS_7_ASN_ENCODING or X509_ASN_ENCODING;
  pSignerCert:= CertFindCertificateInStore(hStoreHandle,enctype,0,CERT_FIND_SUBJECT_STR,pSignerName,nil);
  if pSignerCert <> nil then
    SignSize := CertNameToStr(encType, @pSignerCert.pCertInfo.Issuer , CERT_SIMPLE_NAME_STR, [b]Cert_name[/b], 512);
  Memo.Lines.Add(IntToStr(SignSize));
  Memo.Lines.Add([b]Cert_name[/b]);
3 сен 07, 16:11    [4613668]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
__Avenger__
Member

Откуда:
Сообщений: 1971
Может данная статья поможет?
3 сен 07, 16:17    [4613715]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
LimonFX
Member

Откуда:
Сообщений: 77
Сам нашел свою ошибку. Забыл вот эту строчку написать в коде Cert_name:= StrAlloc(2048); :)

Альт
Допустим я вытащил цифровую подпись из сертификата. Дальше мне нужно проверить подписанный файл. Какую из след. функции мне использовать, в чем их разница?
CryptSignMessage или CryptVerifyDetachedMessageSignature
3 сен 07, 17:58    [4614507]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
LimonFX
Сам нашел свою ошибку. Забыл вот эту строчку написать в коде Cert_name:= StrAlloc(2048); :)
Допустим я вытащил цифровую подпись из сертификата. Дальше мне нужно проверить подписанный файл. Какую из след. функции мне использовать, в чем их разница?
CryptSignMessage или CryptVerifyDetachedMessageSignature


Плохо искал ошибку... узнай, что такое "Retrieving Data of Unknown Length"... пройдет желание писать "StrAlloc(2048)" ))
Самое забавное, что ничего тебе уже не надо... ты поймал контекст сертификата, а именно с ним и работают сипл-функции
Первая подписывает... вторая проверяет detached подписи
3 сен 07, 19:13    [4614846]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
LimonFX
Member

Откуда:
Сообщений: 77
Со StrAlloc потом буду разбираться, не столь важно это сейчас.
На данный момент EXEшник собирается и даже работает, но результатом проверки подписи является такая вот ошибка NTE_BAD_ALGID. Вроде как не нравится используемый алгоритм. Хотелось бы узнать на что нужно в первую очередь обратить внимание, чтобы узнать причину этой ошибки?
4 сен 07, 12:48    [4617206]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Dimitry Sibiryakov
Member

Откуда:
Сообщений: 46121

NTE_BAD_ALGID The hHash handle specifies an algorithm that this CSP does
not support.

Покажи, какие константы CALG_* использовал?

Posted via ActualForum NNTP Server 1.4

4 сен 07, 12:58    [4617312]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
LimonFX
Member

Откуда:
Сообщений: 77
ALG_ID нигде не использовал. Где его нужно указывать?
4 сен 07, 14:01    [4617879]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
LimonFX
Со StrAlloc потом буду разбираться, не столь важно это сейчас.
На данный момент EXEшник собирается и даже работает, но результатом проверки подписи является такая вот ошибка NTE_BAD_ALGID. Вроде как не нравится используемый алгоритм. Хотелось бы узнать на что нужно в первую очередь обратить внимание, чтобы узнать причину этой ошибки?


А как именно выполняется проверка? Например CRYPT_VERIFY_MESSAGE_PARA обязательно должна быть предварительно занулена и потом наполнена... размер cbSize должен быть правильным... подкрепляйте вопрос кодом, гадание на кофейной гуще утомляет...

для CryptVerify*
NTE_BAD_ALGID The message was hashed and signed by using an unknown or unsupported algorithm.

Как вариант... в системе отсутствует необходимый провайдер
4 сен 07, 14:03    [4617898]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
LimonFX
Member

Откуда:
Сообщений: 77
.......  
VerifyPara.cbSize := SizeOf(CRYPT_VERIFY_MESSAGE_PARA);
  VerifyPara.dwMsgAndCertEncodingType := enctype;
  VerifyPara.hCryptProv := hProv;
  VerifyPara.pfnGetSignerCertificate := @MySignerCertificateCallback;
  VerifyPara.pvGetArg := pSignerCert;


  if (dlgOpenSign.Execute) and (dlgOpenSign.FileName <> EmptyStr) then
  begin
//файл с подписью
    AssignFile(f1,dlgOpenSign.FileName);
    Reset(f1, 1);
    SignSize := FileSize(f1);
    GetMem(Sign, SignSize);

    BlockRead(f1,Sign^,SignSize);
    CloseFile(f1);
  end;

  if (dlgOpenFile.Execute) and (dlgOpenFile.FileName <> EmptyStr) then
  begin
//подписанынй файл
    SetLength(MessageArray, sizeof(PByte));
    SetLength(MessageSize, sizeof(DWORD));

    AssignFile(f1,dlgOpenFile.FileName);
    Reset(f1, 1);

    MessageSize[0] := FileSize(f1);
    GetMem(MessageArray[0], MessageSize[0]);

    BlockRead(f1, MessageArray[0]^, MessageSize[0]);
    CloseFile (f1);
  end;


  if CryptVerifyDetachedMessageSignature(@VerifyPara, 0, Sign, SignSize, 1, MessageArray, MessageSize, pSignerCert) then
  begin
    Memo.Lines.Add('Проверка прошла успешно');
  end
  else
  begin
    err := IntToStr(GetLastError);
    Memo.Lines.Add('Ошибка:' + err);
  end;
4 сен 07, 14:11    [4617956]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Dimitry Sibiryakov
Member

Откуда:
Сообщений: 46121

LimonFX
Где его нужно указывать?

В CryptCreateHash например...

Posted via ActualForum NNTP Server 1.4

4 сен 07, 14:13    [4617971]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
Предположим, что подпись и сообщение читаются правильно... и содержат все необходимые данные....
Ну и заполняется вами вроде вся структура... хотя и тут есть свой нюанс:
VerifyPara.hCryptProv := hProv;
хендл какого провайдера вы сюда подпихиваете? может ну его нафик... жираф то большой и ему видней... скиньте в ноль... он сам проверить провайдеров в системе на факт реализации необходимых алгоритмов
VerifyPara.hCryptProv := 0;

Надеюсь и калбек (MySignerCertificateCallback) делает, что нужно

а подписывалось на каком HashAlgorithm.pszObjId?
4 сен 07, 15:31    [4618571]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
LimonFX
Member

Откуда:
Сообщений: 77
Вообщем задача немного поменялась. Теперь все в одном флаконе (инфа и подпись в одном файле), т.е. не detached. Старый код выбросил, залез на msdn, нашел там подходящий пример и на его основе сделал вот этот код:
procedure TForm1.Button3Click(Sender: TObject);
Const
  CERT_STORE_NAME = 'MY';
  MY_ENCODING_TYPE = PKCS_7_ASN_ENCODING or X509_ASN_ENCODING;
var
  hCertStoreHandle : HCERTSTORE;
  SIGNER_NAME: string;
  pSignerName: PWideChar;
  //pbMessage: PByte;
  //cbMessage: DWORD;
  MessageArray: array of PByte;
  MessageSize : array of DWORD;
  pSignerCert: PCCERT_CONTEXT;
  SigParams: CRYPT_SIGN_MESSAGE_PARA;
  cbSignedMessageBlob: DWORD;
  pbSignedMessageBlob: PByte;
  pSignedMessageBlob: CRYPT_DATA_BLOB;
  err: String;
  f1: File;
begin
  Memo.Clear;
  SIGNER_NAME := 'test';
  hCertStoreHandle := nil;
  pSignedMessageBlob.cbData := 0;
  pSignedMessageBlob.pbData := nil;
  hCertStoreHandle := CertOpenSystemStore(0,PChar(CERT_STORE_NAME));
  if hCertStoreHandle = nil then Memo.Lines.Add('Ошибка при открытии хранилища ' + CERT_STORE_NAME);
  GetMem(pSignerName,2*Length(SIGNER_NAME)+1);
  StringToWideChar(SIGNER_NAME,pSignerName,2*Length(SIGNER_NAME)+1);
  pSignerCert := CertFindCertificateInStore(
                                            hCertStoreHandle,
                                            MY_ENCODING_TYPE,
                                            0,
                                            CERT_FIND_SUBJECT_STR,
                                            pSignerName,
                                            nil);
  FreeMem(pSignerName);
  if pSignerCert = nil then
  begin
    err := IntToStr(GetLastError);
    Memo.Lines.Add('Сертификат ' + SIGNER_NAME + ' не найден: ' + err);
    exit;
  end;

  if (srcFile.Execute) and (srcFile.FileName <> EmptyStr) then
  begin
    SetLength(MessageArray, 1*sizeof(PByte));
    SetLength(MessageSize, 1*sizeof(DWORD));

    AssignFile(f1,srcFile.FileName);
    Reset(f1, 1);

    MessageSize[0] := FileSize(f1);
    GetMem(MessageArray[0], MessageSize[0]);

    BlockRead(f1, MessageArray[0]^, MessageSize[0]);
    CloseFile (f1);
  end;

  SigParams.cbSize := SizeOF(CRYPT_SIGN_MESSAGE_PARA);
  SigParams.dwMsgEncodingType := MY_ENCODING_TYPE;
  SigParams.pSigningCert := pSignerCert;
  SigParams.HashAlgorithm.pszObjId := szOID_PKCS_7;
  SigParams.HashAlgorithm.Parameters.cbData := 0;
  SigParams.cMsgCert := 1;
  SigParams.rgpMsgCert := @pSignerCert;
  SigParams.cAuthAttr := 0;
  SigParams.dwInnerContentType := 0;
  SigParams.cMsgCrl := 0;
  SigParams.cUnauthAttr := 0;
  SigParams.dwFlags := 0;
  SigParams.pvHashAuxInfo := nil;
  SigParams.rgAuthAttr := nil;

  if not (CryptSignMessage(
        @SigParams,
        FALSE,
        1,
        MessageArray,
        MessageSize,
        nil,
        @cbSignedMessageBlob)) then
  begin
    err := IntToStr(GetLastError);
    Memo.Lines.Add('Размер буфера: ' + err);
    exit;
  end;
  GetMem(pbSignedMessageBlob, cbSignedMessageBlob);
  if not (CryptSignMessage(
          @SigParams,
          FALSE,
          1,
          MessageArray,
          MessageSize,
          pbSignedMessageBlob,
          @cbSignedMessageBlob)) then
  begin
    err := IntToStr(GetLastError);
    Memo.Lines.Add('Файл не подписан: ' + err);
    exit;
  end
  else Memo.Lines.Add('Файл подписан: ' + err);
  CertFreeCertificateContext(pSignerCert);
  CertCloseStore(hCertStoreHandle, CERT_CLOSE_STORE_CHECK_FLAG);
  hCertStoreHandle := nil;
  pSignedMessageBlob.cbData := cbSignedMessageBlob;
  pSignedMessageBlob.pbData := pbSignedMessageBlob;
  if SaveSignFile.Execute then
  begin
    AssignFile(f1,SaveSignFile.FileName);
    Rewrite(f1, 1);
    BlockWrite(f1, pbSignedMessageBlob^, cbSignedMessageBlob);
    CloseFile (f1);
  end;
end;

procedure TForm1.Button4Click(Sender: TObject);
Const
  CERT_STORE_NAME = 'MY';
  MY_ENCODING_TYPE = PKCS_7_ASN_ENCODING or X509_ASN_ENCODING;
  PROVIDER_NAME = '';
var
  hCertStoreHandle: HCERTSTORE;
  cbDecodedMessageBlob: DWORD;
  pbDecodedMessageBlob: PByte;
  VerifyParams: CRYPT_VERIFY_MESSAGE_PARA;
  pSignedMessageBlob: CRYPT_DATA_BLOB;
  pDecodedMessageBlob: CRYPT_DATA_BLOB;
  err: string;
  SIGNER_NAME: string;
  pSignerName: PWideChar;
  pSignerCert: PCCERT_CONTEXT;
  MessageArray: array of PByte;
  MessageSize : array of DWORD;
  f1: File;
  hProv: HCRYPTPROV;
  
  function MySignerCertificateCallback( pvGetArg : pvoid; 
dwCertEncodingType : DWORD;
pSignerId : PCERT_INFO;
hMsgCertStore : HCERTSTORE): PCCERT_CONTEXT ; stdcall;
  begin
    Result := PCCERT_CONTEXT(pvGetArg);
  end;

begin
  pbDecodedMessageBlob := nil;
  pDecodedMessageBlob.cbData := 0;
  pDecodedMessageBlob.pbData := nil;

  Memo.Clear;
  SIGNER_NAME := 'test';
  hCertStoreHandle := nil;
  pSignedMessageBlob.cbData := 0;
  pSignedMessageBlob.pbData := nil;
  hCertStoreHandle := CertOpenSystemStore(0,PChar(CERT_STORE_NAME));
  if hCertStoreHandle = nil then Memo.Lines.Add('Ошибка при открытии хранилища ' + CERT_STORE_NAME);
  GetMem(pSignerName,2*Length(SIGNER_NAME)+1);
  StringToWideChar(SIGNER_NAME,pSignerName,2*Length(SIGNER_NAME)+1);
  pSignerCert := CertFindCertificateInStore(
                                            hCertStoreHandle,
                                            MY_ENCODING_TYPE,
                                            0,
                                            CERT_FIND_SUBJECT_STR,
                                            pSignerName,
                                            nil);
  FreeMem(pSignerName);
  if pSignerCert = nil then
  begin
    err := IntToStr(GetLastError);
    Memo.Lines.Add('Сертификат ' + SIGNER_NAME + ' не найден: ' + err);
    exit;
  end;

  if not CryptAcquireContext(@hProv, nil, PROVIDER_NAME, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) then
  begin
    Memo.Lines.Add('Провайдер '+PROVIDER_NAME+' не найден');
    exit;
  end;

  VerifyParams.cbSize := sizeof(CRYPT_VERIFY_MESSAGE_PARA);
  VerifyParams.dwMsgAndCertEncodingType := MY_ENCODING_TYPE;
  VerifyParams.hCryptProv := hProv;
  VerifyParams.pfnGetSignerCertificate := @MySignerCertificateCallback;
  VerifyParams.pvGetArg := pSignerCert;

  if (srcFile.Execute) and (srcFile.FileName <> EmptyStr) then
  begin
    AssignFile(f1,srcFile.FileName);
    Reset(f1, 1);

    pSignedMessageBlob.cbData := FileSize(f1);
    GetMem(pSignedMessageBlob.pbData, pSignedMessageBlob.cbData);

    BlockRead(f1, pSignedMessageBlob.pbData^, pSignedMessageBlob.cbData);
    CloseFile (f1);
  end;

  if not (CryptVerifyMessageSignature(
                  @VerifyParams,
                  0,
                  pSignedMessageBlob.pbData,
                  pSignedMessageBlob.cbData,
                  nil,
                  cbDecodedMessageBlob,
                  nil)) then
  begin
    err := IntToStr(GetLastError);
    Memo.Lines.Add('Не удалось узнать размер буфера : ' + err);
    exit;
  end;

  GetMem(pbDecodedMessageBlob,cbDecodedMessageBlob); 

  if not (CryptVerifyMessageSignature(
                  @VerifyParams,
                  0,
                  pSignedMessageBlob.pbData,
                  pSignedMessageBlob.cbData,
                  pbDecodedMessageBlob,
                  cbDecodedMessageBlob,
                  nil)) then
  begin
    err := IntToStr(GetLastError);
    Memo.Lines.Add('Не удалось проверить подпись: ' + err);
    exit;
  end;
  pDecodedMessageBlob.cbData := cbDecodedMessageBlob;
  pDecodedMessageBlob.pbData := pbDecodedMessageBlob;
end;
Сам файл вроде бы подписывается, ошибок замечено не было. А вот при проверке подписи всё таже ошибка - NTE_BAD_ALGID.
5 сен 07, 17:33    [4625427]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
Нда... очень смешно про "szOID_PKCS_7"... ты где взял этот код? короче уломал ты меня писать за тебя... готовь деньги
Вот слепленная на скорую руку реализация поверх твоей

uses
  WinCrypt;

const
  CERT_STORE_NAME = 'MY';
  MY_ENCODING_TYPE = PKCS_7_ASN_ENCODING or X509_ASN_ENCODING;
  SignerName : WideString = 'Pavel';

procedure Err( const msg : String );
var
  dwError, dwLen : DWORD;
  sError, sErrorDecode : String;
  pBuffer : Cardinal;
begin
  dwError := GetLastError;
  dwLen := FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM or
                          FORMAT_MESSAGE_ARGUMENT_ARRAY or
                          FORMAT_MESSAGE_ALLOCATE_BUFFER,
                          nil, dwError, 0, @pBuffer, 0, nil );
  try
    SetString( sErrorDecode, PChar( pBuffer ), dwLen );
  finally
    LocalFree( pBuffer );
  end;
  sError := Format( '%s'#10#13'0x%x %s', [ msg, dwError, sErrorDecode ] );
  Form1.Memo.Lines.Add( msg );
  Raise Exception.Create( sError );
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  pSignerCert: PCCERT_CONTEXT;
  hCertStoreHandle : HCERTSTORE;
  SigParams: CRYPT_SIGN_MESSAGE_PARA;
  pStreamIn, pStreamOut : TMemoryStream;
  MessageArray : PAPBYTE;
  MessageSize : PADWORD;
  MessageCert : PAPCCERT_CONTEXT;
  cbSignedMessageBlob: DWORD;
begin
  Memo.Clear;
  hCertStoreHandle := CertOpenSystemStore( 0, PChar( CERT_STORE_NAME ));
  if ( not assigned( hCertStoreHandle )) then
    Err( Format( 'Ошибка при открытии хранилища: %s', [ CERT_STORE_NAME ] ));
  try
    pSignerCert := CertFindCertificateInStore( hCertStoreHandle, MY_ENCODING_TYPE, 0, CERT_FIND_SUBJECT_STR, PWideChar( SignerName ), nil );
    try
      if ( not assigned( pSignerCert )) then
        Err( Format( 'Сертификат %s не найден', [ SignerName ] ));
      pStreamIn := TMemoryStream.Create;
      pStreamOut := TMemoryStream.Create;
      try
        pStreamIn.LoadFromFile( 'd:\test.txt' );
        GetMem( MessageArray, 1 * SizeOf( PByte ));
        GetMem( MessageSize, 1 * SizeOf( DWORD ));
        GetMem( MessageCert, 1 * SizeOf( PAPCCERT_CONTEXT ));
        MessageArray[ 1 ] := pStreamIn.Memory;
        MessageSize[ 1 ] := pStreamIn.Size;
        MessageCert[ 1 ] := pSignerCert;
        FillChar( SigParams, SizeOf( CRYPT_SIGN_MESSAGE_PARA ), #0 );
        SigParams.cbSize := SizeOF( CRYPT_SIGN_MESSAGE_PARA );
        SigParams.dwMsgEncodingType := MY_ENCODING_TYPE;
        SigParams.pSigningCert := pSignerCert;
        SigParams.HashAlgorithm.pszObjId := szOID_RSA_MD5;
        SigParams.cMsgCert := 1;
        SigParams.rgpMsgCert := MessageCert;
        if not ( CryptSignMessage( @SigParams, false, 1, MessageArray, MessageSize, nil, cbSignedMessageBlob )) then
          Err( 'Размер буфера' );
        pStreamOut.Size := cbSignedMessageBlob;
        if not ( CryptSignMessage( @SigParams, false, 1, MessageArray, MessageSize, pStreamOut.Memory, cbSignedMessageBlob )) then
          Err( 'Файл не подписан' );
        pStreamOut.Size := cbSignedMessageBlob;
        pStreamOut.SaveToFile( 'd:\test.sig' );
        Memo.Lines.Add( 'Файл подписан' );
      finally
        FreeAndNil( pStreamIn );
        FreeAndNil( pStreamOut );
      end;
    finally
      CertFreeCertificateContext(pSignerCert);
    end;
  finally
    CertCloseStore( hCertStoreHandle, CERT_CLOSE_STORE_CHECK_FLAG );
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  VerifyParams : CRYPT_VERIFY_MESSAGE_PARA;
  pStreamIn, pStreamOut : TMemoryStream;
  cbDecodedMessageBlob : DWORD;
begin
  Memo.Clear;
  pStreamIn := TMemoryStream.Create;
  pStreamOut := TMemoryStream.Create;
  try
    pStreamIn.LoadFromFile( 'd:\test.sig' );
    FillChar( VerifyParams, SizeOf( CRYPT_VERIFY_MESSAGE_PARA ), #0 );
    VerifyParams.cbSize := SizeOf( CRYPT_VERIFY_MESSAGE_PARA );
    VerifyParams.dwMsgAndCertEncodingType := MY_ENCODING_TYPE;
    if not ( CryptVerifyMessageSignature( @VerifyParams, 0, pStreamIn.Memory, pStreamIn.Size,
                                          nil, cbDecodedMessageBlob, nil )) then
      Err( 'Не удалось узнать размер буфера' );
    pStreamOut.Size := cbDecodedMessageBlob;
    if not ( CryptVerifyMessageSignature( @VerifyParams, 0, pStreamIn.Memory, pStreamIn.Size,
                                          pStreamOut.Memory, cbDecodedMessageBlob, nil )) then
      Err( 'Не удалось проверить подпись' );
    pStreamOut.Size := cbDecodedMessageBlob;
    pStreamOut.SaveToFile( 'd:\test.res' );
    Memo.Lines.Add( 'Подпись верна' );
  finally
    FreeAndNil( pStreamIn );
    FreeAndNil( pStreamOut );
  end;
end;

у тебя прототипы функций неправильные... ты используешь очень странный алгоритм хеширования... он вообще вычисляется перепобором по-хорошему
Я заморачиваться не стал и просто вложил сертификат в подписанное attached сообщение... потому верификация выглядит так скромно... специфичных мементов в этом коде вагон и маленькая тележка... разбирайся... удачи
6 сен 07, 11:24    [4628091]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
потерял все парные FreeMem в коде есть утечки... только заметил
6 сен 07, 11:31    [4628160]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
LimonFX
Member

Откуда:
Сообщений: 77
Про код я уже писал. За основу взял СИшный пример с MSDN. Ну вообщем, спасибо. Будем разбираться, а там глядишь и знающий человек с отпуска вернется.

P.S: еще немного и я смело могу программистом идти работать :)
6 сен 07, 14:36    [4630107]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
vovanka
Member

Откуда:
Сообщений: 11
ты прогу домучал?
11 мар 08, 12:32    [5393247]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Miktor
Member

Откуда: г. Хабаровск
Сообщений: 398
У меня таже проблема. Попробовал код, любезно предоставленный Альт'ом, но на строчке
if not ( CryptSignMessage( @SigParams, false, 1, MessageArray, MessageSize, nil, cbSignedMessageBlob )) then
          Err( 'Размер буфера' );
вы дает ошибку "Access viol... in module wcrypt32.dll". Я не очень разбираюсь в алгоритмах всей этой криптографии... Мне просто нужно подписать сертификатом и проверить подпись. Может есть готовые решения или компоненты?
27 мар 08, 07:15    [5464959]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
Давайте сверять прототипы функций и типов. Я тоже.. в свое время... стучал головой в монитор... например вот такой нюанс:
WinCrypt.pas
{.$define CRYPT_SIGN_MESSAGE_PARA_HAS_CMS_FIELDS}

type
  PCRYPT_SIGN_MESSAGE_PARA = ^CRYPT_SIGN_MESSAGE_PARA;
  CRYPT_SIGN_MESSAGE_PARA = record
    cbSize :DWORD;
    dwMsgEncodingType :DWORD;
    pSigningCert :PCCERT_CONTEXT;
    HashAlgorithm :CRYPT_ALGORITHM_IDENTIFIER;
    pvHashAuxInfo :PVOID;
    cMsgCert :DWORD;
    rgpMsgCert : PPCCERT_CONTEXT;
    cMsgCrl : DWORD;
    rgpMsgCrl : PPCCRL_CONTEXT;
    cAuthAttr :DWORD;
    rgAuthAttr :PCRYPT_ATTRIBUTE;
    cUnauthAttr :DWORD;
    rgUnauthAttr :PCRYPT_ATTRIBUTE;
    dwFlags :DWORD;
    dwInnerContentType :DWORD;
  {$ifdef CRYPT_SIGN_MESSAGE_PARA_HAS_CMS_FIELDS}
    HashEncryptionAlgorithm : CRYPT_ALGORITHM_IDENTIFIER;
    pvHashEncryptionAuxInfo : DWORD;
  {$endif}
  end;
т.е. структура должна быть реально короче для операционок ниже XP/2003
Ну и давайте показывайте все ваши используемые прототипы... танцуя от CryptSignMessage
27 мар 08, 12:05    [5466619]     Ответить | Цитировать Сообщить модератору
Топик располагается на нескольких страницах: [1] 2 3 4 5 6   вперед  Ctrl      все
Все форумы / Delphi Ответить