for flooders
:: Главная :: Решения :: Статьи :: Сайт М. Дроздова :: Файловый архив :: Книга по VFP 9 :: Русский Help Online :: OFF-LINE Форум
   Л и с о в о д ы   в с е х   с т р а н,  о б ъ е д и н я й т е с ь !!!  

Список Форумов  :: Visual Foxpro, Foxpro for DOS
   :: Помощь сайту :: 

Прямое чтение и запись BLOB-объектов (Crypto API)
rvc44
Автор

Сообщений: 2170
Откуда: Тамбов
Дата: 30.05.18 16:55:29ОтветитьЦитировать
Всем привет!

Несколько лет назад приходилось писать код для получения серийного номера сертификата ключа подписи прямым чтением BLOB-объекта:
  
 * ПОЛУЧИМ СЕРИЙНЫЙ НОМЕР СЕРТИФИКАТА КЛЮЧА ПОДПИСИ ПРЯМЫМ ЧТЕНИЕМ BLOB-ОБЪЕКТА  
 * Пример серийного номера сертификата: 1e 8d 70 93 00 00 00 00 02 34  
  LOCAL lnSNLen, lcSerialNumber, lcSNReversed, lcSNHex  
    
 * Размер поля "Серийный номер" хранится в 4 первых байтах BLOB-объекта CRYPT_INTEGER_BLOB,  
 * содержащегося внутри структуры CERT_INFO, по смещению 5 в данной структуре.  
  lnSNLen = CTOBIN(SubStr(lcCERT_INFO, 5, 4), "4RS")  && 10 байт. CTOBIN converts a binary character representation to a numeric value.  
 * = MessageBox('Длина поля "Серийный номер" сертификата составляет ' + AllT(Str(lnSNLen))+' байт',64,'Сообщение')  
    
 * Ссылка на значение поля "Серийный номер" хранится в 4 последних из 8 байтах BLOB-объекта CRYPT_INTEGER_BLOB,  
 * содержащегося внутри структуры CERT_INFO, по смещению 9 в данной структуре.  
  				  
 * Поскольку содержимое BLOB-объекта CRYPT_INTEGER_BLOB числовое, а не символьное,  
 * то API-функцию CertNameToStr() для получения данных BLOB-объекта здесьприменять нельзя!  
 * Прочитаем серийный номер сертификата ключа подписи напрямую из BLOB-объекта:  
  lcSerialNumber = SYS(2600, CTOBIN(SubStr(lcCERT_INFO, 9, 4), "4RS"), lnSNLen)  
    
 * Big Endian bit = Обратный порядок байтов  
 * вывод серийного номера необходимо осуществить с порядком байтов Big Endian bit,  
 * cделаем реверс порядка байтов в строке с серийным номером до её преобразования в формат Hex  
  lcSNReversed = ""  
  For lnI=m.lnSNLen To 1 Step -1  
  	lcSNReversed = lcSNReversed + SubStr(lcSerialNumber, lnI, 1)  
  EndFor  
    
 * Преобразуем серийный номер в 16-ричный формат:  
  m.lcSNHex = Space(0)  
  For m.lnI=1 to lnSNLen  
  	m.lcSNHex = m.lcSNHex + " "+SubStr(Transform(Asc(SubStr(m.lcSNReversed,m.lnI,1)),"@0"),9)  
  EndFor  
  				  
  m.lcSNHex = AllT(m.lcSNHex)

Сейчас озадачился вопросом реализации обратного действия, т.е. как осуществить прямую запись CryptoAPI BLOB-объекта из проекта на VFP. В общем, есть такой Сишный код:
  
  // Add the cosigner's certificate to the message.  
  CERT_BLOB CosignCertBlob;  
  CosignCertBlob.cbData = pCosignerCert->cbCertEncoded;  && В первых 4 из 8 байтах BLOB-объекта хранится размер поля  
  CosignCertBlob.pbData = pCosignerCert->pbCertEncoded;  && В последних 4 из 8 байтах BLOB-объекта хранится значение поля  
который нужно "перевести" на VFP.

