.

электронный полис омс
Привет, всем) Вот сел написать обещанную статью по электронным полисам омс.

Предисловие

Хочу вам сказать несколько слов о статье до того как её начали читать. Я бы даже не называл этот материал «статьей» - это скорее детективная история о том, как я пытался «хакнуть» электронные полисы омс. «Хакнуть» - в моем сленге, означает взломать проблему. До настоящего момента, если не считать работы над GPS-навигаторами и сканером штрих-кодов, я почти не работал напрямую с железом. Исходя из выше сказанного, мне было очень интересно попробовать найти решение для смарт-карт. При условии, что до этого момента я не чего не знал по этой теме.
Причина почему данный материал я подаю именно в формате «детективная история», очень проста - я хочу вам объяснить как решать проблемы, когда вы в первый раз столкнулись задачей. Сам был новичком и помню не раз, когда начальник ставит мне задачу на реализацию, я сажусь за стол и думаю: «Блин…я в жизни это не реализую…». Но ищу инфу, разбираюсь и в итоге делаю.
И еще один маленький момент. Это уже не первая моя статья о ОМС и программном обеспечении. Я работаю в страховой компании, занимающийся ОМС (для тех кто еще не в курсе). Варюсь в этой теме давно, поэтому много могу рассказать. В общем, это обращение к читателям - если вам будет интересно, оставите комментарий на мой вопрос: БУДЕТ ЛИ ВАМ ИНТЕРЕСНО ЧИТАТЬ ПРО ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ В ОМС, О ТОМ КАК ОБХОДИТЬ ВСЯКИЕ ИЗДЕРЖКИ ЗАКОНОДАТЕЛЬСТВА И КАК ВСЕ ЭТО РАБОТАЕТ В ЖИЗНИ? Ответ можете отставить в комментарии к статье.
Желаю приятного прочтения. Буду надеяться этот материал поможет в ваших изысканиях.

Начало.

Все началось не так давно, почти пару месяцев назад. Я впервые завел блог и думал о чем написать. Поискал в инете и понял, что не кто до сих пор не писал про штрих-код полиса омс и как его разобрать. Написал статью. И у меня появились, и читатели в блоге и, я так понимаю, постоянные читающие. На почту повалилось много писем. Я отвечал на все. Было очень интересно.
Были письма о проблеме с электронными полисами. И вот я твердо решил разобрать, что тут к чему. Взял на работе картридер для смарт-карт , старый электронный полис и началось.

Я использовал картридер ACR38U-I1.

По самой низкой цене можно купить здесь. Я советую его потому, что (вроде как) он отлично подходит именно для этих целей.

Первый этап. Сбор информации.

Пришел домой и первым делом забил в гугл запрос «работа со смарт-картами delphi». Оказалось, что рабочих примеров не так много. Но поиграв не долго  с гуглом. Я выяснил следующие.
1. Для работы со смарт-картами есть специальный открытый протокол «PC/SC» - (от англ. Personal Computer/Smart Card). С помощью протокола можно осуществлять общем данными между компьютером и смарт-картой. Самый главный момент: протоколу по фиг какой картридер используется.
2. В Windows есть специальная динамическая библиотека реализации протокола обмена «PC/SC». Называется Winscart.dll. (В Linux есть аналог pcsc-lite package ).
3. В поисках примеров работы со Winscart.dll, я нашел не плохие исходники работы со смарт-картами на Delphi.
Это сжато то, что я нашел почти сразу. При дальнейшем анализе собранной информации стало интереснее.

Второй этап. Обрабатываем информацию.

С начала я решил разобраться с протоколом «PC/SC». Нашел неплохую статью с описание протокола PC/SC правда она описывала все на C++. Но не беда, стал разбираться. Общая схема следующая.

схема работы со смарт картами
схема работы со смарт картами

Подключение EstablishContext().

RESPONSECODE EstablishContext (
IN DWORD Scope, // A scope indicator
IN DWORD Reserved1, // Reserved for future use to allow privileged
// administrative programs to act on behalf of another user
IN DWORD Reserved2 // Reserved for future use to allow privileged
// administrative programs to act on behalf ofanother terminal
)

