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

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

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

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

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]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
vovanka
Member

Откуда:
Сообщений: 11
а вас не затруднило бы выложить фаил wincrypt.pas
27 мар 08, 13:24    [5467270]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
Спросите гугль по WinCrypt.pas


Скажи, что с нами будет, если нам дадут все ни за что,
Мы умрем в изумрудах, не сказав ничего (с) Магнитная Аномалия - В Изумрудах
27 мар 08, 13:51    [5467506]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Miktor
Member

Откуда: г. Хабаровск
Сообщений: 398
Вот этой части
{$ifdef CRYPT_SIGN_MESSAGE_PARA_HAS_CMS_FIELDS}
    HashEncryptionAlgorithm : CRYPT_ALGORITHM_IDENTIFIER;
    pvHashEncryptionAuxInfo : DWORD;
  {$endif}
там не оказалось. Т.е. все одинаковое кроме условной директивы.
Альт
Ну и давайте показывайте все ваши используемые прототипы... танцуя от CryptSignMessage

Т.е. мне сюда выложить исходники всех используемых прототипов?
function CryptSignMessage(pSignPara :PCRYPT_SIGN_MESSAGE_PARA;
                          fDetachedSignature :BOOL;
                          cToBeSigned :DWORD;
                    const rgpbToBeSigned :array of PBYTE;
                          rgcbToBeSigned :array of DWORD;
                          pbSignedBlob :PBYTE;
                          pcbSignedBlob :PDWORD):BOOL ; stdcall;
function CryptVerifyMessageSignature(pVerifyPara :PCRYPT_VERIFY_MESSAGE_PARA;
                                     dwSignerIndex :DWORD;
                               const pbSignedBlob :PBYTE;
                                     cbSignedBlob :DWORD;
                                     pbDecoded :PBYTE;
                                     pcbDecoded :DWORD;
                                     ppSignerCert :PCCERT_CONTEXT
                                     ):BOOL ; stdcall;
function CertFindCertificateInStore(hCertStore :HCERTSTORE;
                                    dwCertEncodingType :DWORD;
                                    dwFindFlags :DWORD;
                                    dwFindType :DWORD;
                              const pvFindPara :PVOID;
                                    pPrevCertContext :PCCERT_CONTEXT
                                    ):PCCERT_CONTEXT ; stdcall;
Вроде больше ничего такого не используется...
И еще попутный вопрос: как вообще устроена логика? В системе установлены сертификаты (на некоторых несколько различных). Пользователь нажимает кнопку "Подписать", после этого он должен выбрать нужный сертификат и предоставив носитель (дикету или SmartCard или еще что) и введя пароль запускается процедура подписания? При этом в нашем примере мы вкладываем сертификат в подписаный документ, поэтому не надо устанавливать все сертификаты явно? Я вообще не несу чушь? )))) Давайте объясню задачу: у нас разрабатывается ПО "Делопроизводство". Сечас уже эксплуатируется в некотором объеме и постепенно наращиваем функционал. Ну вот начальство придумало чтобы документы были полность в электронном виде. Т.е. докладные, приказы, распоряжения и пр. были подписаны цифровой подписью. Кроме того так же буду проходить согласования тех. задания и договора. Внутри предприятия розданы всем СмакртКарты с уже выданными сертификатами от сервера. Естественно этого достаточно для документов внутри компании. Но вот не надо ли будет устанавливать каждому ВСЕ выданные сертификаты чтобы прошла проверка подписи? Сорри если сильно путано рассказал.
28 мар 08, 01:49    [5470781]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
В общем причину AV я вижу и она типична. Заголовочник содержит неправильные типы. Джеди-тим конечно подложила большую свинью всем дельфистам. Всегда сверяйте прототипы с оригиналами в msdn. Например, чтобы много не переделывать, можно привести все к такому типу:
type
  TByteArray = Array of PByte;
  PByteArray = ^TByteArray;
  TDWordArray = Array of DWORD;
  PDWordArray = ^TDWordArray;
  TPCCertContextArray = Array of PCCERT_CONTEXT;
  PPCCertContextArray = ^TPCCertContextArray;

function CryptSignMessage( var pSignPara : CRYPT_SIGN_MESSAGE_PARA;
                           fDetachedSignature : BOOL;
                           cToBeSigned :DWORD;
                     const rgpbToBeSigned : PByteArray;
                           rgcbToBeSigned : PDWordArray;
                           pbSignedBlob : PBYTE;
                           var pcbSignedBlob : DWORD ) : BOOL; stdcall;

и, соответственно, поменяется реализация подписи...

var
  ...
  MessageArray : TByteArray;
  MessageSize : TDWordArray;
  MessageCert : TPCCertContextArray;
  ...
begin
   ...
    try
      pStreamIn := TMemoryStream.Create;
      pStreamOut := TMemoryStream.Create;
      SetLength( MessageArray, 1 );
      SetLength( MessageSize, 1 );
      SetLength( MessageCert, 1 );
      try
        pStreamIn.LoadFromFile( 'c:\test.txt' );
        MessageArray[ 0 ] := pStreamIn.Memory;
        MessageSize[ 0 ] := pStreamIn.Size;
        MessageCert[ 0 ] := 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 := Pointer( MessageCert );
        if ( not ( CryptSignMessage( SigParams, false, 1, Pointer( MessageArray ), Pointer( MessageSize ), nil, cbSignedMessageBlob ))) then
          Err( 'Размер буфера' );
        pStreamOut.Size := cbSignedMessageBlob;
        if not ( CryptSignMessage( SigParams, false, 1, Pointer( MessageArray ), Pointer( MessageSize ), pStreamOut.Memory, cbSignedMessageBlob )) then
          Err( 'Файл не подписан' );
        pStreamOut.Size := cbSignedMessageBlob;
        pStreamOut.SaveToFile( 'c:\test.sig' );
        Memo.Lines.Add( 'Файл подписан' );
      finally
        FreeAndNil( pStreamIn );
        FreeAndNil( pStreamOut );
      end;
    ...
end;

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

зы: а где будет храниться приватный ключ дело ваше, на отчужденных ключевых носителях, в реестре или еще где, тут скорее только вопрос стоимости развертывания решения и требований к конфиденциальности от вашей СБ. Я работал и с параноиками и жадными ит-директорами. Везде свои плюсы ))))))


Скажи, что с нами будет, если нам дадут все ни за что,
Мы умрем в изумрудах, не сказав ничего (с) Магнитная Аномалия - В Изумрудах
28 мар 08, 11:41    [5472038]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
vovanka
Member

Откуда:
Сообщений: 11
а еще вопрос какая версия делфи использовалась?
у меня половина величин не распознается компилятором, на пример типы данных DWORD Cardinal
29 мар 08, 13:54    [5476340]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
uses Windows?
29 мар 08, 20:46    [5476811]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
vovanka
Member

Откуда:
Сообщений: 11
а вот еще вопрос тут выложен пример но он ссылается на TForm1 с описанием пареметров окна. можно ли посмотреть код TForm1 для этого примера
29 мар 08, 21:21    [5476857]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
Я его выдумал 5 сентября 2007 года... т.е. более 6 месяцев назад... просто глядя на код LimonFX... никакого кода у меня нет... что именно вам не понятно? старайтесь задавать вопросы в топике по криптографии, а не по базовым знаниям программирования


Скажи, что с нами будет, если нам дадут все ни за что,
Мы умрем в изумрудах, не сказав ничего (с) Магнитная Аномалия - В Изумрудах
29 мар 08, 21:27    [5476864]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
vovanka
Member

Откуда:
Сообщений: 11
просто я привык разбираться во всем сам, на примерах, а для этого нужна полностью рабочая прога. я писал анологичную прогу на си на базе примера из мсдн, там у меня все получилось. а здесь ничего не выходит:(
ладно буду дальше стараться понять
29 мар 08, 22:22    [5476951]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
Статей хватает за оба глаза, например:
http://rsdn.ru/article/crypto/cryptoapi.xml
Ну и... http://www.podgoretsky.com/ftp/Docs/Delphi/CryptoAPI/
В оригинале это ^^ было тремя или четырьмя статьями... потому есть мысль, что Анатолий что-то потерял при выкладывании
На предыдущей странице я давал ссылку на готовые примеры от компании КриптоПро
http://www.cryptopro.ru/cryptopro/products/csp/20/sample-2-0.zip
Ну и msdn, который стал уже давно бесплатным
30 мар 08, 10:40    [5477645]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
vovanka
Member

Откуда:
Сообщений: 11
спасибо я пополним мою коллекцию програмных кодов на эту тему:)
30 мар 08, 16:35    [5477964]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
vovanka
спасибо я пополним мою коллекцию програмных кодов на эту тему:)

пока вы что-то там копите... другие уже выписали XEnroll, CAPICOM и CNG )
30 мар 08, 16:56    [5478005]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
vovanka
Member