Немного прикинул, тряхнув стариной, и вот что у меня получилось:
  
 * 30.05.2018, ПРЯМАЯ ЗАПИСЬ BLOB-ОБЪЕКТА VFP  
 * Add the cosigner's certificate to the message.  
 * Original MSDN code: hттps://msdn.microsoft.com/en-us/library/windows/desktop/aa904939(v=vs.85).aspx  
 * SYS(2600) return Pointer as String  
 * CTOBIN - Converts a binary character representation to a numeric value  
 * BINTOC - Converts a numeric value to a binary character representation.  
 * Указатель на закодированный сертификат, размером 4 байта. SYS(2600) - Return Pointer as String  
  pbCertEncoded = CTOBIN(SubStr(lcCERT_CONTEXT, 5, 4), "4RS")  && Указатель на закодированный сертификат CERT_CONTEXT -> BYTE* pbCertEncoded  
 * Размер, в байтах, закодированного сертификата  
  cbCertEncoded = CTOBIN(SubStr(lcCERT_CONTEXT, 9, 4), "4RS")  && Размер, в байтах, закодированного сертификата CERT_CONTEXT -> DWORD cbCertEncoded  
    
 * BLOB-объект (CryptoAPI) состоит из 8 байт.  
 * В первых 4 из 8 байтах BLOB-объекта хранится размер поля  
 * В последних 4 из 8 байтах BLOB-объекта хранится значение поля  
  lcCosignCertBlob = 0 + BINTOC(cbCertEncoded, "4RS") + BINTOC(pbCertEncoded, "4RS")  
 *-- Выделяем 8 байт под объект BLOB  
 *-- hттp://msdn2.microsoft.com/en-us/library/aa366781.aspx -Memory Management Functions...  
 *-- Запрашиваем память у Windows и получаем указатель на неё (дескриптор)  
 *-- 0x0040 означает неперемещаемую (фиксированную) память, заполненную нулями  
 *-- m.lnLen указывает размер выделяемой памяти в байтах.  
 *-- Если память под pbCERT_BLOB выделять пустой строкой в VFP, то работать не будет  
  pbCERT_BLOB = LocalAlloc(0x0040, 8)  
 * Копируем объект BLOB в выделенную память  
  SYS(2600, pbCERT_BLOB, 8, lcCosignCertBlob)  
    
 * Структура CERT_CONTEXT определана в MSDN:  
 * hттp://msdn.microsoft.com/en-us/library/aa919756.aspx  
 *|typedef struct _CERT_CONTEXT {  
 *|  DWORD dwCertEncodingType;                       0:4  
 *|  BYTE* pbCertEncoded;                            4:4 Указатель на закодированный сертификат, имеет размер 4 байта.  
 *|  DWORD cbCertEncoded;                            8:4 Размер, в байтах, закодированного сертификата.  
 *|  PCERT_INFO pCertInfo;                          12:4 Указатель на структуру CERT_INFO  
 *|  HCERTSTORE hCertStore;                         16:4 Указатель  
 *|} CERT_CONTEXT, *PCERT_CONTEXT; total 20 bytes  
 *|typedef const CERT_CONTEXT *PCCERT_CONTEXT;  
    
 * Структура CERT_INFO определана в MSDN:  
 * hттp://msdn.microsoft.com/en-us/library/aa919721.aspx  
 *|typedef struct _CERT_INFO {  
 *|  DWORD dwVersion;                                0:4  && Certificate's version number (CERT_V3)  
 *|  CRYPT_INTEGER_BLOB SerialNumber;                4:8  && BLOB (Cryptography) structure containing the certificate's serial number. The least significant byte is the zero byte of the pbData member of SerialNumber. The index for the last byte of pbData is one less than the value of the cbData member of SerialNumber. The most significant byte is the last byte of pbData. Leading 0x00 or 0xFF bytes are removed. For more information, see CertCompareIntegerBlob.   
 *|  CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm; 12:12  
 *|  CERT_NAME_BLOB Issuer;                         24:8  && Поле "Поставщик" (issuer) содержит имя УЦ, издателя сертификата  
 *|  FILETIME NotBefore;                            32:8  
 *|  FILETIME NotAfter;                             40:8  
 *|  CERT_NAME_BLOB Subject;                        48:8  && Certificate subject's encoded name.  
 *|  CERT_PUBLIC_KEY_INFO SubjectPublicKeyInfo;     56:24  
 *|  CRYPT_BIT_BLOB IssuerUniqueId;                 80:12  
 *|  CRYPT_BIT_BLOB SubjectUniqueId;                92:12  
 *|  DWORD cExtension;                             104:4  
 *|  PCERT_EXTENSION rgExtension;                  108:4  && Указатель на CERT_EXTENSION  
 *|} CERT_INFO, *PCERT_INFO; total 112 bytes  

