Автор: Иван Тимохин 
1.
Цитата:если заранее известно в каком формате файл сертификата (DER, не BASE64), тогда можно сразу вызвать CertCreateCertificateContext на прочитанном блобе
Значит в CertCreateCertificateContext могу просто передать указатель на массив BYTE и длину массива из CERT_BLOB, так?
Да, с учетом, что формат должен быть именно такой - одиночный сертификат (DER, не BASE64). В случае фактического формата p7b требуется сначала извлечь сам сертификат из более сложной структуры, об чуть этом ниже.
Проблема с комбайном определения формата CryptQueryObject вероятно связана с тем, что полученный от УЦ формат данных можно разобрать несколькими способами - в том числе, как подпись p7s или как связку сертификатов p7b. При разборе связки первым идет сертификат УЦ, потом конечный сертификат. Если извлекся сертификат УЦ, он конечно не будет соответствовать контейнеру. В случае снятия подписи как p7s извлекается вообще что-то непонятное.
Корректная альтернатива для чтения p7b на мой взгляд - открыть файл как хранилище сертификатов функцией CertOpenStore с параметром CERT_STORE_PROV_FILENAME(_W) или CERT_STORE_PROV_PKCS7 потом перечислить функцией CertEnumCertificatesInStore сертификаты. В результате получится такой же PCERT_CONTEXT, как при CertCreateCertificateContext. Правда, не забываем, что сертификат УЦ тоже там присутствует и может идти впереди.
Автор: Иван Тимохин 
2.
Цитата:Вообще меня терзают сомнения насчет правильности применения кодировки - если приложение Unicode и функции Crypt* без литеры связаны с Crypt*W, то как вообще имя контейнера оказалось в CP_ACP? Или Вы в не-Unicode приложение привязали Crypt*W функции без литеры? Тогда еще и операционную систему укажите, на *nix бывает нужен UTF-8 вместо Unicode.
Подскажие, о какой литере идет речь? О литере L?
Ведь ниже по коду я произвожу конверацию имени контейнера командой convertCharArrayToLPCWSTR(container).
Речь о литере A/W. Верно, конвертируете. Зачем? Именно про это и вопрос: например, в библиотеке на самом деле нет функции CryptAcquireContext, есть 2 функции: CryptAcquireContextA для char и CryptAcquireContextW для wchar. Однако заголовочный файл в зависимости от поддержки Unicode в самом приложении вводит синоним CryptAcquireContext (без A/W на конце) - для CryptAcquireContextA если поддержки Unicode нет или для CryptAcquireContextW если поддержка есть. Между прочим, функция CryptAcquireContextA на современных windows вызывает внутри MultiByteToWideChar и CryptAcquireContextW.
В коде использован именно этот синоним CryptAcquireContext без A/W и переданы данные в wchar (Unicode). Вариант первый, заголовочный файл от Microsoft, тогда программа с поддержкой Unicode. Однако если программа Unicode, то объявленная в коде строка констант тоже уже по идее должна быть в wchar. Соответственно вопрос зачем ее переобъявлять как char и потом вручную конвертировать в wchar? Хорошо, объявили char, тогда чем CryptAcquireContextA не угодил?
Вариант второй - в программе нет поддержки Unicode, но зачем-то изменен заголовочный файл, CryptAcquireContext указывает на CryptAcquireContextW вместо CryptAcquireContextA. Тогда конвертация немного лишняя, так как можно положиться на автоматику в CryptAcquireContextA.
Автор: Иван Тимохин 
3.
Цитата:не совсем понятно почему вызываете CryptAcquireContext вместо попытки сразу проверить установленную в 4) ссылку на контейнер функцией CryptAcquireCertificatePrivateKey
Можете подсказать для чего это нужно делать? Для проверки корректности результата работы CertSetCertificateContextProperty?
Дело в том, что CertSetCertificateContextProperty не проверяет корректность ссылки на контейнер, просто сохраняет ссылку, не вызывая сам криптопровайдер. Из-за этого не помешает включить шаг проверки, что ссылка на контейнер корректна с точки зрения криптопровайдера. CryptAcquireCertificatePrivateKey производит извлечение ссылки CertGetCertificateContextProperty и CryptAcquireContext внутри. Делать так или иначе конечно Вам решать. Мне привычнее CryptAcquireCertificatePrivateKey.
Автор: Иван Тимохин 
4.
Цитата:Можете не трудиться, замена сертификата в контейнере КриптоПро штатно не поддерживается.
Получается, я не смогу обновить сертификат в контейнера, который расположен на токене Rutoken?
Уточню, я проверял на контейнерах, созданных КриптоПро напрямую (в реестре и на флешке), там: один сертификат на один ключ (в одном контейнере могут быть максимум два ключа - один AT_KEYEXCHANGE и один AT_SIGNATURE), после первичной установки обновить сертификат штатно нельзя, ни через API, ни штатными утилитами - возвращается код ошибки.
С токеном, у которого есть самостоятельное API, ситуация может быть иной. При обращении к токену криптопровайдер КриптоПро только промежуточное звено, сам контейнер создается API токена, при активном режиме токена можно вообще без КриптоПро подписывать. Тем не менее предположу, что КриптоПро не даст заменить сертификат и на токене.
Автор: Иван Тимохин 
5.
Цитата:Другими словами, технически возможно у двух сертификатов проставить ссылку на один контейнер (шаг 4), но в таком контейнере не должно быть сертификата (то есть не выполнять шаг 7).
Можете подсказать что мне даст проставление ссылки на контейнер? Я смогу воспользоваться таким сертификатом/контейнером для подписи/шифрования?
Теоретически сможете, а практически зависит от программы, где используется ключ.
В целом для подписания шифрования нужно два объекта - сертификат и контейнер с ключевой парой, остальное только упрощает их нахождение и сопоставление. Есть разные подходы подходы к поиску ключей и сертификатов:
Первый - перечислять контейнеры, потом находить сертификаты в хранилище. Для этого достаточно уметь прочитать открытый ключ из контейнера (даже если в контейнере нет сертификата, открытый ключ все равно там есть), по открытому ключу можно найти сертификат в хранилище. Плюс в том, что ссылка в хранилище не очень-то нужна. Минус в том, что сертификат должен быть в хранилище; время обращения к контейнеру само по себе большое, а тут время всего поиска пропорционально произведению количества контейнеров на количество сертификатов. С учетом что обычно количества сравнимые выходит почти квадратичное от количества сертификатов. Токен должен быть подключен чтобы показался сертификат.
Второй подход - перечислять контейнеры, потом находить сертификаты в контейнере. Плюс в том, что хранилище вообще не нужно, как и ссылка в нем; гарантировано что если нашлось, то контейнер вставлен. Минус - что сертификат должен присутствовать в контейнере. Токен должен быть подключен чтобы показался сертификат. Данный подход реализован в плагине госуслуг: если в контейнере нет сертификата, ничего не будет отображено. Плагин госуслуг еще чего-то сверяет по хранилищу, так что время выходит как у первого варианта (квадратичное). А уж если подключить токен и в хранилище 15-16 сертификатов, то можно успеть подремать пока браузер нарисует список сертификатов для госуслуг.
Третий подход - наоборот перечислять сертификаты в хранилище и по ссылке переходить к контейнеру. Плюс - контейнеры не перечисляются, обращение идет к тому, что указан в ссылке в хранилище у конкретного сертификата; общее время поиска линейно зависит от числа сертификатов; в контейнере может не быть сертификата. Если же при неопределенности где искать сертификат перечисление контейнеров все же идет, то пробуются в фоне только 3 контейнера, потом предлагается указать нужный контейнер явно. Минус - ссылка на контейнер должна быть в хранилище; контейнер на токене может оказаться не подключен в данный момент, тогда выйдет предложение подключить токен. Данный подход реализован в плагине КриптоПро. Уже при 4-6 сертификатах третий подход значительно быстрее чем первые два. Например, удобно в программе запомнить отпечаток сертификата, для него есть стандартный поиск по хранилищу сертификатов.
В итоге, можно сказать, что устанавливая сертификат в контейнер оптимизируется поиск для второго подхода. Устанавливая сертификат в хранилище - для первого и третьего. Устанавливая ссылку на контейнер в хранилище - для третьего. Приведенный отрезок кода добавляет в контейнер и прописывает ссылку на контейнер, то есть универсально задуман на все три подхода (но не добавляет сертификат в хранилище!), а при использовании конкретного подхода можно часть шагов пропустить.