Электронные ключи 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
{
// чего-то сделать при извлечении ключа
}
}
Комментариев нет:
Отправить комментарий