phContext - handle на объект ResourceManager (объект в котором будет храниться информация о подключенных картах и картридерах.) Она не указана в данной в спецификации, но в первоисточнике от «msdn» она прописана. По идее это должна быть глобальная переменная в вашей программе. То есть вы в процедуру отдаете глобальную переменную. Она отрабатывает в результате большое число. Типа DWORD.
Reserved1, Reserved2 – просто зарезервированные переменные.
Обратите внимание на тип данных DWORD (это охренительно большое число, в C++ похожий тип данных Long.) Я специально говорю об этом, в конце стать я приведу ссылку на исходники программы она С++ (для нее понадобится Microsoft Visual Studio). Сразу обращаю ваше внимание на мелочи.
Ну, теперь немного слов о самой работе функции. Как я уже говорил, в функцию передается указатель на глобальную переменную. Функция отрабатывает, в глобальную переменную записывается handle, а в результат огромное число типа (DWORD). Если вернулся 0, все хорошо можно работать, отличное от 0 – это код ошибки. Список кодов ошибок.

MSDN:SCardEstablishContext

Дальше получаем список картридеров.

RESPONSECODE ListReaders(
IN STR[] Groups // Array of strings containing Group names of interest
OUT STR[] Readers // Array of strings containing Readers within the Groups
)

Подключение к карте.

RESPONSECODE Connect(
IN STR ReaderName // Friendly name for a Reader
IN DWORD Flags // Desired access mode information
IN DWORD PreferredProtocols // Card communications protocols that may be used
OUT DWORD ActiveProtocol // Protocol actually in use
)

Дальше выполнения первой функции я не добрался. Потому что найденные исходники для Delphi выдавали ошибку. Я ни как не мог найти соответствия в списке. В общем, на некоторое время я забил на эту тему.
Пока случайно не наткнулся на прогу, которая читает инфу, со смарт-карты (электронного полиса омс). Запустить лучше прям через cmd.

пример проги чтения элетноных полисов
Все красиво. Тут я еще больше загорелся. Сторонняя программа это хорошо, но я тоже хочу уметь доставать данные из смарт-карты (электронного полиса). Скачать программу для чтения электронных полисов.

Поехали дальше.
Рядом с найденной программой лежала инструкция в word-e. Прошло несколько недель, прежде чем я решил: покурю мануал. И случайно в тексте натыкаюсь на название функций протокола «PC/SC». Но там были еще функции, названия которых я не знал.
Ну думаю: я не знаю, но гугл точно должен знать. Забиваю название некоторых функций. Гугл знал, я в нем не ошибся - мне выдали три ссылки. Вторая вела на репозиторий GitHub.
Открыл читаю код программы….

[C++]
//Установление соединения с смарт-карт API
manager.EstablishContext(SmartCard.PCSC.READERSCONTEXTSCOPE.SCOPE_USER);
//Вывод списка ридеров в консоль
if (manager.OfType().Select(s => s.ReaderName).ToList().Contains(args["roi"]))
{
//Получение объекта ридера
ISCard card = manager[args["roi"]];
//Создание объекта для работы с картой полиса ОМС
PolicySmartcardBase policy = new PolicySmartcardBase(card);
//Подключение к карте полиса ОМС
policy.Connect();
//Чтение информации о владельце полиса ОМС
OwnerInformation owner_info = policy.GetOwnerInformation();
if (owner_info != null)
{
printf("Информация о владельце:");
printf("{0} = {1}", "Фамилия".PadRight(35, ' '), FormatPolicyText(owner_info.Identity_1, "Отсутствует"));
printf("{0} = {1}", "Имя".PadRight(35, ' '), FormatPolicyText(owner_info.Identity_2, "Отсутствует"));
printf("{0} = {1}", "Отчество".PadRight(35, ' '), FormatPolicyText(owner_info.Identity_3, "Отсутствует"));
printf("{0} = {1}", "Пол".PadRight(35, ' '), owner_info.Sex == 1 ? "Мужской" : owner_info.Sex == 2 ? "Женский" : "Неизвестно");
printf("{0} = {1}", "Дата рождения".PadRight(35, ' '), FormatPolicyDate(owner_info.BirthDate, "Отсутствует"));
printf("{0} = {1}", "Место рождения".PadRight(35, ' '), FormatPolicyText(owner_info.BirthPlace, "Отсутствует"));
if (owner_info.Citizenship != null)
{
printf("Гражданство:");
printf("{0} = {1}", "Код страны".PadRight(35, ' '), FormatPolicyText(owner_info.Citizenship.CoutryCode, "Отсутствует"));
printf("{0} = {1}", "Кириллическое название".PadRight(35, ' '), FormatPolicyText(owner_info.Citizenship.CoutryCyrillicName, "Отсутствует"));
}
else
{
printf("{0} = {1}", "Гражданство".PadRight(35, ' '), "Отсутствует");
}
printf("Информация о полисе:");
printf("{0} = {1:D16}", "Номер полиса".PadRight(35, ' '), owner_info.PolicyNumber);
printf("{0} = {1}", "Дата выпуска".PadRight(35, ' '), FormatPolicyDate(owner_info.ExpireDate, "Отсутствует"));
printf("{0} = {1}", "Срок действия".PadRight(35, ' '), FormatPolicyDate(owner_info.ExpireDate, "Не ограничено"));
printf("{0} = {1}", "СНИЛС".PadRight(35, ' '), FormatPolicyText(owner_info.SNILS, "Отсутствует"));
printf("Чтение атрибутов безопасности карты полиса ОМС");
SecurityInformation SOD = policy.GetSecurityInformation();
printf("{0} = {1}", "Статус ЭЦП".PadRight(35, ' '), owner_info.VerifyEDS(SOD) ? "Верна" : "Неверна");
[/C++]

И вдруг меня прошибает: результат работы этого кода на скриншоте выше. Я понимаю: «мой бог», я нашел исходники программы на С++ для работы со электронными полисами (смарт-картами). Да это просто пипец какая удача…
Дальше я опять на время забил на эту тему. Потом получил комментарий с вопросом и собственно эта статья ответ.
Исходники я поковырял, есть одна идея как реализовать это на Delphi. Ну об этом чуть позже. Подпишитесь на уведомление по E-mail, чтоб не пропустить.)))