Откуда:
Сообщений: 11
ну пример проги http://www.cryptopro.ru/cryptopro/products/csp/20/sample-2-0.zip на си:(
на си у меня у самого есть:( причем собственного написания
а остальные две ссылки я уже давно изучаю. у меня это уже есть:(
30 мар 08, 22:10    [5478633]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
vovanka
ну пример проги http://www.cryptopro.ru/cryptopro/products/csp/20/sample-2-0.zip на си:(
на си у меня у самого есть:( причем собственного написания

можете подобное... идите в криптографы, а не тусуйтесь среди прикладников
какая разница, какой язык из типовых для программирования... ведь это лишь инструмент
30 мар 08, 22:28    [5478687]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Артем_84
Member

Откуда:
Сообщений: 707
vovanka
на си у меня у самого есть:( причем собственного написания

Ох, как это гордо было сказано



Согласен с Альтом
30 мар 08, 22:40    [5478716]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
vovanka
Member

Откуда:
Сообщений: 11
я очень плохой програмист
а цифровая подпись это мой диплом
я в курсаче написал консольную прогу на си, теперь одна из поставленных задач это написать прогу на делфи с оконным интерфейсом возможностью выбора ключа из опр контейнера и нужного куска внутри текста для подписи. но вот на дефи у меня ничего не выходит:(
31 мар 08, 08:55    [5479171]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Карабас Барабас
Member

Откуда: СССР
Сообщений: 9360
vovanka

v> я очень плохой програмист
v> а цифровая подпись это мой диплом
ну и зачем всё это ?

Posted via ActualForum NNTP Server 1.4

31 мар 08, 09:23    [5479226]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
vovanka
я в курсаче написал консольную прогу на си, теперь одна из поставленных задач это написать прогу на делфи с оконным интерфейсом возможностью выбора ключа из опр контейнера и нужного куска внутри текста для подписи. но вот на дефи у меня ничего не выходит:(


В вашем задании нет ничего по теме топика... тут разбираются симпл-функции, а вам нужны лишь базовые... CryptAcquireContext/CryptReleaseContext, CryptGenKey/CryptExportKey, CryptCreateHash/CryptHashData/CryptDestroyHash и CryptSignHash/CryptVerifySignature... т.е. достаточно открыть/создать контейнер, открыть/создать ключевую пару, посчитать хеш по любым данным и зашифровать полученный хеш приватным ключем, экспортировать публичный ключ... все это есть в первых двух частях статьи про защиту секретов. Ничего не выходит потому, что вы лентяй и нюня. Начните писать хоть что-то приблизительно работающее и по ходу вашей работы я смогу ответить на вопросы.
31 мар 08, 12:35    [5480541]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Miktor
Member

Откуда: г. Хабаровск
Сообщений: 398
2Альт

Блин перепробовал все...
        SigParams.cMsgCert := 1;
        SigParams.rgpMsgCert := Pointer( MessageCert );
        if ( not ( CryptSignMessage( SigParams, false, 1, Pointer( MessageArray ),
          Pointer( MessageSize ), nil, Pointer( cbSignedMessageBlob )))) then
          Err( 'Размер буфера' );
        pStreamOut.Size := cbSignedMessageBlob;
        if not ( CryptSignMessage( SigParams, false, 1, Pointer( MessageArray ),
          Pointer( MessageSize ), pStreamOut.Memory, Pointer( cbSignedMessageBlob ))) then
          Err( 'Файл не подписан' );
        pStreamOut.Size := cbSignedMessageBlob;
        pStreamOut.SaveToFile( 'c:\test.sig' );
        Memo1.Lines.Add( 'Файл подписан' );

С переменным успехом оба раза функция выполняется и даже создается файл test.sig. Но размер у него 9 255 712 и весь заполнен пустыми значениями. Пробовал в объявлении функции писать не CRYPT_SIGN_MESSAGE_PARA а PCRYPT_SIGN_MESSAGE_PARA. Результат тот же. Но сечас и так не работает. Опять Access viol... crypt32.dll.
Пытался очистить StreamOut.Clear. Но так вообще не пашет.

Я уже готов оговорить сумму за работоспособный код.
2 апр 08, 06:51    [5489937]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
Спокойствие, смотрим на последний параметр в моем определении функции

function CryptSignMessage( var pSignPara : CRYPT_SIGN_MESSAGE_PARA;
                           fDetachedSignature : BOOL;
                           cToBeSigned :DWORD;
                     const rgpbToBeSigned : PByteArray;
                           rgcbToBeSigned : PDWordArray;
                           pbSignedBlob : PBYTE;
                           var pcbSignedBlob : DWORD ) : BOOL; stdcall;

И, само собой, в моем коде нет Pointer( cbSignedMessageBlob ) и быть не может, в принципе.
Штатные телепаты подсказывают, что у вас в объявлении сидит
                           pcbSignedBlob : PDWORD ) : BOOL; stdcall;
что в корне неверно


Скажи, что с нами будет, если нам дадут все ни за что,
Мы умрем в изумрудах, не сказав ничего (с) Магнитная Аномалия - В Изумрудах
2 апр 08, 07:22    [5489962]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
Хотя... вам можно написать и так:
@cbSignedMessageBlob
получатся теже яйца, только в профиль... но уж точно не надо писать Pointer( cbSignedMessageBlob ) %)


Скажи, что с нами будет, если нам дадут все ни за что,
Мы умрем в изумрудах, не сказав ничего (с) Магнитная Аномалия - В Изумрудах
2 апр 08, 07:30    [5489967]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Miktor
Member

Откуда: г. Хабаровск
Сообщений: 398
Да про Pointer это я пытался различные варианты испробовать. Ну ессно отправил неправильный код. Сорри. Все что я говорил касалось и исходного варианта.
2 апр 08, 07:43    [5489977]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
Miktor
Да про Pointer это я пытался различные варианты испробовать. Ну ессно отправил неправильный код. Сорри. Все что я говорил касалось и исходного варианта.

Не обманывайте меня... я не спал 20 часов за себя не ручаюсь. Вот попытка компиляции с Pointer( cbSignedMessageBlob )
[Error] test.pas(100): Types of actual and formal var parameters must be identical
на моем типе функции. выкладывайте полный ваш тестовый компилируемый и не работающий вариант и сертификат тоже
2 апр 08, 08:02    [5489986]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Miktor
Member

Откуда: г. Хабаровск
Сообщений: 398
Просто у меня действительно в прототипе было
...
pcbSignedBlob :PDWORD):BOOL ; stdcall;
Поэтому
Альт
[Error] test.pas(100): Types of actual and formal var parameters must be identical

не выскакивало и все компилировалось.
Хорошо. Вот прототип
type
  TByteArray = Array of PByte;
  PByteArray = ^TByteArray;
  TDWordArray = Array of DWORD;
  PDWordArray = ^TDWordArray;
  TPCCertContextArray = Array of PCCERT_CONTEXT;
  PPCCertContextArray = ^TPCCertContextArray;
...
function CryptSignMessage(pSignPara :CRYPT_SIGN_MESSAGE_PARA;
                          fDetachedSignature :BOOL;
                          cToBeSigned :DWORD;
                    const rgpbToBeSigned :PByteArray;
                          rgcbToBeSigned :PDWordArray;
                          pbSignedBlob :PBYTE;
                          pcbSignedBlob :PDWORD):BOOL ; stdcall;
Вот процедура
procedure TForm1.Button1Click(Sender: TObject);
var
  pSignerCert: PCCERT_CONTEXT;
  hCertStoreHandle : HCERTSTORE;
  SigParams: CRYPT_SIGN_MESSAGE_PARA;
  pStreamIn, pStreamOut : TMemoryStream;
  MessageArray : TByteArray;
  MessageSize : TDWordArray;
  MessageCert : TPCCertContextArray;
  cbSignedMessageBlob: DWORD;
begin
  Memo1.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 );
    cbSignedMessageBlob := 0;
    try
      if ( not assigned( pSignerCert )) then
        Err( Format( 'Ñåðòèôèêàò %s íå íàéäåí', [ SignerName ] ));
      pStreamIn := TMemoryStream.Create;
      pStreamOut := TMemoryStream.Create;
      SetLength( MessageArray, 1 );
      SetLength( MessageSize, 1 );
      SetLength( MessageCert, 1 );
      try
        pStreamIn.LoadFromFile( 'c:\Test.txt' );
        MessageArray[ 0 ] := pStreamIn.Memory;
        MessageSize[ 0 ] := pStreamIn.Size;
        MessageCert[ 0 ] := 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 := Pointer( MessageCert );
        if ( not ( CryptSignMessage( SigParams, false, 1, Pointer( MessageArray ),
          Pointer( MessageSize ), nil, cbSignedMessageBlob ))) then
          Err( 'Ðàçìåð áóôåðà' );
        pStreamOut.Size := cbSignedMessageBlob;
        pStreamOut.Clear;
        if not ( CryptSignMessage( SigParams, false, 1, Pointer( MessageArray ),
          Pointer( MessageSize ), pStreamOut.Memory, cbSignedMessageBlob )) then
          Err( 'Ôàéë íå ïîäïèñàí' );
        pStreamOut.Size := cbSignedMessageBlob;
        pStreamOut.SaveToFile( 'c:\test.sig' );
        Memo1.Lines.Add( 'Ôàéë ïîäïèñàí' );
      finally
        FreeAndNil( pStreamIn );
        FreeAndNil( pStreamOut );
        FreeAndNil( SigParams );
      end;
    finally
      CertFreeCertificateContext(pSignerCert);
    end;
  finally
    CertCloseStore( hCertStoreHandle, CERT_CLOSE_STORE_CHECK_FLAG );
  end;
end;
В доказательство прикалываю скриншот ))))

К сообщению приложен файл. Размер - 0Kb
2 апр 08, 08:40    [5490040]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
Боже мой
function CryptSignMessage(pSignPara :CRYPT_SIGN_MESSAGE_PARA;
                          fDetachedSignature :BOOL;
                          cToBeSigned :DWORD;
                    const rgpbToBeSigned :PByteArray;
                          rgcbToBeSigned :PDWordArray;
                          pbSignedBlob :PBYTE;
                          pcbSignedBlob :PDWORD):BOOL ; stdcall;
ну найдете уже два отличия с моим:
function CryptSignMessage( var pSignPara : CRYPT_SIGN_MESSAGE_PARA;
                           fDetachedSignature : BOOL;
                           cToBeSigned :DWORD;
                     const rgpbToBeSigned : PByteArray;
                           rgcbToBeSigned : PDWordArray;
                           pbSignedBlob : PBYTE;
                           var pcbSignedBlob : DWORD ) : BOOL; stdcall;

И, что характерно, опять обманываете... вы уже сами запутались с каким именно юнитом WinCrypt у вас реально компилируется приложение... т.к. простая проверка на
pcbSignedBlob :PDWORD):BOOL ; stdcall;
и попыткой собрать приложение с
nil, cbSignedMessageBlob ))) then
приводит к ошибке:
[Error] test.pas(98): Incompatible types: 'Cardinal' and 'PDWORD'
А я тем временем медленно, но верно приближаюсь к целым рабочим суткам


Скажи, что с нами будет, если нам дадут все ни за что,
Мы умрем в изумрудах, не сказав ничего (с) Магнитная Аномалия - В Изумрудах
2 апр 08, 11:02    [5490743]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Miktor
Member