Прошу оценить свежим взглядом и сделать вывод: "взлетит" данный код или нет? Что-то мне не очень нравится:
  
  BYTE* pbCertEncoded
Ratings: 0 negative/0 positive

Re: Прямое чтение и запись BLOB-объектов (Crypto API)
Igor Korolyov

Сообщений: 31499
Дата: 31.05.18 12:46:17ОтветитьЦитировать
В чём смысл всего этого безобразия? Не понимаю зачем лезть внутрь pbCertEncoded... Создаётся сертификат совсем по другому.


------------------
WBR, Igor
Ratings: 0 negative/0 positive

Re: Прямое чтение и запись BLOB-объектов (Crypto API)
rvc44
Автор

Сообщений: 2170
Откуда: Тамбов
Дата: 01.06.18 01:15:58ОтветитьЦитировать
Игорь, Вы не уловили сути. Здесь ни о каком создании сертификата речь даже не идет.
Глобальная цель - решить задачу по подписанию определенного файла второй подписью.
Не подписать еще раз подписанные данные, а именно наложить вторую, а если потребуется и третью отсоединенную (detached) подпись в формате PKCS#7, причем, что важно, в одном, а не 2 или 3 отдельных файлах. Последнее время, Росреестр только так принимает в электронном виде подписанные концессионные соглашения и сведения о произведенной кадастровой оценке объектов недвижимости. На один XML-файл требуют один SIG-Файл, содержащий 3 разных подписи внутри, а три разных SIG-файла к одному XML не допускается. Найденный алгоритм осуществления подобного безобразия, предполагает именно такой код. У Вас есть другие идеи, как это осуществить?

Обратите внимание на ссылку "Original MSDN code" выше, заменив "тт" на "tt" в http в третьей сверху строке комментариев к коду (операция CoSign - "Доподписание").



Исправлено: rvc44, 01.06.18 01:50
Ratings: 0 negative/0 positive

Re: Прямое чтение и запись BLOB-объектов (Crypto API)
Igor Korolyov

Сообщений: 31499
Дата: 01.06.18 13:41:31ОтветитьЦитировать
Т.е. если это всё конкретно для
CERT_BLOB CosignCertBlob;  
  CosignCertBlob.cbData = pCosignerCert->cbCertEncoded;  
  CosignCertBlob.pbData = pCosignerCert->pbCertEncoded;

То должно работать во-первых и без LocalAlloc (в MSDN нет указаний на то что для этой функции нужно как-то "по особому" память для структуры выделять - да и в коде примера там тривиальная локальная переменная и потом просто указатель на неё передаётся в функцию),
во-вторых по сути "внутрь" структуры чей адрес в pbCertEncoded лежит ты и не лезешь - только сам адрес (указатель) берёшь из другой структуры. А раз так, то всё упрощается до
lcCosignCertBlob = SubStr(m.lcCERT_CONTEXT, 9, 4) + SubStr(m.lcCERT_CONTEXT, 5, 4);
И да, в таком случае проблем нет - не ты своим прикладным кодом, а сами АПИ функции лезут внутрь этих бинарных закодированных структур


------------------
WBR, Igor
Ratings: 0 negative/0 positive



Извините, только зарегистрированные пользователи могут писать в этом форуме.

On-line: 53 Grummek  and Guests: 52


© 2000-2018 Fox Club 
Яндекс.Метрика