Проверка серийника устройства Anviz через RSA в C#
Для своей компоненты взаимодействия с биометрическими устройствами Anviz я хочу, чтобы она работала только с определенными устройствами, на серийники которых я выдал лицензию.
Делюсь небольшим участком кода компоненты, который я написал на C# для этого и расскажу немного о том, как я отлаживал и разрабатывал этот код:
check_serial: //Check serial with cryptography Boolean serial_ok = false; AddLog("Get serial of device: " + serial_number); if (serial_number == "") AddLog("Serial is empty"); else { Int64 serial_number_long = Convert.ToInt64(serial_number); string serial_number_hex = Convert.ToString(serial_number_long, 16).ToUpper(); string serial_number_dec = Convert.ToString(serial_number_long, 10).ToUpper(); AddLog("Serial of device in 16 system: " + serial_number_hex); AddLog("Serial of device in 10 system: " + serial_number_dec); foreach (string currLic in SerialsLicens) { //Проверяем лицензию TwoWords twLic = SplitByColon(currLic); string currSerial = twLic.First.ToUpper().TrimStart('0'); //Remove leading zeroes AddLog("Curr serial from Licenses: " + currSerial); if ((currSerial != serial_number_hex) & (currSerial != serial_number_dec)) continue; //skip if not our serial TwoWords twRSA = SplitByColon(public_key); string DecodedSerial = myRSA.RSA_Decode(twLic.Second, twRSA.First, twRSA.Second); AddLog("Restored serial by RSA: " + DecodedSerial); if (DecodedSerial == currSerial) { //Here hex serial_ok = true; AddLog("Serial OK restored by RSA"); } else AddLog("Serial BAD restored by RSA"); break; }
Клиенту выдается публичный ключ в виде строки: 1001:ABDFCD10….
Где первая часть — экспонента, а вторая — модуль алгоритма RSA.
Клиенту также на каждое устройство выдается ключ в виде строки: 0123456789:BBEDF203….
Первая часть этой строки — серийный номер устройства. Вторая — закодированный через RSA серийный номер.
При подключении идет запрос к устройству, определяется его серийный номер и дальше, прежде чем начать полноценную работу с устройством, проверяется, есть ли лицензия на этот серийный номер.
Для ускорения в списке лицензий ищется серийный номер, соответствующий текущему и для него выполняется декодирование зашифрованного серийника. Если в результате расшифровки получаем нужный нам серийник, то все ок, лицензия имеется. Можно было бы перебирать все лицензии, но это долго, т.к. декодирование — не самый быстрый процесс. Поэтому используется небольшое ускорение в виде явного указания серийника в списке лицензий.
Как видите, везде в алгоритме я использую по максимуму логирование через AddLog. Если логирование в моей компоненте выключено, то этот метод ничего не делает, если включено, то пишет в лог, что полезно, если нужно понять причины ошибки.
И вот клиент прислал мне лог, что его серийный номер не проходит проверку RSA.
Я начал разбираться и понял, что у меня проверяется только шестнадцатеричный номер серийника, хотя лицензия выдается на десятичный номер. Видимо, когда я отлаживал, я получил с устройства номер в шестнадцатеричном формате и так и оставил, забыв, что лицензии выдаются на десятичные номера.
Я решил оставить оба формата — 16 и 10, объединил проверку через логическое И. И добавил преобразование к верхнему регистру на всякий случай (для букв шестнадцатиричной системы).
Однако и тут не проходила проверку. Анализ показал, что строка серийника в список лицензий выдается с лидирующими нулями, хотя расшифровывается он без лидирующих нулей (т.к. получаем просто число).
Поэтому в сравнение я еще добавил убирать лидирующие нули.
В итоге сравнение и декодирование заработало.
В коде вы можете увидеть метку check_serial, да, я использовал тестовый код, чтобы сразу переходить на эту метку с нужным мне серийником:
После завершения отладки я закомментировал этот код.
Весь код не очень профессиональный, написал с помощью Google, но работает. Дело в том, что я лишь начинающий программист в C#.
Писать основную логику в else-бранче — признак плохого стиля. Использовать метки когда не надо — так же.
Бессмысленные конверсии туда-сюда не нужны. Если подумаешь над сутью операций, сможешь сделать все проще и лаконичней.
метки я использовал для отладки.
насчет else не совсем понял.
но в целом, претензии возможны, т.к. C# — не мой основной язык. Использую по крайней необходимости.
Обычно пишут if (condition) { /* основная логика */ } else { /*альтернатива*/}. У тебя вынесена логика в else-ветку (branch)
да, обычно я в 1С тоже так пишу, но в незнакомой языковой среде пишешь так, как пишется