Откуда: г. Хабаровск
Сообщений: 398
Намек понял ) Готов возместить пивом потраченое на меня время )
На самом деле огоромное спасибо. Все подписывает.
Если я еще не довел до белого коленя...
Как теперь вывести на экран информацию о том кто подписал?
Да, и вторая процедура тоже компилируется, но при втором вызове CryptVerifyMessageSignature говорит что "Не удалось проверить подпись". :(
И еще... сверял с MSDN протопит фукнции CryptVerifyMessageSignature... там почемуто pbSignedBlob и pbDecoded объявлены как BYTE. (вот).
3 апр 08, 02:28    [5495692]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
Ну раз на проверке сертификата не скандалит на структуру "подписи", значит и правда подписали удачно... чтобы опять не переделывать код... поменяйте тип так:

function CryptVerifyMessageSignature(pVerifyPara :PCRYPT_VERIFY_MESSAGE_PARA;
                                     dwSignerIndex :DWORD;
                               const pbSignedBlob :PBYTE;
                                     cbSignedBlob :DWORD;
                                     pbDecoded :PBYTE;
                                     pcbDecoded :DWORD;
                                     ppSignerCert :PCCERT_CONTEXT
                                     ):BOOL ; stdcall;


function CryptVerifyMessageSignature( pVerifyPara : PCRYPT_VERIFY_MESSAGE_PARA;
                                      dwSignerIndex : DWORD;
                                const pbSignedBlob : PBYTE;
                                      cbSignedBlob : DWORD;
                                      pbDecoded : PBYTE;
                                 var  pcbDecoded : DWORD;
                                      ppSignerCert : PCCERT_CONTEXT
                                     ) : BOOL; stdcall;                                     

BOOL WINAPI CryptVerifyMessageSignature(
  __in       PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara,
  __in       DWORD dwSignerIndex,
  __in       const BYTE* pbSignedBlob,
  __in       DWORD cbSignedBlob,
  __out      BYTE* pbDecoded,
  __inout    DWORD* pcbDecoded,
  __out_opt  PCCERT_CONTEXT* ppSignerCert
);

вроде все.


Скажи, что с нами будет, если нам дадут все ни за что,
Мы умрем в изумрудах, не сказав ничего (с) Магнитная Аномалия - В Изумрудах
3 апр 08, 12:30    [5497402]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Miktor
Member

Откуда: г. Хабаровск
Сообщений: 398
Да у меня так и сделано. Вот только при проверке - "Не удалось проверить подпись. 0x80090006 Неправильная подпись." :(
4 апр 08, 01:45    [5502039]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
>на проверке сертификата
это не я писал... это демоны...

Выложите уже эту "подпись"... я не телепат... я программист


Скажи, что с нами будет, если нам дадут все ни за что,
Мы умрем в изумрудах, не сказав ничего (с) Магнитная Аномалия - В Изумрудах
4 апр 08, 22:03    [5506900]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
vovanka
Member

Откуда:
Сообщений: 11
мне нужно включить в прграмму возможность выбора криптопровайдера из всех возможных. подскажите пожалуйста функцию в дельфи позволяющую вывести список всех доступных криптоправайдеров
16 апр 08, 09:00    [5552012]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Мимопроходящий
Member

Откуда: бурятский тундрюк, эсквайр
Сообщений: 29045

Привет, vovanka!
Ты пишешь:

vovanka
v> мне нужно включить в прграмму возможность выбора криптопровайдера из всех возможных.

смотри описание Predefined Provider Types

--
With best regards, Мимопроходящий.

Posted via ActualForum NNTP Server 1.4

16 апр 08, 12:22    [5553679]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
vovanka
мне нужно включить в прграмму возможность выбора криптопровайдера из всех возможных. подскажите пожалуйста функцию в дельфи позволяющую вывести список всех доступных криптоправайдеров


перебираете функциями CryptEnumProviderTypes/CryptEnumProviders начиная с Index = 0 до тех пор пока не вернет ошибку ERROR_NO_MORE_ITEMS
16 апр 08, 12:32    [5553790]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
Хотя стоп... vovanka, вы вообще читали ссылки, которые тут приводились по нескольку раз... вот первое же с CryptEnumProviders...
http://www.podgoretsky.com/ftp/Docs/Delphi/CryptoAPI/list2.htm
лень - это ваша беда


Скажи, что с нами будет, если нам дадут все ни за что,
Мы умрем в изумрудах, не сказав ничего (с) Магнитная Аномалия - В Изумрудах
16 апр 08, 13:02    [5554130]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Miktor
Member

Откуда: г. Хабаровск
Сообщений: 398
2Альт

Вот файлик с подписью...

К сообщению приложен файл (test.sig - 1Kb) cкачать
18 апр 08, 04:50    [5564249]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
Сейчас мозг себе сломаю... подпись правильна


Скажи, что с нами будет, если нам дадут все ни за что,
Мы умрем в изумрудах, не сказав ничего (с) Магнитная Аномалия - В Изумрудах
18 апр 08, 14:38    [5566909]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
Структурно правильная. Подпись заявлена именно от того сертификата, который в нее и вложен, дата использования у сертификата не просрочена, в szOID_KEY_USAGE (Digital Signature заявлено)... но на проверке получаю стабильное "0x80090006 Invalid Signature."... одолевает, кучу времени убил на перепроверках. Смущает внушительный список Extension, может использование сертификата ограничивает одна из них. Сейчас у меня уже каша в голове и не получается сообразить (может что-то в szOID_ENHANCED_KEY_USAGE).

Вадим, давайте проведем эксперимент, я выкладываю самоподписной сертификат с приватным ключем вообще без ограничений на использование и подпись с моей машины. Проверяем в два этапа:
1. На первом просто валидируете мою подпись. Если все ок. Двигаемся дальше.
2. На втором вы импортируете сертификат с паролем 123, включив приватный ключ. Устанавливаете его в пользовательское хранилище и до кучи в хранилище доверенных корневых центров.
Пользователь для поиска DPN, подписываете и проверяете подпись. Если опять все ок, то разбираемся с областью использования вашего сертификата.


Скажи, что с нами будет, если нам дадут все ни за что,
Мы умрем в изумрудах, не сказав ничего (с) Магнитная Аномалия - В Изумрудах


К сообщению приложен файл (SERTS.zip - 2Kb) cкачать
18 апр 08, 15:30    [5567371]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Miktor
Member

Откуда: г. Хабаровск
Сообщений: 398
Понял. Попробую. (я уже чуствую что пивом не обойдусь )
19 апр 08, 04:54    [5569514]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Miktor
Member

Откуда: г. Хабаровск
Сообщений: 398
Все проходит на ура. И проверка вашего файла и подпись и проверка на месте.

Miktor
19 апр 08, 05:02    [5569515]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
В общем моя теория, что виноваты KU/EKU не подтвердилась, в приложении к посту проверочный сертификат с приватным ключем (пароль тот же, пользователь при поиске Test). Есть последняя мысль, что надо обязательно кормить систему корневым сертификатом и списком отзыва (у меня на проверке он конечно нагибает сеть, но по заявленным в сертификате путям и для CS-ROOTDC.crl и для CS-ROOTDC.crt ничего нет). Выложите их

К сообщению приложен файл (test.p12 - 1Kb) cкачать
19 апр 08, 21:35    [5570445]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Miktor
Member

Откуда: г. Хабаровск
Сообщений: 398
Проверочный сертификат тоже все подписал и проверил успешно. У меня есть 3 корневых сертификата с именем CS-ROOTDC. Выложил в архиве...

К сообщению приложен файл (Sert.rar - 3Kb) cкачать
21 апр 08, 02:28    [5572060]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
Зачем вам столько перевыпусков (да еще и не цепочкой)? Подошел второй. Перепроверил до кучи самую тоску Application Certificate Policy (szOID_APPLICATION_CERT_POLICIES = '1.3.6.1.4.1.311.21.10')
Подписывает!, что с политикой:
XCN_OID_KP_DOCUMENT_SIGNING = 1.3.6.1.4.1.311.10.3.12
The certificate can be used for signing documents.
Вот по этому списку, что без него (как у вас).
Все. Больше в сертификате нет узких мест. Вы морочите мне голову. Ваш сертификат выпущен от другой ключевой пары. Либо пара была пересоздана позднее. Очень жаль потраченное время.
Ключевые пары обычно вообще не пересоздаются или в редких чп случаях... вроде ситуации компрометации. И корневые сертификаты так тоже не пересоздаются. Отформатируйте ключевой носитель. Создайте чистую пару. Создайте запрос своему ца именно от этой пары и выпустите на нем (текущем!) нормальный сертификат


Скажи, что с нами будет, если нам дадут все ни за что,
Мы умрем в изумрудах, не сказав ничего (с) Магнитная Аномалия - В Изумрудах
21 апр 08, 10:38    [5572607]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

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

К сообщению приложен файл (demo_cert_123.zip - 5Kb) cкачать
21 апр 08, 14:19    [5574266]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Miktor
Member

Откуда: г. Хабаровск
Сообщений: 398
Большущее спасибо. Удалил лишние корневые пары и все заработало. Надеюсь все-таки что время потрачено не зря и топик будет полезен еще кому-нибудь.
22 апр 08, 01:49    [5577368]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Miktor
Member

Откуда: г. Хабаровск
Сообщений: 398
Снова я...
Хочу вывести информацию о подписи (сертификате)
  pSignerCert: PCCERT_CONTEXT;
  CertInfo: CERT_INFO;
  VerifyParams : CRYPT_VERIFY_MESSAGE_PARA;
  pStreamIn, pStreamOut : TMemoryStream;
  cbDecodedMessageBlob : DWORD;
  MemAddr: Cardinal;
  hCertStoreHandle : HCERTSTORE;
begin
  Memo1.Clear;
  pStreamIn := TMemoryStream.Create;
  pStreamOut := TMemoryStream.Create;
  //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 );
    pStreamIn.LoadFromFile( 'c:\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( 'c:\test.res' );
    Memo1.Lines.Add( 'Ïîäïèñü âåðíà' );
    FillChar(CertInfo, SizeOf(CERT_INFO), #0);
    hCertStoreHandle := CertOpenSystemStore( 0, PChar( CERT_STORE_NAME ));
    pSignerCert := VerifyParams.pfnGetSignerCertificate(VerifyParams.pvGetArg,
                                                        X509_ASN_ENCODING or PKCS_7_ASN_ENCODING,
                                                        @CertInfo,
                                                        hCertStoreHandle);
    Memo1.Lines.Add('Äî: '+DateTimeToStr(CertInfo.NotAfter.dwHighDateTime+CertInfo.NotAfter.dwLowDateTime));
  finally
    FreeAndNil( pStreamIn );
    FreeAndNil( pStreamOut );
  end;

Но ругается в функции pfnGetSignerCertificate на AccessViol...
Может кто знает в чем тут дело? Сама проверка проходит нормально. Структура CertInfo благополучно заполняется 0-ми... Чего ему еще надо?
6 май 08, 05:26    [5628719]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
Привет, а это снова я... душевно сидим ) похоже, что тема больше никому не интересна...
Между вашей постановкой задачи и реализацией мало связей... или я опять не понял ваш код. Вам нужно поймать контексты сертификатов... и сделать это можно кучей способов... хотите про pfnGetSignerCertificate, давайте... она является калбек функцией и вызывается крипто ядром для поиска сертификата еще на этапе валидации... сама attached подпись является, если утрировать, хранилищем сертификатов... вы делаете странные вещи… занулили структуру VerifyParams, а потом пытаетесь вызвать pfnGetSignerCertificate с нулевого адреса… какой в этом смысл? только не говорите, что в мсдн именно так выглядит пример...

Я не знаю, что придумать, чтобы нам не лезть в дебри… давайте просто отработаем калбек и переберем сертификаты в подписи…
function CompareCertificateInfo( const Base, Comp: PCERT_INFO ) : Boolean;
var iSize : DWORD;
begin
  iSize := Base.SerialNumber.cbData;
  if iSize > Comp.SerialNumber.cbData then
    iSize := Comp.SerialNumber.cbData;
  Result := CompareMem( Base.SerialNumber.pbData, Comp.SerialNumber.pbData, iSize );
end;

function GetSignerCertificate( pvGetArg: PVOID; dwCertEncodingType: DWORD; pSignerId: PCERT_INFO;
          hMsgCertStore: HCERTSTORE  ) : PCCERT_CONTEXT; stdcall;
begin
  Result := nil;
  repeat
    Result := CertEnumCertificatesInStore( hMsgCertStore, Result );
    if Assigned( Result ) then
      if CompareCertificateInfo( pSignerId, Result.pCertInfo ) then
        Break;
  until not assigned( Result );
end;

...
var
  Certificate : PCCERT_CONTEXT;
  VerifyParams : CRYPT_VERIFY_MESSAGE_PARA;
  pStreamIn, pStreamOut : TMemoryStream;
  cbDecodedMessageBlob : DWORD;
begin
  Memo.Clear;
  pStreamIn := TMemoryStream.Create;
  pStreamOut := TMemoryStream.Create;
  try
    pStreamIn.LoadFromFile( 'c:\test.sig' );
    FillChar( VerifyParams, SizeOf( CRYPT_VERIFY_MESSAGE_PARA ), #0 );
    VerifyParams.cbSize := SizeOf( CRYPT_VERIFY_MESSAGE_PARA );
    VerifyParams.dwMsgAndCertEncodingType := MY_ENCODING_TYPE;
    VerifyParams.pfnGetSignerCertificate := @GetSignerCertificate;
    if not ( CryptVerifyMessageSignature( VerifyParams, 0, pStreamIn.Memory, pStreamIn.Size,
                                          nil, cbDecodedMessageBlob, nil )) then
      Err( 'Не удалось узнать размер буфера' );
    pStreamOut.Size := cbDecodedMessageBlob;
    Memo.Lines.Add( IntToStr( cbDecodedMessageBlob ));
    if not ( CryptVerifyMessageSignature( VerifyParams, 0, pStreamIn.Memory, pStreamIn.Size,
                                          pStreamOut.Memory, cbDecodedMessageBlob, @Certificate )) then
      Err( 'Не удалось проверить подпись' );
    pStreamOut.Size := cbDecodedMessageBlob;
    pStreamOut.SaveToFile( 'c:\test.res' );
    Memo.Lines.Add( 'Подпись верна' );
    { тут можно делать с контекстом серитфиката все, что вашей душе угодно }
    if not ( CertFreeCertificateContext( Certificate )) then
      Err( 'Не удалось удалить контекс сертификата' );
  finally
    FreeAndNil( pStreamIn );
    FreeAndNil( pStreamOut );
  end;
end;

а вообще вам все это не нужно... калбек интересен, например, когда мы хотим работать с альтернативными хранилищами сертификатов... например бд... лучше посмотрите на описание последнего параметра CryptVerifyMessageSignature, до CertFreeCertificateContext вы можете все что угодно делать в полученным в Certificate контекстом сертификата подписавшего...


Скажи, что с нами будет, если нам дадут все ни за что,
Мы умрем в изумрудах, не сказав ничего (с) Магнитная Аномалия - В Изумрудах
6 май 08, 09:03    [5628867]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
что-то у меня только сейчас мозг включился... исправляю дополнительную функцию сравнения...
function CompareBlob( const Base, Comp: PCRYPTOAPI_BLOB ) : Boolean;
begin
  Result := ( Base.cbData = Comp.cbData );
  if Result then
    Result := CompareMem( Base.pbData, Comp.pbData, Base.cbData );
end;

function CompareCertificateInfo( const Base, Comp: PCERT_INFO ) : Boolean;
begin
  Result := CompareBlob( @Base.Issuer, @Comp.Issuer ) and
            CompareBlob( @Base.SerialNumber, @Comp.SerialNumber );
end;
извиняюсь за неточность


Скажи, что с нами будет, если нам дадут все ни за что,
Мы умрем в изумрудах, не сказав ничего (с) Магнитная Аномалия - В Изумрудах
6 май 08, 12:43    [5630296]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
vovanka
Member

Откуда:
Сообщений: 11
интересна, я ее постоянно читаю
6 май 08, 21:00    [5633353]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Miktor
Member

Откуда: г. Хабаровск
Сообщений: 398
Да нет, это я извиняюсь за безграмотную постановку. Надо было действительно внимательней прочиться описание функции CryptVerifyMessageSignature. Кажется это все что мне надо. Т.е. хотелось бы подписать документ (проверив его валидность по срокам), потом проверить правильность подписи и в случае необходимости вывести информацию о сертификате. Больше ничего не надо. Ну может быть еще при настройке клиента, вывести список его личных сертификатов, чтобы выбрать тот, которым он будет подписывать. Но это можно сделать и в ручную.
И опять же спасибо за помощ.
8 май 08, 01:12    [5639553]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Miktor
Member

Откуда: г. Хабаровск
Сообщений: 398
Еще вопрос. (может не в тему) Не могу получить дату из CertInfo.NotAfter. Пытаюсь
Var
  FileTime: DWORD;
...
    LongRec(FileTime).Lo := CertInfo.NotAfter.dwLowDateTime;
    LongRec(FileTime).Hi := CertInfo.NotAfter.dwHighDateTime;
    Memo1.Lines.Add('Äî: '+DateTimeToStr(FileDateToDateTime(FileTime)));

Выдает 1982 год. Ну вообще не ту дату. Нашел функцию
function FileDate2DateTime(FileTime : tFileTime) : tDateTime;
var LocalTime : tFileTime;
    DOSFileTime : DWord;
begin
  FileTimeToLocalFileTime(LocalTime, LocalTime); // Compensate for time zone
  FileTimeToDosDateTime(FileTime,  LongRec(DOSFileTime).Hi,
     LongRec(DOSFileTime).Lo);
  Result := FileDateToDateTime(DOSFileTime);
end;
но результат тоже не тот. Совсем не тот. Че не так-то?
8 май 08, 02:02    [5639607]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
Валидация срока использования пойманного контекста выполняется, например, вызовом в лоб:
Win32Check( CertVerifyTimeValidity( nil, Certificate.pCertInfo ) = 0 );

функция разбора дат
function DecodeDate( FileTime : TFileTime ) : TDateTime;
var
  SystemTime : TSystemTime;
  ModifiedTime : TFileTime;
begin
  FileTimeToLocalFileTime( FileTime, ModifiedTime );
  FileTimeToSystemTime( ModifiedTime, SystemTime );
  Result := SystemTimeToDateTime( SystemTime );
end;

пример вызова
  ...
  DateTimeToStr( DecodeDate( Certificate.pCertInfo.NotBefore ))
  DateTimeToStr( DecodeDate( Certificate.pCertInfo.NotAfter ))
  ...

А вообще я не гордый и бедный... принимаю любые суммы на вебмани счет R153279149005
Раз уж писать код... тем более профильный, то делать это надо за вознаграждение.
С уважением, Павел Данченко
http://cryptocode.ru
8 май 08, 10:04    [5640111]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Miktor
Member

Откуда: г. Хабаровск
Сообщений: 398
Дык я вроде сразу предлагал :) (найду где продаются ВебМани обязательно отблагодарю)
С датами все получилось. Спасибо. Последние 2 маленьких вопроса: Как получить имя серт. центра и владельца сертификата?
12 май 08, 03:28    [5648723]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
renaton
Member

Откуда:
Сообщений: 753
Miktor
Дык я вроде сразу предлагал :) (найду где продаются ВебМани обязательно отблагодарю)
С датами все получилось. Спасибо. Последние 2 маленьких вопроса: Как получить имя серт. центра и владельца сертификата?


не сможешь поделицца исходниками?
12 май 08, 09:17    [5648892]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
Miktor, упрощенно вот так...
procedure BinToHexInverted( Buffer, Text: PChar; BufSize: Integer);
const
  Convert : Array [ 0..15 ] of Char = '0123456789abcdef';
var
  ii : Integer;
begin
  for ii := BufSize - 1 downto 0 do
  begin
    Text[ 0 ] := Convert[ Byte( Buffer[ ii ]) shr 4 ];
    Text[ 1 ] := Convert[ Byte( Buffer[ ii ]) and $F ];
    Inc( Text, 2 );
  end;
end;

function DecodeSerialNumber( Source : PCRYPT_INTEGER_BLOB ) : String;
begin
  SetLength( Result, Source.cbData * 2 );
  BinToHexInverted( PChar( Source.pbData ), Pointer( Result ), Source.cbData );
end;

function DecodeName( Source : PCRYPTOAPI_BLOB ) : String;
var iLength : DWORD;
begin
  iLength := CertNameToStr( MY_ENCODING_TYPE,
                            PCERT_NAME_BLOB( Source ),
                            CERT_OID_NAME_STR,
                            nil, 0 );
  SetLength( Result, iLength );
  iLength := CertNameToStr( MY_ENCODING_TYPE,
                            PCERT_NAME_BLOB( Source ),
                            CERT_OID_NAME_STR,
                            Pointer( Result ), iLength );
  SetLength( Result, iLength );
end;

ну и примеры вызовов...
  ...
  DecodeName( @Certificate.pCertInfo.Subject )
  DecodeName( @Certificate.pCertInfo.Issuer )
  DecodeSerialNumber( @Certificate.pCertInfo.SerialNumber )
  ...

Поиграйтесь с dwStrType у CertNameToStr и определитесь, что именно вам необходимо из данных, возможно хватит и флага CERT_SIMPLE_NAME_STR
Если капать дальше, то OIDs описаны в разделе Name Properties в Platform SDK, до кучи типы X500 частично описаны таблицей в описании функции CertStrToName
12 май 08, 13:30    [5650465]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
renaton
не сможешь поделицца исходниками?

любой каприз за ваши деньги... все равно я напишу быстрее, чем вы сумеете понять суть )
12 май 08, 13:32    [5650474]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
renaton
Member

Откуда:
Сообщений: 753
А где достать правильный файл CryptoAPI.pas ???
12 май 08, 15:00    [5651110]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
renaton
А где достать правильный файл CryptoAPI.pas ???

Уже обсуждалось начиная от.
Для полноценной работы так же необходимы оригинальный WinCrypt.h (7z) (zip) и MS Platform SDK... чем выше версии, тем лучше
12 май 08, 21:43    [5653064]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
renaton
Member

Откуда:
Сообщений: 753
Как сделать так чтобы в свойствах подписаннго файла появилась вкладка подписи и там уже показывались подписанные сертификаты????
14 май 08, 07:54    [5659057]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
renaton
Как сделать так чтобы в свойствах подписаннго файла появилась вкладка подписи и там уже показывались подписанные сертификаты????

Добрый день. Я не понял вопроса. Почти все обсуждение мы говорим об "присоединенной" подписи. Я уже озвучивал, что она по своей сути является хранилищем сертификатов. "показывались подписанные сертификаты" наверное имелось ввиду сертификаты подписавших... в примерах формировали подпись в файле test.sig, чтобы винда корректно открывала подпись в снапине... ".sig" можно заменить на одно из зарегистрированных расширений, к примеру так test.p7s.
Или мы про программную реализацию... тогда вот так (я немного поменял обвязку кода, который уже приводил ранее в калбек функции GetSignerCertificate):

var
  Store : HCERTSTORE;
  Blob : CRYPTOAPI_BLOB;
  pStream : TMemoryStream;
  Certificate : PCCERT_CONTEXT;
begin
  pStream := TMemoryStream.Create;
  try
    Stream.LoadFromFile( 'c:\test.sig' );
    Blob.cbData := pStream.Size;
    Blob.pbData := pStream.Memory;
    Store := CertOpenStore( CERT_STORE_PROV_PKCS7, MY_ENCODING_TYPE, 0, 0, @Blob );
    if not Assigned( Store ) then
      Err( 'Не удалось открыть хранилище' );
    Certificate := nil;
    repeat
      Certificate := CertEnumCertificatesInStore( Store, Certificate );
      if Assigned( Certificate ) then
      begin
        { тут можно делать с контекстом серитфиката все, что вашей душе угодно }
      end;
    until not assigned( Certificate );
    if ( not CertCloseStore( Store, CERT_CLOSE_STORE_CHECK_FLAG )) then
      Err( 'Ошибка закрытия хранилища' );
  finally
    FreeAndNil( pStream );
  end;

тут уже нет никакой валидации подписи и она просто рассматривается как хранилище

К сообщению приложен файл. Размер - 0Kb
14 май 08, 09:33    [5659244]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
renaton
Member

Откуда:
Сообщений: 753
Так может вот этот скриншот пояснит мой вопрос, это свойтсва инсталлятора АдобеРидер 8

К сообщению приложен файл. Размер - 0Kb
14 май 08, 09:40    [5659278]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
renaton
Так может вот этот скриншот пояснит мой вопрос, это свойтсва инсталлятора АдобеРидер 8


Зачем вы оверкворите.. неудобно же читать. То, что вы хотите... к теме топика не относится - это authenticode signature. Выполняется утилитами идущими в поставке почти всех девелоперских продуктов мс... подпись в лоб в использованием мастера и сертификата, который я выкладывал для примера тут ранее (у сертификата должна быть включена KU = Code sign или область использования неограниченна), с ком.строки запускаю:
signtool.exe signwizard
по завершению работы мастера, ответив на вопросы и указав файл и сертификат для подписи получаем

К сообщению приложен файл. Размер - 0Kb
14 май 08, 10:27    [5659576]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
renaton
Member

Откуда:
Сообщений: 753
Альт
renaton
Так может вот этот скриншот пояснит мой вопрос, это свойтсва инсталлятора АдобеРидер 8


Зачем вы оверкворите.. неудобно же читать. То, что вы хотите... к теме топика не относится - это authenticode signature. Выполняется утилитами идущими в поставке почти всех девелоперских продуктов мс... подпись в лоб в использованием мастера и сертификата, который я выкладывал для примера тут ранее (у сертификата должна быть включена KU = Code sign или область использования неограниченна), с ком.строки запускаю:
signtool.exe signwizard
по завершению работы мастера, ответив на вопросы и указав файл и сертификат для подписи получаем


А где достать signtool.exe? В поставке делфи он есть?
14 май 08, 10:44    [5659707]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
renaton
А где достать signtool.exe? В поставке делфи он есть?

Ну, имя тоже может отличаться (например signcode.exe)... не знаю про дельфи... потому утверждать не буду. Список, куда конкретно включены SignTool ищите в msdn, глянул каталоги... у меня на девелоперской машине они продублированы в куче мест... Microsoft SDK/Microsoft.NET 1.1/Microsoft.NET 2.0 и т.д.
поколупайтесь в bin каталогах эмсишных продуктов... первое же найденное:
C:\Program Files\Microsoft.NET\SDK\v1.1\Bin\signcode.exe
14 май 08, 10:58    [5659849]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
LimonFX
Member

Откуда:
Сообщений: 77
vovanka
ты прогу домучал?

Прогу домучал. Вроде всё работает.
Если есть вопросы то чем смогу, помогу. Но это было так давно, что я уже успел немного позабыть и уволится из того банка в котором писал эту программку, не знаю какова её судьба, но для себя исходнички успел скопировать :)
24 июн 08, 12:28    [5839208]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
vovanka
Member