С вами был ваш Shinobi.

P.S. Нравится материал. Длитесь в твитере, лайкайте в контакте и так далее. Все спасибо))

8 комментариев

  1. uranic

    Получилось ли Delphi получить код ОГРН из электронного полиса?

    Ответить
    • admin_shinobi

      Честно, с момента написания статьи о электронных полисах, я больше ими в плотную не занимался.НО…через две недели примерно или чуть раньше, у меня плану написание модуля работы с электронными полисами для их перегистрации (т.е. чтоб менять этот самый ОГРН). Конечно, обо всем этом я напишу в блоге, подпишитесь на обновление по электронной почте. Отвечая на ваш вопрос, когда я перенесу все в Delphi просмотр данных о полисе я вам напишу на почту.

      Ответить
  2. uranic

    Честно говоря не понял зачем в полисе менять ОГРН, но с интересом почитаю статью. Считывание полисов в итоге внедрили, заказчику поставили, ждем отзывов

    Ответить
    • admin_shinobi

      Да все просто на бумажных полисах на оборотной стороне полиса, написана принадлежность к какой страховой компании имеет этот полис. На смарт картах такой информации нет. На пример у нас на них клеят небольшие наклейки, что бы в больнице знали, что это наш застрахованный. НО в смарт-картах записан ОГРН, по которому по факту и должны определять принадлежность к страховой компании. Есть такая операции как «Перегистраци» и «Смена СМО», при этим операция меняется принадлежность к страховой компании, т.е. собственно смена ОГРН компании к которой привязан полис. Надеюсь я ответил на ваш вопрос)

      Ответить
      • Denis

        Исходники на C# под .NET 4.0 если что.
        Кроме ОГРН нужно ОКАТО считывать — тоже есть на карточке — у крупных страховых компаний может быть один и тот же ОГРН по всем регионам. В общем нужно фильтровать справочник страховых по ОКАТО+ОГРН и если там несколько вариантов (т.к. по одному ОКАТО может быть несколько филиалов с одинаковым ОГРН но разным кодом СМО) — дать пользователю возможность вручную выбрать из этих вариантов.

        Ответить
  3. Александр

    Вместе с библиотеками идут примеры. Кто-нибудь проверял их работоспособность? читать информацию о застрахованном, о самой страховой получается, а вот чтение истории, там где нужно использовать токен,ничего не получается(
    пишет что ошибка аутентификации. Пароль передаю как параметр(метод Authentification_External). Пароль верный. Может подскажет кто нибудь.

    Ответить
  4. Андрей

    На Delphi всё отлично реализуется. Для работы со сканером используем WinsCard. Для чтения используются простые APDU команды. Если интересно, могу выслать результат работы.

    Ответить
  5. Александр

    «Если интересно, могу выслать результат работы»
    Спасибо за ответ, но очень поздно) сменил место работы.

    Ответить

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *