Электронные ключи HASP выпускаются c различными интерфейсами, в статье речь идет только о USB-брелках HASP HL.
Технологии HASP HL и HASP SRM на данный момент не взломаны, надежность защиты целиком и полностью зависит от человека, который будет внедрять защиту в свое приложение, момент "внедрения защиты" в статье опущен, а сама статья касается непосредственной манипуляцией с ключами, как примитивным устройством (open, close, read, write, etc) + распознавание вставки и извлечения ключа из USB-порта.
Сборка hasp_net_windows.dll, которая шла вместе с драйверами не поддерживает установку двух и более ключей одной серии на один компьютер одновременно. Поэтому класс по работе с ключами логично объявить статическим и первый воткнутым и опознанный ключ считать единственным в системе.
После добавление в Reference и прописывания namespace Aladdin.HASP можно начинать работать с ключами.
Первое что необходимо сделать, определить поля:
private static HaspFeature feature = HaspFeature.FromProgNum(0);//номер функции по умолчанию, поле касается защиты софта и не вписывается в тему статьи private static HaspStatus status;// статус выполненной операции private static int id = 0;// хранение идентификатора личного ключа Для регистрации событий вставки и извлечения ключа введем перечисление last_operation с двумя состояниями public enum last_operation { INSERT,// ключ вставлен REMOVE // ключ извлечен }и поле, характеризующее текущее состояние ключа:
private static last_operation lastoperation;Ещё потребуются два поля - экземпляров Hasp hasp - сам ключ, и HaspFile hasp_file - его файловой системы
Для проверки открыта или нет сессия по работе с ключом, объявим свойство-геттер:
private static bool flg_isopen; public static bool isopen { get { return flg_isopen; } } private static bool flg_isopen;Два события на вставку и извлечения ключа:
public static event EventHandler KeyArrived; public static event EventHandler KeyRemoved;И два приватных слушателя usb-портов:
private static ManagementEventWatcher watcher1; private static ManagementEventWatcher watcher2;Вот и все поля.
Перейдем к методам. Первое что надо сделать,- открыть сессию по работе с ключами.
public static void open() if (flg_isopen) return;// открывать два раза не хорошо, можно на открытии следует вызвать метод Close, если задача позволяет // инициализация ключа feature.SetOptions(FeatureOptions.NotRemote, FeatureOptions.Default);// для примера: только локальные ключи hasp = new Hasp(feature);Далее необходимо подписаться на сообщения системы о подключении/извлечении ключа, делается это через WQL-технологию из namespace System.Management. Для регистрации отключения:
WqlEventQuery queryRemove = new WqlEventQuery("Select * " + "FROM __InstanceDeletionEvent within 1 " + "WHERE TargetInstance ISA 'Win32_PnPEntity' "+ "AND TargetInstance.Description='Aladdin HASP HL Key' "+ "AND TargetInstance.ClassGuid='{36FC9E60-C465-11CF-8056-444553540000}'"); watcher1 = new ManagementEventWatcher(queryRemove); watcher1.EventArrived += new EventArrivedEventHandler(watcher_remove); watcher1.Start();Важными являются строчки с дескриптором и гуидом класса. По аналогии можно отслеживать подключение принтеров, флешек и тд и тп. Технология WQL очень мощная и гибкая.
Для регистрации подключения к usb-порту точно так же, но "FROM __InstanceCreationEvent within 1 "
flg_isopen = true; // далее взводится флаг об успешном открытии сессии if(detecting_key()) // и проверяется, вставлен ли уже ключ KeyArrived(null, EventArgs.Empty);На этом метод Open заканчивается. В нем использованы обработчики событий watcher_remove и watcher_insert и приватный метод detecting_key() возвращающий true, если ключ уже вставлен на момент вызова Open. Опишем его:
public static bool detecting_key() { if (Detected("Aladdin USB Key", "Aladdin Knowledge Systems", "VID_0529&PID_0001")) { if ((id=KeyId) != 0) { lastoperation = last_operation.INSERT; return true; } } else lastoperation = last_operation.REMOVE; return false; }Тут происходит вызов метода обнаружения оборудование в дереве устройств машины с помощью технологии WMI и обращение к свойству KeyId, для начала опишем сам метод Detected: первый аргумент,- дескриптор оборудования, дальше производитель и последний аргумент это PID&VID, всё в виде строк:
private static bool Detected(string Description, string Manufacturer, string VersionIDandVendorID) { bool rez = false; ManagementScope scope = new ManagementScope("root\\CIMV2"); scope.Options.EnablePrivileges = true; ObjectQuery query = new ObjectQuery("Select * From Win32_USBControllerDevice"/*Win32_USBControlerDevice*/); ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query); StringBuilder Response = new StringBuilder(); foreach (ManagementObject mgmtObj in searcher.Get()) { string[] arrDeviceID; ManagementObjectSearcher mySearcher = new ManagementObjectSearcher("Select * From Win32_PnPEntity " + "Where DeviceID =" + mgmtObj["Dependent"].ToString().Replace("\"", "'").Split('=')[1]/*Win32_PnPEntity*/); foreach (ManagementObject mobj in mySearcher.Get()) { arrDeviceID = mobj["DeviceID"].ToString().Split('\\'); if (mobj["Manufacturer"] != null) { if ( mobj["Description"].ToString().Equals(Description) && mobj["Manufacturer"].ToString().Equals(Manufacturer) && arrDeviceID[1].Equals(VersionIDandVendorID) ) { rez = true; break; } } } } return rez; }Значения аргументов берутся из диспетчера оборудования. Метод универсальный, позволяет проверять большинство доступного оборудования в системе.
Перед описанием свойства KeyId, которое возвращает идентификатор ключа, необходимо описать ещё два приватных метода appl_stat() и appl_stop():
private static bool appl_stat() { if (hasp.IsLoggedIn()) return true;// если сессия уже открыта status = hasp.Login(ASCIIEncoding.ASCII.GetBytes(vendor_code)); hasp_file = hasp.GetFile(HaspFiles.Main); // проверка верности открытии сессии if (HaspStatus.StatusOk == status) return true; return false; } private static bool appl_stop() { if(!hasp.IsLoggedIn() return true;// если сессия уже закрыта status = hasp.Logout(); // проверка верности закрытия сессии if (HaspStatus.StatusOk == status) return true; return false; }Тут важным является вход на ключ, который осуществяется вызовом API ALADDIN'а, где vendor_code - мастер-код ключа разработчика. Логика такая, вызывается метод старта работы с ключем, выполняется операция, потом вызывается метод стоп работы с ключом, это описано в документации для разработчика, поставляемой вместе с ключами.
Вернемся к свойству KeyId:
public static Int32 KeyId { get { if (appl_stat()) { string s = null; hasp.GetSessionInfo(Hasp.KeyInfo, ref s); XmlDataDocument x = new XmlDataDocument(); x.LoadXml(s); XmlNode l = x.DocumentElement.GetElementsByTagName("haspid").Item(0).FirstChild; Int32 i Int32.TryParse(l.Value, out i); appl_stop(); return i; } else return 0; } }XML-документ всегда содержит информацию только об одном ключе,- таково ограничение HASP HL, поэтому Item(0).FirstChild
По аналогии можно определить свойство Size, отличие только в запросе элемента:
XmlNode l = x.DocumentElement.GetElementsByTagName("size").Item(0).FirstChild;Дополним класс методами, которые обрабатывают вставку/извлечение ключа:
private static void watcher_remove(object sender, EventArrivedEventArgs e) { if (lastoperation != last_operation.REMOVE && id != KeyId) {// вытаскивание 'нашего' ключа, т.к. KeyId вернет ноль если он был вытащен lastoperation = last_operation.REMOVE; id = 0; KeyRemoved(null, EventArgs.Empty);// генерация события в приложение } } private static void watcher_insert(object sender, EventArrivedEventArgs e) { if (lastoperation != last_operation.INSERT && id == 0) {// если идентификатор ноль - ключа не было, отсечка всех остальных ключей if ((id = KeyId) != 0) {// идентификатор получен lastoperation = last_operation.INSERT; KeyArrived(null, EventArgs.Empty); } } }Со вставкой/извлечением и инициализацией работы ключа всё, теперь необходимо описать метод Close, для корректного завершения работы с ключом:
public static void close() { if (hasp_file != null) hasp_file.Dispose();// уничтожение файловой системы ключа hasp.Dispose();// уничтожение самого себя // уничтожение слушателей вставки/извлечения ключа watcher1.Stop(); watcher2.Stop(); // сброс флагов и текущего идетификатора ключа id = 0; flg_isopen = false; }Методы записи/чтения/обнуления ключа зависят от конкретной информации, записываемой на ключ и политики защиты, структурно выглядят примерно так:
public static bool write() {// запись данных на ключ if (appl_stat() == true)// открытие сессии работы с ключем { hasp_file.FilePos = 0; // запись семерки status = hasp_file.Write(BitConverter.GetBytes(7), 0, sizeof(int)); if (HaspStatus.StatusOk != status) { return false;// запись не прошла } appl_stop();// закрытие сессии return true; } return false; } }Последний важный момент, события KeyArrived и KeyRemoved генерируются не в UI-потоке приложения, поэтому необходимо вызвать инвок обработчика этих событий, например так:
private void hasp_Key_KeyRemoved(object sender, EventArgs e) { if (this.InvokeRequired) BeginInvoke((EventHandler)hasp_Key_KeyRemoved, null); else { // чего-то сделать при извлечении ключа } }
Комментариев нет:
Отправить комментарий