Откуда:
Сообщений: 11
дай почитать пожалуста, для само образования:))
30 июл 08, 09:56    [6002585]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
_test_
Member

Откуда: Узбекистан, Навои -> Сибирь, Томск -> Санкт-Петербург
Сообщений: 45
LimonFX
C предыдущим вопросом я уже разобрался - сертификат находит.


Ребят, такая же проблема. выложил вопрос здесь:

http://programmersforum.ru/showthread.php?t=47254

http://www.cyberforum.ru/win-api/thread31754.html

http://forum.vingrad.ru/forum/topic-257177/kw-certfindcertificatei-%D1%81%D0%B5%D1%80%D1%82%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%82.html

http://www.cryptopro.ru/cryptopro/forum2/default.aspx?g=posts&t=1319

ПОМОГИТЕ ПОЖАЛУЙСТА!
27 апр 09, 16:41    [7119402]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
О... Томск... единственный, известный мне город, где есть маршрутка двигающаяся со Спичечной фабрики на Карандашную ) один из самых любимых городов ) еще неделю назад я бы помог разобраться... сидя в том же лагерном... без всяких проблем )
Вопрос сишный и это первое... CertOpenStore без "MY_ENCODING_TYPE" и с закомментированным enc.type... на глаз мало чего ловится... покажите сертификат
27 апр 09, 20:11    [7120429]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
Как-то я вчера больше о Томске мечтал... чем на код смотрел )
В общем, нормальный код... насколько он может быть нормальным для си )... соорудил пробник (в приложении сертификат с RDN CN='Gafarov_Artur') и запихнул его в личные... проверяю кодом:
+
const
  CN = 'Gafarov_Artur';
  CERT_STORE_NAME = 'MY';
  CRYPTO_ENCODING_TYPE : DWORD = PKCS_7_ASN_ENCODING or X509_ASN_ENCODING;
var
  hStore : HCERTSTORE;
  pCert : PCCERT_CONTEXT;
begin
  hStore := CertOpenSystemStore( 0, CERT_STORE_NAME );
  if CryptoCheck( hStore <> 0 ) then
  try
    pCert := CertFindCertificateInStore( hStore, CRYPTO_ENCODING_TYPE, 0,
      CERT_FIND_SUBJECT_STR, CN, nil );
    if CryptoCheck( Assigned( pCert )) then
    try
    //
    // Работаем с сертификатом
    //
    finally
      CryptoCheck( CertFreeCertificateContext( pCert ));
    end;
  finally
    CryptoCheck( CertCloseStore( hStore, CERT_CLOSE_STORE_CHECK_FLAG ));
  end;
end;

все работает, все находит... попробуйте моим вариантом, через CertOpenSystemStore и моим же сертификатом.
зы: есть еще мысли про printableString в вашем RDN и допустимых символов в значении ('_' не входит в их число), но тут надо видеть ваш сертификат. Чем вы его сооружали?

К сообщению приложен файл (ga.p7b - 1Kb) cкачать
28 апр 09, 09:06    [7121317]     Ответить | Цитировать Сообщить модератору
Между сообщениями интервал более 1 года.
 Re: Как сделать цифровую подпись на основе сертификата  [new]
LimonFX
Member

Откуда:
Сообщений: 77
О! Моя первая тема на sql.ru :) Помню тогда сильно погрузился в эту задачу, недели на две. Очень помогли примеры на msdn'e. Там правда код на Си, но на delphi с легкостью можно самому переписать.
7 май 10, 15:19    [8742865]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
frozzen
Member

Откуда:
Сообщений: 35
Альт, спасибо!
коды в теме полезные
9 июн 10, 14:58    [8916259]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
_Сыр_
Guest
Да... Ребята... Тема интересная... Жаль что я ее смотрю ))
Подскажите с чего начать?.. Допустим какие компоненты должны быть на 12 дельфе, библиотеки?
7 сен 10, 17:22    [9399288]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
one123456
Guest
Люди, подскажите, как получить данные о сертификате, который находится в контейнере. Ну или ссыли, чет я уже совсем подзадолбался.
8 сен 10, 12:58    [9403528]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Гвость
Guest
Дабы не плодить тему...

Нужно подписать сертификатом файл.

Мой код:

procedure TForm1.btnSignClick(Sender: TObject);
Const
  CERT_STORE_NAME = 'MY';
  MY_ENCODING_TYPE = PKCS_7_ASN_ENCODING or X509_ASN_ENCODING;
var
  wsFind : WideString;
  CertStore: HCERTSTORE;
  Cert:  PCERT_CONTEXT;

  SigParams: CRYPT_SIGN_MESSAGE_PARA;

  strMessage: String;
  strSize: DWORD;

  SignedBlob: CRYPT_DATA_BLOB;
  SignedSize: DWORD;
begin
  //Хранилище
  CertStore := CertOpenSystemStore( 0, 'MY');
  if CertStore = nil then exit;

  //Сертификат
  wsFind := 'ТЕСТ';
  Cert := CertFindCertificateInStore(CertStore, PKCS_7_ASN_ENCODING or X509_ASN_ENCODING,
  0, CERT_FIND_SUBJECT_STR, PWideString(wsFind), nil);
  if Cert = nil then exit;

  //Данные которые подписываем
  strMessage := '123';
  strSize := 3;

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

      SignedBlob.pbData := nil;

      if CryptSignMessage(@SigParams, False, 1, @strMessage, @strSize, SignedBlob.pbData, SignedBlob.cbData) then
        begin
          Memo1.Lines.Add('Размер: ' + intToStr(SignedBlob.cbData));
        end
      else Memo1.Lines.Add('False');

      FreeAndNil(SigParams);

      CertFreeCertificateContext(Cert);
      CertCloseStore(CertStore, CERT_CLOSE_STORE_CHECK_FLAG );
end;

Размер возвращается но после этого пол секунды спустя Эксес Виолешн... Если запускать не из Delphi прога просто закрывается...

для работы с cryptoAPI использую JwaWinCrypt.pas от JEDI

function CryptSignMessage(pSignPara: PCRYPT_SIGN_MESSAGE_PARA;
  fDetachedSignature: BOOL; cToBeSigned: DWORD; rgpbToBeSigned: LPBYTE;
  rgcbToBeSigned: LPDWORD; var pbSignedBlob: LPBYTE; var pcbSignedBlob: DWORD): BOOL; stdcall;
19 окт 10, 12:12    [9632811]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Альт
Member

Откуда: Сибирь
Сообщений: 4581
Не спешите меня хоронить, я тут регулярно появлюсь.

Тут надо просто немного думать.
SigParams.HashAlgorithm.pszObjId := szOID_PKCS_7;
основа подписи - это хеш к данным (вычисленный по какому-то известному аглоритму), который на втором шаге шифруется приватным ключом пары. Разные провайдеры поддерживают разные наборы алгоритмов хеширования. Вот этой строчкой и задается алгоритм хеша. Перечитайте топик, ведь это обсуждалось c LimonFX еще в 2007 году.

FreeAndNil(SigParams);
SigParams - это структура, а не объект и этот код никогда не откомпилируется. И SignedBlob тоже структура. Их элементарно надо занулить перед использованием. Как-то вот так.
FillChar(SigParams, SizeOf(SigParams), 0);
FillChar(SignedBlob, SizeOf(SignedBlob), 0);
Можно смело воткнуть в самое начало функции.
Да и телепаты подсказывают, что у вас в SignedBlob мусор. Вызов CryptSignMessage должен быть двойным. На первом вы запрашиваете размер подписи, потом выделяете память, и лишь на повторном вызове получаете заветную подпись. Профит.
21 окт 10, 09:38    [9646306]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Гвость
Guest
Здравствуйте, Альт.

Сейчас если ставлю перед вызовом CryptSignMessage ваш код:
FillChar(SignedBlob, SizeOf(SignedBlob), 0);

или

SignedBlob.cbData := 0;

функция возвращает False. Но при этом приложение не падает.

Если перед вызовом CryptSignMessage не ставить вышеприведённый код. То при первом вызове функция возвращает размер:
Memo1.Lines.Add('Размер: ' + intToStr(SignedBlob.cbData));

но сразу после вылетает...

с алгоритмом хеширования понятно:
SigParams.HashAlgorithm.pszObjId := szOID_RSA_SHA1RSA;

теперь так... посмотрел в сертификате "алгоритм подписи".
21 окт 10, 13:34    [9648858]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Гвость
Guest
GetLastError даёт 5... ERROR_ACCESS_DENIED
21 окт 10, 15:51    [9650586]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Fiyanov
Member

Откуда: Казахстан
Сообщений: 27
Ура!

было:
function CryptSignMessage(pSignPara: PCRYPT_SIGN_MESSAGE_PARA;
  fDetachedSignature: BOOL; cToBeSigned: DWORD; rgpbToBeSigned: LPBYTE;
  rgcbToBeSigned: LPDWORD; pbSignedBlob: LPBYTE; var pcbSignedBlob: DWORD): BOOL; stdcall;

стало:
function CryptSignMessage(var pSignPara: CRYPT_SIGN_MESSAGE_PARA;
  fDetachedSignature: BOOL; cToBeSigned: DWORD; rgpbToBeSigned: LPBYTE;
  rgcbToBeSigned: LPDWORD; pbSignedBlob: LPBYTE; var pcbSignedBlob: DWORD): BOOL; stdcall;

сделал как у Альта... пока всё решилось:

      if CryptSignMessage(SigParams, False, 1, @strMessage, @strSize, SignedBlob.pbData, SignedBlob.cbData) then
        begin
          Memo1.Lines.Add('Размер: ' + intToStr(SignedBlob.cbData));
          if CryptSignMessage(SigParams, False, 1, @strMessage, @strSize, SignedBlob.pbData, SignedBlob.cbData) then
            Memo1.Lines.Add('Подписано!');
        end
      else Memo1.Lines.Add('False');
22 окт 10, 09:08    [9653996]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Fiyanov
Member

Откуда: Казахстан
Сообщений: 27
Хотя странно... в msdn именно PCRYPT_SIGN_MESSAGE_PARA. Но почему то при:

var
SigParams: CRYPT_SIGN_MESSAGE_PARA;

и вызове функции с этим параметром со знаком "@":

 if CryptSignMessage(@SigParams, ...

Наблюдается как раз мой случай когда после возврата размера прога падает... хотя может дело в ключевом слове var которого в моём заголовочном файле от JEDI не было...
22 окт 10, 09:17    [9654030]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
dv_belyaev
Member

Откуда:
Сообщений: 7
всем привет
в проекте использую шифрование и подпись файлов сертификатами
функции CryptEncryptMessage / CryptDecryptMessage / CryptSignMessage / CryptVerifyMessageSignature

все работает отлично, но есть момент
при расшифровке и проверке подписи у файлов размером ~ 100 Мб и выше
выскакивет ошибка Слишком большое значение ASN1"
в инете ничего кроме http://www.sql.ru/forum/actualthread.aspx?tid=804878 не обнаружил
там чел предлагает "Спасибо, скорее всего нужно блоками дробить."
пробовал расшифровывать кусками - копирую в дополнительный буфер часть файла и на расшифровку
ошибка - "В ASN1 встречен неожиданный конец данных"
наталкивает на мысль что я неправильно загоняю данные в буфер
вот кусок кода (на тестировании подразумеваю что файл больше 100 Мб, но всего 2 куска)

     pStreamIn := TMemoryStream.Create;
     pStreamOut := TMemoryStream.Create;
     pStreamIn.LoadFromFile(filename);


     if pStreamIn.Size > 95 * 1024 * 1024 then // если файл больше 95 Мб
     begin
       for i := 0 to 1 do
       begin
         tpStreamIn := TMemoryStream.Create;
         tpStreamOut := TMemoryStream.Create;
         if i = 0
           then tpStreamIn.CopyFrom(pStreamIn, 95 * 1024 * 1024)
           else tpStreamIn.CopyFrom(pStreamIn, pStreamIn.Size - pStreamIn.Position);
         tpStreamIn.Position := 0;

         if CryptVerifyMessageSignature( @VerifyParams, 0, tpStreamIn.Memory, tpStreamIn.Size, nil, @cbDecodedMessageBlob, nil ) then
         begin
           tpStreamOut.Size := cbDecodedMessageBlob;
           if CryptVerifyMessageSignature(@VerifyParams, 0, tpStreamIn.Memory, tpStreamIn.Size, tpStreamOut.Memory, @cbDecodedMessageBlob, @pCert) then
           begin
             pStreamOut.Size := pStreamOut.Size + tpStreamOut.Size;
             tpStreamOut.Position := 0;
             pStreamOut.CopyFrom(tpStreamOut, tpStreamOut.Size);
           end;
         end else ShowMessage(SysErrorMessage(GetLastError));
         tpStreamIn.Free;
         tpStreamOut.Free;
       end;
       if pStreamOut.Size > 0 then
       begin
         fotopath := UserTEMPDir+'tmp'+ExtractFileExt(temp);
         DeleteFile(fotopath);
         pStreamOut.SaveToFile( fotopath );
       end;
     end else  //  загоняю целиком
     begin
       if CryptVerifyMessageSignature( @VerifyParams, 0, pStreamIn.Memory, pStreamIn.Size, nil, @cbDecodedMessageBlob, nil ) then
       begin
         pStreamOut.Size := cbDecodedMessageBlob;
         if CryptVerifyMessageSignature(@VerifyParams, 0, pStreamIn.Memory, pStreamIn.Size, pStreamOut.Memory, @cbDecodedMessageBlob, @pCert) then
         begin
           fotopath := UserTEMPDir+'tmp'+ExtractFileExt(temp);
           pStreamOut.Size := cbDecodedMessageBlob;
           pStreamOut.SaveToFile( fotopath );
         end;
       end;
     end;
5 июл 11, 16:06    [10926136]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
A-n-d-r-e-y
Member

Откуда:
Сообщений: 53
Откажись от высокоуровневых функций: CryptVerifyMessageSignature и т.п.
5 июл 11, 19:56    [10927764]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
exsander
Member

Откуда:
Сообщений: 8
Здравствуйте!
Пытаюсь найти установленный в системе тестовый сертификат от КриптоПро. За основу взят код из первых сообщений данной темы
При поиске по отпечатку сертификат не найден, GetLastError возвращает ошибку 0x80092004 Объект или свойство не найдено
+

...
implementation
const
  CERT_STORE_NAME = 'ROOT';
  MY_ENCODING_TYPE = PKCS_7_ASN_ENCODING or X509_ASN_ENCODING;
  SignerName : WideString = 'Test Center CRYPTO-PRO';
...
procedure TForm1.btFindHashClick(Sender: TObject);
var
  pSignerCert: PCCERT_CONTEXT;
  hCertStoreHandle : HCERTSTORE;
  iLen, i:integer;
  P:PByte;
  pError:LPCTSTR;
  pCertHash: CRYPT_HASH_BLOB;
begin
  Memo.Clear;
  FillChar(pCertHash,SizeOf(pCertHash),0);

  Memo.Lines.Add(ByteArrayToStr(pCertHash.pbData,pCertHash.cbData));

  hCertStoreHandle := CertOpenSystemStore( 0, PChar( CERT_STORE_NAME ));
  if ( not assigned( hCertStoreHandle )) then
    Err( Format( 'Ошибка при открытии хранилища: %s', [ CERT_STORE_NAME ] ));

  iLen := Length(CertHash.Text) div 2;
  pCertHash.cbData:=iLen;
  pCertHash.pbData:= GetMemory(iLen);

  P:=pCertHash.pbData;
  i:=Length(CertHash.Text)-1;
  while i >= 0 do begin
    P^ := GetHexValue(CertHash.Text[i]) * 16 + GetHexValue(CertHash.Text[i + 1]);
    P := Pointer(Integer(P) + 1);
    dec(i, 2);
  end;
  Memo.Lines.Add(ByteArrayToStr(pCertHash.pbData,pCertHash.cbData));
  pSignerCert := CertFindCertificateInStore(hCertStoreHandle, MY_ENCODING_TYPE, 0, CERT_FIND_SHA1_HASH, @pCertHash, nil);
  if ( not assigned( pSignerCert )) then
    Err('Сертификат не найден')
  else
    Memo.Lines.Add(GetSerialNumber(pSignerCert));
end;



Хочется понять что делается мной не так? Заранее спасибо.
8 янв 12, 14:27    [11870109]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Кто-то уже как бэ
Guest
Здравствуйте, я даже задумываться не хочу, что именно делает вот этот говнокод:
exsander
0x80092004 Объект или свойство не найдено
+

  iLen := Length(CertHash.Text) div 2;
  pCertHash.cbData:=iLen;
  pCertHash.pbData:= GetMemory(iLen);

  P:=pCertHash.pbData;
  i:=Length(CertHash.Text)-1;
  while i >= 0 do begin
    P^ := GetHexValue(CertHash.Text[i]) * 16 + GetHexValue(CertHash.Text[i + 1]);
    P := Pointer(Integer(P) + 1);
    dec(i, 2);
  end;



Хочется понять что делается мной не так? Заранее спасибо.


при поиске там должен быть хеш в виде бина, т.е. если вам на влете приходит хеш в виде хекса, то и приводить его надо через HexToBin. Вы точно в правильном хранилище ищите? Сертификат не в MY? На глаз я больше особых проблем не вижу. В моих поделках эта функция объявлена вот так, сравнивайте со своим:
+
//+-------------------------------------------------------------------------
//  Find the first or next certificate context in the store.
//
//  The certificate is found according to the dwFindType and its pvFindPara.
//  See below for a list of the find types and its parameters.
//
//  Currently dwFindFlags is only used for CERT_FIND_SUBJECT_ATTR,
//  CERT_FIND_ISSUER_ATTR or CERT_FIND_CTL_USAGE. Otherwise, must be set to 0.
//
//  Usage of dwCertEncodingType depends on the dwFindType.
//
//  If the first or next certificate isn't found, NULL is returned.
//  Otherwise, a pointer to a read only CERT_CONTEXT is returned. CERT_CONTEXT
//  must be freed by calling CertFreeCertificateContext or is freed when passed as the
//  pPrevCertContext on a subsequent call. CertDuplicateCertificateContext
//  can be called to make a duplicate.
//
//  pPrevCertContext MUST BE NULL on the first
//  call to find the certificate. To find the next certificate, the
//  pPrevCertContext is set to the CERT_CONTEXT returned by a previous call.
//
//  NOTE: a NON-NULL pPrevCertContext is always CertFreeCertificateContext'ed by
//  this function, even for an error.
//--------------------------------------------------------------------------

function CertFindCertificateInStore( hCertStore :HCERTSTORE;
                                     dwCertEncodingType, dwFindFlags,
                                     dwFindType : DWORD;
                               const pvFindPara;
                                     pPrevCertContext: PCERT_CONTEXT = nil
                                    ): PCERT_CONTEXT ; stdcall;


ну и вызов первого поиска тогда будет вот таким:
CertFindCertificateInStore( FStore, CRYPT_ENCODING, 0, CERT_FIND_HASH, Blob );
9 янв 12, 07:45    [11872757]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
exsander
Member

Откуда:
Сообщений: 8
Добрый день!

С поиском я разобрался, сертификат находится, подписывает и шифрует файл. Непонятен остался один момент. При подписи файла, перед текстом сообщения и после него добавляется информация. При помощи утилиты cryptcp (КриптоПро-шная утилита командной строки) пытаюсь удалить подпись из сообщения. То что находится после текста, т.е. сама подпись удаляется, а вот то, что добавилось перед текстом - остается.
это подписываемый текст
+

 Сорри несколько за глупый вопрос. Просто проект нужно сдать в понедельник. Время поджимает и реально не успеваю.
Что нужно:на основе сертификата (выбирается вручную из установленных) подписать файл цифровой подписью, а потом
проверить эту самую подпись. Стало быть как сделать? Если кто-нибудь кинет рабочим примером на Дельфи, буду очень благодарен.

Это уже подписанный текст
+

0'	*&#9532;H&#9532;ч
 '0'10
*:	
 'd'`Сорри несколько за глупый вопрос. Просто проект нужно сдать в понедельник. Время поджимает и реально не успеваю.
Что нужно:на основе сертификата (выбирается вручную из установленных) подписать файл цифровой подписью, а потом
проверить эту самую подпись. Стало быть как сделать? Если кто-нибудь кинет рабочим примером на Дельфи, буду очень благодарен.1'v0'r0_Є0_>10	*&#9532;H&#9532;ч
	test-uc@tii.ru10	URU10
Umoscow10
Umoscow10U

Integrator IT10USecurity of documents10U
Testing CA
}О_
*:	
	1	*&#9532;H&#9532;ч
0	*&#9532;H&#9532;ч
	1
120129100105Z0/	*&#9532;H&#9532;ч
	1" UBiЙ-_РЪFzй)Х:о<sы&#9488;JН_у&#9488;D8лD0_ш*&#9532;H&#9532;ч
	/1_и0_е0_в0_Я0*:	 2Д-NУЬ![<&#9488;__biiЇ2XПpц_c~HЦы0_°0_Ў¤__0_>10	*&#9532;H&#9532;ч
	test-uc@tii.ru10	URU10
Umoscow10
Umoscow10U

Integrator IT10USecurity of documents10U
Testing CA
}О_
*:
р-є
р-є
р-є


а это текст после удаления подписи при помощи cryptcp
+

0'_	*&#9532;H&#9532;ч
 '_0'_10
*:	
 'd'`Сорри несколько за глупый вопрос. Просто проект нужно сдать в понедельник. Время поджимает и реально не успеваю.
Что нужно:на основе сертификата (выбирается вручную из установленных) подписать файл цифровой подписью, а потом
проверить эту самую подпись. Стало быть как сделать? Если кто-нибудь кинет рабочим примером на Дельфи, буду очень благодарен.1


Что это за инфа в начале текста, и если это часть подписи почему она не удалилась? Заранее спасибо.
29 янв 12, 13:12    [11991185]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
exsander
Member

Откуда:
Сообщений: 8
Вопрос снят, ответ нашелся здесь: http://citforum.ru/security/articles/defense/
29 янв 12, 18:02    [11992167]     Ответить | Цитировать Сообщить модератору
Между сообщениями интервал более 1 года.
 Re: Как сделать цифровую подпись на основе сертификата  [new]
SereZa
Member

Откуда:
Сообщений: 21
в теме встретился кусочек кода:
Store := CertOpenStore( CERT_STORE_PROV_PKCS7, MY_ENCODING_TYPE, 0, 0, @Blob );
я так понимаю что это работает только в случае если не было пароля. а если есть пароль на сертификате - коим образом его указать? это должна быть другая команда для чтения такого сертификата?
23 янв 15, 05:05    [17157318]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Antoshka
Member

Откуда:
Сообщений: 828
SereZa
в теме встретился кусочек кода:
Store := CertOpenStore( CERT_STORE_PROV_PKCS7, MY_ENCODING_TYPE, 0, 0, @Blob );
я так понимаю что это работает только в случае если не было пароля. а если есть пароль на сертификате - коим образом его указать? это должна быть другая команда для чтения такого сертификата?

На сертификате пароля не бывает. Вы про PFX или про eToken?
23 янв 15, 08:37    [17157473]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
SereZa
Member

Откуда:
Сообщений: 21
наверное про PFX... только PFXImportCertStore не хочет работать, пишет что не правильный алгоритм. PFXIsPFXBlob и PFXVerifyPassword замечательно работают. а вот PFXImportCertStore нет. и сам этот сертификат стандартным виндосовским мастером регистрации не устанавливается. в самом конце выдает ошибку про поставщика криптографии. здесь на форуме писали что нужен CSP - только на том сайте java апплет читает этот сертификат и регистрирует его в системе. никакой дополнительной программы при этом не устанавливается.
23 янв 15, 19:46    [17161885]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Antoshka
Member

Откуда:
Сообщений: 828
На каком "том сайте"? Вангую, что в сертификате у вас ГОСТовский ключ, который не распознаётся системой без установки соответствующего криптопровайдера, умеющего работать с алгоритмами ГОСТ. Откуда у вас вообще появился данный сертификат в PFX?
23 янв 15, 20:32    [17162005]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
SereZa
Member

Откуда:
Сообщений: 21
никакой установки криптопровайдера не происходит. все делает java апплет. он может выполнять функции подобного криптопровайдера? своеобразный портэйбл-CSP? такой сертификат выдает налоговая. для своего сайта для отправки налоговой отчетности. задумка была сделать программу, которая бы могла проверять сроки действия и читать имя в подобных сертификатах на жестком диске.


и второй момент, не совсем по теме - но рядом. существует некоторые интернет банкинки, которые как раз используют подобные CSP - там целая программа, что должна быть установлена, висит в процессах, работает как сервис. как можно объяснить браузеру чтоб он запустился без использования этого CSP? режим инкогнито хрома вроде как запускается без него... но проблема в том, что он не запоминает какой сертификат был выбран и опять выкидывает окно выбора сертификата при входе и зацикливается. (имелось в виду окошко с выбором уже установленных в системе сертификатов). как бы так хитро запустить браузер, чтоб и запоминал что выбрано и чтоб без использования CSP? принудительная остановка службы этого криптопровайдера не помогает. только если удалить полностью программы. а вот как бы без удаления...
23 янв 15, 21:51    [17162327]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
sql2012
Member

Откуда: РФ
Сообщений: 624
Antoshka
На каком "том сайте"? Вангую, что в сертификате у вас ГОСТовский ключ, который не распознаётся системой без установки соответствующего криптопровайдера, умеющего работать с алгоритмами ГОСТ. Откуда у вас вообще появился данный сертификат в PFX?


Похоже SereZa Delphi и CryptoAPI]не читает ответы[/url] на свои вопросы.
23 янв 15, 22:02    [17162379]     Ответить | Цитировать Сообщить модератору
Между сообщениями интервал более 1 года.
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Shuraken
Member

Откуда:
Сообщений: 582
Хорошая тема, помогла найти многие ответы на вопросы, но кое-что мне непонятно.

unit JwaWinCrypt;
...
function CryptSignMessage( var pSignPara : CRYPT_SIGN_MESSAGE_PARA;
                           fDetachedSignature : BOOL;
                           cToBeSigned :DWORD;
                     const rgpbToBeSigned : PByteArray;
                           rgcbToBeSigned : PDWordArray;
                           pbSignedBlob : PBYTE;
                           var pcbSignedBlob : DWORD ) : BOOL; stdcall;
....

unit Main;

uses jwaWinScrypt...

// обрабатываем ошибки
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 ] );
  frmSignatureFromCertificate.mDestination.Lines.Text := msg;
  Raise Exception.Create( sError );
end;

// подписываем содержимое одного Memo-поля, выводим в другое Memo поле
procedure TfrmSignatureFromCertificate.acSignFileExecute(Sender: TObject);
const
  CERT_STORE_NAME = 'MY';
  MY_ENCODING_TYPE = PKCS_7_ASN_ENCODING or X509_ASN_ENCODING;
  SignerName : WideString = 'Shuraken';
var
  pSignerCert: PCCERT_CONTEXT;
  hCertStoreHandle: HCERTSTORE;
  SigParams: CRYPT_SIGN_MESSAGE_PARA;
  pStreamIn, pStreamOut: TMemoryStream;
  MessageArray : TByteArray;
  MessageSize : TDWordArray;
  MessageCert : TPCCertContextArray;
  cbSignedMessageBlob: DWORD;
  SignName: WideString;
begin
  mDestination.Clear;
  hCertStoreHandle := CertOpenSystemStore( 0, PChar( CERT_STORE_NAME ));
  if ( not assigned( hCertStoreHandle )) then
    Err( Format( 'Ошибка при открытии хранилища: %s', [ CERT_STORE_NAME ] ));
  try
    SignName := Trim(cmbCertificates.Text);
    pSignerCert := CertFindCertificateInStore( hCertStoreHandle, MY_ENCODING_TYPE, 0, CERT_FIND_SUBJECT_STR, PWideChar( SignName ), nil );
    try
      if ( not assigned( pSignerCert )) then
        Err( Format( 'Сертификат %s не найден', [ SignName ] ));
      pStreamIn := TMemoryStream.Create;
      pStreamOut := TMemoryStream.Create;
      try
        mSource.Lines.SaveToStream(pStreamIn);
        GetMem( MessageArray, 1 * SizeOf( PByte ));
        GetMem( MessageSize, 1 * SizeOf( DWORD ));
        GetMem( MessageCert, 1 * SizeOf( PPCCERT_CONTEXT ));
        MessageArray[ 0 ] := pStreamIn.Memory;
        MessageSize[ 0 ] := pStreamIn.Size;
        MessageCert[ 0 ] := 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 := Pointer( MessageCert );
        if ( not ( CryptSignMessage( SigParams, false, 1, Pointer( MessageArray ), Pointer( MessageSize ), nil, cbSignedMessageBlob ))) then
          Err( 'Размер буфера' );
        pStreamOut.Size := cbSignedMessageBlob;
        if not ( CryptSignMessage( SigParams, false, 1, Pointer( MessageArray ), Pointer( MessageSize ), pStreamOut.Memory, cbSignedMessageBlob )) then
          Err( 'Файл не подписан' );
        pStreamOut.Size := cbSignedMessageBlob;

        mDestination.Lines.LoadFromStream(pStreamOut);
      finally
        FreeAndNil( pStreamIn );
        FreeAndNil( pStreamOut );
      end;
    finally
      CertFreeCertificateContext(pSignerCert);
    end;
  finally
    CertCloseStore( hCertStoreHandle, CERT_CLOSE_STORE_CHECK_FLAG );
  end;
end;

// выводим список личных сертификатов
procedure TfrmSignatureFromCertificate.FormShow(Sender: TObject);
var
  hProv       : HCRYPTPROV;
  hStoreHandle: hcertstore;
  pSignerCert : PCCERT_CONTEXT;
  pCertContext: PCCERT_CONTEXT;
  len: integer;
  CertName: PByte;

function CertNameToWideString: WideString;
begin
  if (len = 0) then
  begin
    Result := '';
    Exit;
  end;

  SetLength(Result, len div 2);
  System.Move(CertName^, Pointer(Result)^, len);
  len := Length(Result);
  if (Result[len] = #0) then
  begin
    Dec(len);
    SetLength(Result, len);
  end;
end;

begin

  if not CryptAcquireContext(hProv, nil, nil, PROV_RSA_SIG, CRYPT_VERIFYCONTEXT) then
    exit;

  hStoreHandle := CertOpenSystemStore(hProv, 'MY');
  if (hStoreHandle = nil) then
  begin
    Err('ErrorOpenStore');
    exit;
  end;

  pCertContext := nil;
  repeat
    pCertContext := CertEnumCertificatesInStore(hStoreHandle, pCertContext);
    if (pCertContext <> nil) then
    begin
      len := CertGetNameString(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, nil, nil, 0);
      if len > 0 then
      begin
        len := len*2;
        GetMem(CertName, len);
        CertGetNameStringW(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, nil, PWideChar(CertName), len);
        cmbCertificates.Items.Add(CertNameToWideString);
        FreeMem(CertName);
        CertName := nil;
      end;
    end;
  until (pCertContext = nil);
  cmbCertificates.ItemIndex := 0;
end;


Какой-то код взят из этой темы, какой-то из других источников, и вот что интересно. У меня на компьютере установлено 5 различных сертификатов. Когда я пытаюсь подписать содержимое memo-поля сертификатом, у которого нет закрытого ключа, то выдаётся ошибка "0х8009200В Не удаётся найти сертификат и закрытый ключ для расшифровки". Делая то же самое сертификатом с закрытым ключом, получаю нормальный результат. Вопрос: почему не удаётся подписать содержимое сертификатом без закрытого ключа.
15 фев 18, 10:55    [21193157]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
Shuraken
Member

Откуда:
Сообщений: 582
Подниму тему. Можно ли вообще подписывать что-либо сертификатом без закрытого ключа?
20 фев 18, 13:02    [21204636]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
__Avenger__
Member

Откуда:
Сообщений: 1971
Shuraken
Подниму тему. Можно ли вообще подписывать что-либо сертификатом без закрытого ключа?


Нет. Подпись только закрытым ключом. Шифровать можно на открытом ключе.
21 фев 18, 13:51    [21207977]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
alexer81
Member

Откуда:
Сообщений: 42
спасибо большое за код!
добавлю при подписи удобнее выбрать сертификат из установленных:
pSignerCert :=CryptUIDlgSelectCertificateFromStore(hCertStoreHandle, self.Handle,'','',0,0,nil);

и вставил (не знаю правильно ли)
CryptAcquireCertificatePrivateKey(pSignerCert,CRYPT_ACQUIRE_CACHE_FLAG,nil,prov,@pdwKeySpec,@pfCallerFreeProv)

перед CryptSignMessage(..) чтобы пароль два раза от контейнера не вводить

А остался у меня такой вопрос - CryptVerifyMessageSignature возвращает исходные данные и сертификат которым подписано, а как достать "Время подписания (1.2.840.113549.1.9.16.2.47 )" через CryptoAPi?
7 сен 18, 16:19    [21668358]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
sql2012
Member

Откуда: РФ
Сообщений: 624
alexer81
спасибо большое за код!
добавлю при подписи удобнее выбрать сертификат из установленных:
pSignerCert :=CryptUIDlgSelectCertificateFromStore(hCertStoreHandle, self.Handle,'','',0,0,nil);

и вставил (не знаю правильно ли)
CryptAcquireCertificatePrivateKey(pSignerCert,CRYPT_ACQUIRE_CACHE_FLAG,nil,prov,@pdwKeySpec,@pfCallerFreeProv)

перед CryptSignMessage(..) чтобы пароль два раза от контейнера не вводить

А остался у меня такой вопрос - CryptVerifyMessageSignature возвращает исходные данные и сертификат которым подписано, а как достать "Время подписания (1.2.840.113549.1.9.16.2.47 )" через CryptoAPi?



CryptUIDlgSelectCertificateFromStore - не особо будет удобен, когда есть сертификаты с одинаковым CN и т.п.
(разные УЦ\разные SN, G- ФИО, которые ОС не покажет в этом списке) + эта функция может "тормозить", не использую её...

К сообщению приложен файл. Размер - 72Kb
8 сен 18, 12:30    [21668843]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
sql2012
Member

Откуда: РФ
Сообщений: 624
поэтому - можно своё что-то изобразить всегда, если захочется, см. снимок выше.
8 сен 18, 12:31    [21668845]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
sql2012
Member

Откуда: РФ
Сообщений: 624
alexer81,

Добавление атрибута даты в ЭЦП
https://qa-help.ru/questions/dobavlenie-atributa-daty-v-ecp
8 сен 18, 12:36    [21668848]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
sql2012
Member

Откуда: РФ
Сообщений: 624
  ca: array[0..10] of CRYPT_ATTRIBUTE;  // перечень подписываемых атрибутов: дата\время, комментарий, использование, csp и т.д.
  FTime: TFileTime;
  cablob: CRYPT_ATTR_BLOB;
  pbAuth : PByte;
...

 ///////////////////////////////////////////////////////////////////////////////////////////
  FTime := DateTimeToFileTime(DTSign);
  CryptEncodeObject(MY_ENCODING_TYPE, szOID_RSA_signingTime, @FTime, nil, cbAuth);
  GetMem(pbAuth, cbAuth);  
  CryptEncodeObject(MY_ENCODING_TYPE, szOID_RSA_signingTime, @FTime, pbAuth, cbAuth); 
  cablob.cbData := cbAuth;
  cablob.pbData := pbAuth;
  ///////////////////////////////////////////////////////////////////////////////////////////
  ca[0].pszObjId := szOID_RSA_signingTime; // дата и время
  ca[0].cValue := 1;
  ca[0].rgValue := @cablob;
  /////////////////////////////////////////////////////////////////////////////////////////////////////////////
  
 // ca[1] .. 9 - другие атрибуты при необходимости
...

  SignerEncodeInfo.rgAuthAttr := @ca; 
    SignerEncodeInfo.cAuthAttr :=1 ; // если использовать только дату\время из ca[]
8 сен 18, 12:47    [21668853]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
alexer81
Member

Откуда:
Сообщений: 42
sql2012, спасибо за ответы! ваша информация пригодится.
Но в подписанном файле есть уже время подписания и потому мой вопрос:
автор
- CryptVerifyMessageSignature возвращает исходные данные и сертификат которым подписано, а как достать "Время подписания (1.2.840.113549.1.9.16.2.47 )" через CryptoAPi?
10 сен 18, 10:34    [21669603]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
sql2012
Member

Откуда: РФ
Сообщений: 624
alexer81,


hMsg: HCRYPTMSG;  
  authAttr: PCRYPT_ATTRIBUTES;
  Attr: PCRYPT_ATTRIBUTE;  
ft: FILETIME;

...
CryptMsgGetParam(hMsg, CMSG_SIGNER_AUTH_ATTR_PARAM, signerIndex, nil, authAttr_len)
выделить память размером authAttr_len  для authAttr 
 CryptMsgGetParam(hMsg, CMSG_SIGNER_AUTH_ATTR_PARAM, signerIndex, authAttr, authAttr_len) 
...
 Attr := CertFindAttribute(szOID_RSA_signingTime, authAttr.cAttr, authAttr.rgAttr);

 dwSize := SizeOf(ft);
    Win32Check(CryptDecodeObject(MY_ENCODING_TYPE, szOID_RSA_signingTime,
    
  Attr.rgValue.pbData, Attr.rgValue.cbData, 0, @ft, dwSize));
    

DT := DecodeFileTime(ft);


+ добавить проверку вызовов, выделение\освобождение памяти
10 сен 18, 10:44    [21669614]     Ответить | Цитировать Сообщить модератору
 Re: Как сделать цифровую подпись на основе сертификата  [new]
alexer81
Member

Откуда:
Сообщений: 42
спасибо. разобрался.
время конечно в в атрибуте с оид - szOID_RSA_signingTime. я приплёл 1.2.840.113549.1.9.16.2.47, не туда наверно глянул
но почему-то атрибуты сразу через CryptMsgGetParam(..CMSG_SIGNER_AUTH_ATTR_PARAM..) не получилось взять
взял через CryptMsgGetParam(..CMSG_SIGNER_INFO_PARAM..)
в CMSG_SIGNER_INFO.AuthAttrs
10 сен 18, 15:50    [21669975]     Ответить | Цитировать Сообщить модератору
Топик располагается на нескольких страницах: 1 2 3 4 5      [все]
Все форумы / Delphi Ответить