Самостоятельный учебный опыт в новом собственном Android-приложении: реальный ключевой контейнер, полная сверка байтов, явный постоянный импорт и математическая проверка своих данных.
Где и как запускать
Полный модуль своей общей управляемой формы в новом Android ARM64 developer-приложении; только МобильноеПриложениеКлиент. МобильноеПриложениеСервер используется лишь для собственного скалярного маркера.
Подготовка
Используйте только отдельные собственные Windows VM и пустой Android ARM64-стенд. Создайте новую пустую конфигурацию УчебныйPKCS1285; версия конфигурации 16d8ca1f-94a0-47cd-966f-cef9eb1c8056, русский встроенный язык, совместимость Не использовать. Эта учебная метка не доказывает владельца устройства. Не подключайте рабочую ИБ, пользователей, сертификаты, расширения и фоновые задания.
В конфигураторе: Общие → Общие формы → создайте управляемую форму ПультКлюча85. Реквизиты: UUIDКомплекта85 — Строка длиной 36; ПарольКомплекта85 — Строка длиной 64, у поля РежимПароля, сохранение настроек выключено; КаталогКомплектов85 и Состояние85 — Строка длиной 0, поля ТолькоПросмотр; СогласенЧитатьКомплект85 и СогласенИмпортироватьКлюч85 — Булево, начально Ложь. Добавьте команду ПроверитьКонтейнер85, клиентское действие с тем же именем и кнопку. Назначьте ПриОткрытии. Вставьте весь основной блок выбранного варианта в модуль этой формы. Три варианта в один модуль не вставляются.
В свойствах корня Конфигурации: Назначения использования → Мобильное устройство; ПК не выбран. Затем ПКМ по корню Конфигурации → Открыть рабочую область начальной страницы; выберите шаблон с одной колонкой и добавьте только свою общую форму ПультКлюча85. Нажмите F7 для своей конфигурации; выполните проверку для мобильного приложения — клиента и сервера, включая неподдерживаемые функции. При ошибке остановитесь. Только затем Конфигурация → Мобильное приложение → Публиковать… в новый собственный виртуальный каталог своей лабораторной Windows VM/IIS. Публикация мобильной конфигурации не собирает APK и не должна заменять существующую публикацию.
Будущий ручной запуск требует официальной developer-платформы 1cem-arm64.apk своей фактически полученной поставки. Не 1cem-client-arm64.apk и не 1cem-standalone-arm64.apk. Windows 8.5.1.1343 не доказывает наличие Android APK этой версии. Здесь ничего не скачано и не установлено. Если фактическая мобильная версия не ровно 8.5.1.1343, форма отказывает; доступность подходящей поставки не обещана.
После своего ручного развёртывания developer-платформы: + → Address = настоящий адрес собственной новой публикации → Load → имя УчебныйPKCS1285 → OK/Done → открыть своё приложение. Не localhost телефона и не выдуманный URL. Это отдельная будущая подготовка в своей лабораторной сети, не выполненная нами. Официальные маршруты: https://1c-dn.com/library/publishing_of_1c_mobile_application/ ; https://1c-dn.com/library/tutorials/1c_enterprise_development_tools_user_guide/ ; https://1c-dn.com/library/tutorials/first_application_mobile_platform/ .
Только на своей новой Windows VM подготовьте Python 3 и новую собственную папку опыта. В её терминале вручную: python -m venv .venv, затем .venv\Scripts\python.exe -m pip install cryptography==48.0.0. Сохраните полный нижний Python-блок как own_pkcs1285_fixture.py в этой папке. Вручную выполните .venv\Scripts\python.exe own_pkcs1285_fixture.py --base "точная собственная существующая папка подготовки на Windows VM". Android-путь нельзя подставлять как Windows-путь. Скрипт спросит новый только учебный пароль из 16–64 печатных ASCII-символов, создаст свежий UUID-каталог и два новых файла, затем выведет настоящий UUID. Эти команды здесь не запускались: скрипт только статически разобран, библиотека не импортировалась и ключи не создавались.
own.p12 содержит собственный закрытый ключ и зашифрован паролем. own-public.cer содержит только публичный сертификат этого ключа. Генератор независимо перечитывает PKCS12 и сравнивает настоящий ключ, сертификат и CA до записи. Сертификаты учебные, на семь суток; доверие ОС и реальные ключи пользователя не используются. mode 0700 не обещает Windows ACL, а пароль PKCS12 не заменяет защиту всей VM. Официальная документация PKCS12 и ограничений защиты: https://cryptography.io/en/48.0.0/hazmat/primitives/asymmetric/serialization/ .
В новой Android-форме посмотрите фактический КаталогКомплектов85. До нажатия команды вручную перенесите только свой свежий каталог OwnPKCS1285_<напечатанный UUID> с двумя файлами непосредственно в этот каталог своего нового приложения штатным способом ОС своего стенда. Не угадывайте /storage/...; не переименовывайте чужой PFX. Если штатный доступ не дает обычного локального пути, остановитесь: нет ADB, root, SDK, URI-конвертации, расширенных разрешений и сетевой отправки ключа.
Введите именно свой UUID и пароль, затем отдельно подтвердите чтение секретного комплекта и постоянный импорт своего ключа в Личное приложения. Импорт реально меняет хранилище при будущем запуске. Нужен один спокойный собственный сеанс без иных писателей файлов/хранилища. Исходно Личное должно быть пустым; иначе опыт прекращается, существующие записи не показываются и не удаляются.
Для каждого варианта используйте отдельное новое приложение/стенд с пустым Личным, а не повторную попытку в уже изменённом приложении. После опыта завершите процессы и вручную утилизируйте полностью собственный учебный стенд. Урок не удаляет ключ/сертификат, не откатывает ОС и не обещает очистку после сбоя. Не отправляйте P12, пароль, Base64 секрета или память отладчика в чат и репозиторий.
&НаКлиенте
Перем КонтекстКлюча85, ЗапускНачат85;
&НаСервереБезКонтекста
Функция ПолучитьМаркерПакета85()
#Если МобильноеПриложениеСервер Тогда
Возврат Метаданные.Имя + "|" + Метаданные.Версия;
#Иначе
ВызватьИсключение "Нужен локальный сервер собственного мобильного приложения";
#КонецЕсли
КонецФункции
&НаКлиенте
Процедура ПроверитьСреду85()
#Если МобильноеПриложениеКлиент Тогда
Среда85 = Новый СистемнаяИнформация;
Если Среда85.ТипПлатформы <> ТипПлатформы.Android_ARM_64 Или ПолучитьМаркерПакета85() <> "УчебныйPKCS1285|16d8ca1f-94a0-47cd-966f-cef9eb1c8056" Тогда
ВызватьИсключение "Не совпали собственный пакет, Android ARM64 или фактическая версия";
КонецЕсли;
#Иначе
ВызватьИсключение "Урок предназначен только для своего мобильного приложения";
#КонецЕсли
КонецПроцедуры
&НаКлиенте
Процедура ПриОткрытии(Отказ)
Если ЗапускНачат85 <> Неопределено Тогда
Отказ = Истина;
Возврат;
КонецЕсли;
ЗапускНачат85 = Ложь;
КонтекстКлюча85 = Неопределено;
СогласенЧитатьКомплект85 = Ложь;
СогласенИмпортироватьКлюч85 = Ложь;
ПарольКомплекта85 = "";
UUIDКомплекта85 = "";
Состояние85 = "Не проверено";
Попытка
ПроверитьСреду85();
КаталогКомплектов85 = КаталогДокументов();
Если ТипЗнч(КаталогКомплектов85) <> Тип("Строка") Или Лев(КаталогКомплектов85, 1) <> "/" Тогда
ВызватьИсключение "Не получен обычный абсолютный каталог документов своего приложения";
КонецЕсли;
Исключение
ЗапускНачат85 = Истина;
Состояние85 = "СТОП: подготовка среды не подтверждена";
Отказ = Истина;
КонецПопытки;
КонецПроцедуры
&НаКлиенте
Функция ПрочитатьСвойФайл85(Имя85, Предел85)
Файл85 = Новый Файл(Имя85);
Если Не Файл85.Существует() Тогда
ВызватьИсключение "Нет собственного подготовленного файла";
КонецЕсли;
Если Не Файл85.ЭтоФайл() Тогда
ВызватьИсключение "Ожидался собственный обычный файл";
КонецЕсли;
Размер85 = Файл85.Размер();
Если ТипЗнч(Размер85) <> Тип("Число") Или Размер85 <= 0 Или Размер85 > Предел85 Тогда
ВызватьИсключение "Размер собственного файла не подходит";
КонецЕсли;
Байты85 = Новый ДвоичныеДанные(Имя85);
Если Байты85.Размер() <> Размер85 Тогда
ВызватьИсключение "Файл изменился во время чтения";
КонецЕсли;
Возврат Байты85;
КонецФункции
&НаКлиенте
Функция НачатьСвойОпыт85(Команда)
Если Команда = Неопределено Или ЗапускНачат85 <> Ложь Тогда
ВызватьИсключение "Этот экземпляр формы уже использован или не подготовлен";
КонецЕсли;
ЗапускНачат85 = Истина;
Состояние85 = "Не проверено";
Если ТипЗнч(СогласенЧитатьКомплект85) <> Тип("Булево") Или Не СогласенЧитатьКомплект85
Или ТипЗнч(СогласенИмпортироватьКлюч85) <> Тип("Булево") Или Не СогласенИмпортироватьКлюч85 Тогда
ВызватьИсключение "Нужны два отдельных ручных согласия";
КонецЕсли;
СогласенЧитатьКомплект85 = Ложь;
СогласенИмпортироватьКлюч85 = Ложь;
Пароль85 = ПарольКомплекта85;
ПарольКомплекта85 = "";
ПроверитьСреду85();
Если ТипЗнч(Пароль85) <> Тип("Строка") Или СтрДлина(Пароль85) < 16 Или СтрДлина(Пароль85) > 64 Тогда
ВызватьИсключение "Введите только пароль собственного учебного комплекта";
КонецЕсли;
UUID85 = НРег(Строка(Новый УникальныйИдентификатор(UUIDКомплекта85)));
Если UUID85 <> UUIDКомплекта85 Или UUID85 = "00000000-0000-0000-0000-000000000000" Тогда
ВызватьИсключение "Введите точный UUID из своей ручной подготовки";
КонецЕсли;
Основа85 = КаталогДокументов();
Если ТипЗнч(Основа85) <> Тип("Строка") Или Основа85 <> КаталогКомплектов85 Или Лев(Основа85, 1) <> "/" Тогда
ВызватьИсключение "Изменился обычный каталог документов собственного приложения";
КонецЕсли;
Если Прав(Основа85, 1) <> "/" Тогда
Основа85 = Основа85 + "/";
КонецЕсли;
Папка85 = Основа85 + "OwnPKCS1285_" + UUID85 + "/";
К = Новый Структура("UUID,Запуск,Версия,Фаза,СогласияПотреблены,Завершен,Пароль,ИмяP12,ИмяDER",
UUID85, Строка(Новый УникальныйИдентификатор), "8.5.1.1343", "Подготовка", Истина, Ложь,
Пароль85, Папка85 + "own.p12", Папка85 + "own-public.cer");
К.Вставить("ИсходныйP12", ПрочитатьСвойФайл85(К.ИмяP12, 262144));
К.Вставить("ИсходныйDER", ПрочитатьСвойФайл85(К.ИмяDER, 65536));
К.Вставить("Образец", Новый СертификатКриптографии(К.ИсходныйDER));
К.Вставить("Контейнер", Неопределено);
К.Вставить("ИмпортРазрешен", Истина);
К.Вставить("Данные", ПолучитьДвоичныеДанныеИзСтроки("OwnPKCS1285|" + UUID85 + "|Тетрадь|5*25=" + Строка(5 * 25), "UTF-8", Ложь));
ПроверитьСвойСертификат85(К.Образец, К);
КонтекстКлюча85 = К;
Возврат К;
КонецФункции
&НаКлиенте
Процедура ПроверитьСвойСертификат85(Сертификат85, К)
Если ТипЗнч(Сертификат85) <> Тип("СертификатКриптографии") Тогда
ВызватьИсключение "Ожидался фактический собственный сертификат";
КонецЕсли;
Если ТипЗнч(Сертификат85.Субъект) <> Тип("ФиксированнаяСтруктура")
Или Не Сертификат85.Субъект.Свойство("CN", CN85) Или ТипЗнч(CN85) <> Тип("Строка")
Или CN85 <> "OwnPKCS1285 Client " + К.UUID Тогда
ВызватьИсключение "Субъект не соответствует своему свежему комплекту";
КонецЕсли;
ПубличныйDER85 = Сертификат85.Выгрузить();
Если ТипЗнч(ПубличныйDER85) <> Тип("ДвоичныеДанные") Или Base64Строка(ПубличныйDER85) <> Base64Строка(К.ИсходныйDER) Тогда
ВызватьИсключение "Не совпали полные публичные DER-байты";
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПроверитьПродолжение85(К, Фаза85)
ПроверитьСреду85();
Если ТипЗнч(К) <> Тип("Структура") Или ТипЗнч(КонтекстКлюча85) <> Тип("Структура")
Или К.UUID <> КонтекстКлюча85.UUID Или К.Запуск <> КонтекстКлюча85.Запуск
Или К.Версия <> "8.5.1.1343" Или ТипЗнч(К.СогласияПотреблены) <> Тип("Булево")
Или Не К.СогласияПотреблены Или ТипЗнч(К.Завершен) <> Тип("Булево") Или К.Завершен
Или К.Фаза <> Фаза85 Или КонтекстКлюча85.Фаза <> Фаза85 Тогда
ВызватьИсключение "Нарушен порядок собственного опыта";
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПроверитьВыгрузку85(Байты85, К)
Если ТипЗнч(К.Контейнер) <> Тип("КонтейнерКлючейКриптографии")
Или ТипЗнч(Байты85) <> Тип("ДвоичныеДанные")
Или Base64Строка(Байты85) <> Base64Строка(К.ИсходныйP12) Тогда
ВызватьИсключение "Контейнер не вернул полные исходные PKCS12-байты";
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ИмпортироватьИПроверить85(К)
ПроверитьПродолжение85(К, "Выгрузка");
Менеджер85 = Новый МенеджерКриптографии("", "", 0);
Сведения85 = Менеджер85.ПолучитьИнформациюМодуляКриптографии();
Если ТипЗнч(Сведения85) <> Тип("ИнформацияМодуляКриптографии")
Или ТипЗнч(Сведения85.АлгоритмыХеширования) <> Тип("ФиксированныйМассив")
Или ТипЗнч(Сведения85.АлгоритмыПодписи) <> Тип("ФиксированныйМассив")
Или Сведения85.АлгоритмыХеширования.Найти("SHA256") = Неопределено
Или Сведения85.АлгоритмыПодписи.Найти("RSA") = Неопределено Тогда
ВызватьИсключение "Фактический модуль не подтвердил SHA256 и RSA";
КонецЕсли;
Менеджер85.АлгоритмХеширования = "SHA256";
Менеджер85.АлгоритмПодписи = "RSA";
Менеджер85.ВключениеДанныхВПодпись = Ложь;
Менеджер85.ВключениеСертификатовВПодпись = РежимВключенияСертификатовКриптографии.ВключатьСертификатСубъекта;
Если Менеджер85.АлгоритмХеширования <> "SHA256" Или Менеджер85.АлгоритмПодписи <> "RSA"
Или Менеджер85.ВключениеДанныхВПодпись <> Ложь
Или Менеджер85.ВключениеСертификатовВПодпись <> РежимВключенияСертификатовКриптографии.ВключатьСертификатСубъекта Тогда
ВызватьИсключение "Фактические параметры менеджера не совпали";
КонецЕсли;
Личное85 = Менеджер85.ПолучитьХранилищеСертификатов(ТипХранилищаСертификатовКриптографии.ПерсональныеСертификаты,
РасположениеХранилищаСертификатовКриптографии.ДанныеПриложения);
Если ТипЗнч(Личное85) <> Тип("ХранилищеСертификатовКриптографии") Тогда
ВызватьИсключение "Не получено хранилище своего приложения";
КонецЕсли;
До85 = Личное85.ПолучитьВсе();
Если ТипЗнч(До85) <> Тип("Массив") Или До85.Количество() <> 0
Или ТипЗнч(К.ИмпортРазрешен) <> Тип("Булево") Или Не К.ИмпортРазрешен Тогда
ВызватьИсключение "Для этого нового опыта нужно пустое Личное и отдельное согласие";
КонецЕсли;
К.ИмпортРазрешен = Ложь;
К.Фаза = "Неизвестно после попытки импорта";
Состояние85 = "Импорт может изменить хранилище: результат пока неизвестен";
Личное85.Добавить(К.Контейнер);
Найденный85 = Личное85.НайтиПоОтпечатку(К.Образец.Отпечаток);
ПроверитьСвойСертификат85(Найденный85, К);
После85 = Личное85.ПолучитьВсе();
Если ТипЗнч(После85) <> Тип("Массив") Или После85.Количество() <> 1 Тогда
ВызватьИсключение "Свежий состав Личного не соответствует одному собственному сертификату";
КонецЕсли;
ПроверитьСвойСертификат85(После85[0], К);
Подпись85 = Менеджер85.Подписать(К.Данные, Найденный85);
Если ТипЗнч(Подпись85) <> Тип("ДвоичныеДанные") Или Подпись85.Размер() <= 0 Тогда
ВызватьИсключение "Подпись не вернула фактические байты";
КонецЕсли;
СертификатПроверки85 = Неопределено;
Менеджер85.ПроверитьПодпись(К.Данные, Подпись85, СертификатПроверки85, Ложь);
ПроверитьСвойСертификат85(СертификатПроверки85, К);
Измененные85 = ПолучитьДвоичныеДанныеИзСтроки("OwnPKCS1285|" + К.UUID + "|Тетрадь|6*25=" + Строка(6 * 25), "UTF-8", Ложь);
ИзменениеОтказано85 = Ложь;
Попытка
Менеджер85.ПроверитьПодпись(Измененные85, Подпись85, , Ложь);
Исключение
ИзменениеОтказано85 = Истина;
КонецПопытки;
Если Не ИзменениеОтказано85 Тогда
ВызватьИсключение "Измененные байты ошибочно приняты";
КонецЕсли;
Если Base64Строка(ПрочитатьСвойФайл85(К.ИмяP12, 262144)) <> Base64Строка(К.ИсходныйP12)
Или Base64Строка(ПрочитатьСвойФайл85(К.ИмяDER, 65536)) <> Base64Строка(К.ИсходныйDER) Тогда
ВызватьИсключение "Собственный исходный комплект изменился";
КонецЕсли;
К.Пароль = "";
К.Завершен = Истина;
К.Фаза = "Завершено";
Состояние85 = "Проверено: исходные PKCS12-байты совпали; свой ключ импортирован; подпись суммы 125 проверена своим DER; измененные байты отвергнуты. Доверие, отзыв и юридическая сила не проверены";
КонецПроцедуры
&НаКлиенте
Процедура ОстановитьСвойОпыт85()
Если ТипЗнч(КонтекстКлюча85) = Тип("Структура") Тогда
КонтекстКлюча85.Пароль = "";
КонтекстКлюча85.Завершен = Истина;
КонтекстКлюча85.Фаза = "СТОП";
КонецЕсли;
ПарольКомплекта85 = "";
ЗапускНачат85 = Истина;
Состояние85 = "СТОП: опыт не подтвержден; импорт мог произойти. Не повторяйте и не удаляйте записи автоматически";
КонецПроцедуры
&НаКлиенте
Процедура ПроверитьКонтейнер85(Команда)
Попытка
К = НачатьСвойОпыт85(Команда);
К.Фаза = "Выгрузка";
К.Контейнер = Новый КонтейнерКлючейКриптографии(К.ИмяP12, К.Пароль);
Выгруженные85 = К.Контейнер.Выгрузить();
ПроверитьВыгрузку85(Выгруженные85, К);
К.Контейнер = Новый КонтейнерКлючейКриптографии(Выгруженные85, К.Пароль);
Если ТипЗнч(К.Контейнер) <> Тип("КонтейнерКлючейКриптографии") Тогда
ВызватьИсключение "Восстановленный объект не является ключевым контейнером";
КонецЕсли;
ИмпортироватьИПроверить85(К);
Исключение
ОстановитьСвойОпыт85();
КонецПопытки;
КонецПроцедуры
Python: own_pkcs1285_fixture.py
Отдельный файл в своей новой Windows VM; не модуль 1С.
Только будущий ручной запуск в собственном изолированном окружении. Сейчас выполнен только статический AST-разбор текста; генератор не запускался.
import argparse
import getpass
import importlib.metadata
import os
import sys
import uuid
from datetime import datetime, timedelta, timezone
from pathlib import Path
from cryptography import x509
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.serialization import pkcs12
from cryptography.x509.oid import ExtendedKeyUsageOID, NameOID
def build_own_fixture(base, password):
if os.name != "nt" or importlib.metadata.version("cryptography") != "48.0.0":
raise RuntimeError("Use your own isolated Windows VM and cryptography48.0.0")
base = Path(base).resolve(strict=True)
if not base.is_dir() or not 16 <= len(password) <= 64 or not password.isascii() or not password.isprintable():
raise ValueError("Own existing folder and new training-only printable ASCII password16-64 required")
identity = str(uuid.uuid4())
now = datetime.now(timezone.utc)
expiry = now + timedelta(days=7)
ca_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
client_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
ca_name = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "OwnPKCS1285 CA " + identity)])
client_name = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "OwnPKCS1285 Client " + identity)])
ca = (x509.CertificateBuilder().subject_name(ca_name).issuer_name(ca_name)
.public_key(ca_key.public_key()).serial_number(x509.random_serial_number())
.not_valid_before(now - timedelta(minutes=5)).not_valid_after(expiry)
.add_extension(x509.BasicConstraints(ca=True, path_length=0), critical=True)
.add_extension(x509.KeyUsage(digital_signature=True, content_commitment=False, key_encipherment=False,
data_encipherment=False, key_agreement=False, key_cert_sign=True,
crl_sign=True, encipher_only=None, decipher_only=None), critical=True)
.sign(ca_key, hashes.SHA256()))
client = (x509.CertificateBuilder().subject_name(client_name).issuer_name(ca_name)
.public_key(client_key.public_key()).serial_number(x509.random_serial_number())
.not_valid_before(now - timedelta(minutes=5)).not_valid_after(expiry)
.add_extension(x509.BasicConstraints(ca=False, path_length=None), critical=True)
.add_extension(x509.KeyUsage(digital_signature=True, content_commitment=False, key_encipherment=True,
data_encipherment=False, key_agreement=False, key_cert_sign=False,
crl_sign=False, encipher_only=None, decipher_only=None), critical=True)
.add_extension(x509.ExtendedKeyUsage([ExtendedKeyUsageOID.CLIENT_AUTH]), critical=False)
.sign(ca_key, hashes.SHA256()))
client.verify_directly_issued_by(ca)
p12_bytes = pkcs12.serialize_key_and_certificates(identity.encode("ascii"), client_key, client, [ca],
serialization.BestAvailableEncryption(password.encode("ascii")))
loaded_key, loaded_client, loaded_ca = pkcs12.load_key_and_certificates(p12_bytes, password.encode("ascii"))
if loaded_key is None or loaded_client is None or len(loaded_ca) != 1:
raise RuntimeError("OwnPKCS12 must contain own private key, client certificate and one CA")
if loaded_key.public_key().public_numbers() != client_key.public_key().public_numbers():
raise RuntimeError("Own private key mismatch")
if loaded_client.fingerprint(hashes.SHA256()) != client.fingerprint(hashes.SHA256()):
raise RuntimeError("Own client certificate mismatch")
if loaded_ca[0].fingerprint(hashes.SHA256()) != ca.fingerprint(hashes.SHA256()):
raise RuntimeError("Own CA mismatch")
public_der = loaded_client.public_bytes(serialization.Encoding.DER)
folder = base / ("OwnPKCS1285_" + identity)
folder.mkdir(mode=0o700, exist_ok=False)
for filename, payload in (("own.p12", p12_bytes), ("own-public.cer", public_der)):
with (folder / filename).open("xb") as stream:
stream.write(payload)
return identity, folder, expiry
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--base", required=True)
arguments = parser.parse_args()
if not sys.stdin.isatty():
raise RuntimeError("Run interactively only in your own VM; do not pipe/log the training password")
password = getpass.getpass("New training-only PKCS12 password: ")
confirmation = getpass.getpass("Repeat password: ")
if password != confirmation:
raise ValueError("Password confirmation differs")
identity, folder, expiry = build_own_fixture(arguments.base, password)
password = confirmation = ""
print("Own UUID:", identity)
print("Own folder:", folder)
print("Expires UTC:", expiry.isoformat())
print("Only own.p12 and public own-public.cer. No OS import/trust or network was requested.")
if __name__ == "__main__":
main()
Разбор по шагам
ПриОткрытии подтверждает настоящий Android ARM64 и скалярную метку своего приложения; показывает настоящий каталог документов. На сервер не передаются ключ, пароль, пути и контейнеры.
Команда использует два отдельных согласия один раз. После любого запуска данный экземпляр формы нельзя повторно использовать. UUID разбирается настоящим конструктором; имена двух файлов строятся только из действительного каталога, свежего UUID и фиксированных имён.
Сначала проверяются существование и размер файла, затем читаются фактические двоичные данные. Нет записи/выгрузки секрета в файл из 1С. В публичном сертификате проверяются CN собственного UUID и все DER-байты, не одно похожее название.
Выбранный способ реально создаёт и инициализирует ключевой контейнер. Выгрузка без имени файла возвращает данные, использованные при инициализации. Полные фактические двоичные данные сравниваются через одинаковое native Base64-кодирование, включая MIME-переносы строк; они никогда не печатаются.
Фактический мобильный менеджер создаётся своим конструктором. Первые три параметра на мобильной платформе игнорируются; это не подбор Windows-провайдера. Из настоящей информации модуля читаются фиксированные массивы названий алгоритмов; SHA256/RSA должны действительно присутствовать. Нет подмены названием или устаревшим алгоритмом.
До импорта читается настоящее Личное в расположении ДанныеПриложения. Оно должно оказаться пустым. Согласие на импорт потребляется, а статус Неизвестно устанавливается ДО Добавить: ошибка может оставить изменение. Добавить — процедура, не результат Булево.
После импорта выполняются новые native поиск по двоичному отпечатку и чтение списка. Полученный собственный сертификат сравнивается полностью с публичным DER. Контейнер без реальной связи ключа и сертификата не объявляется пригодным по одному факту создания.
Из собственного UUID и вычисления 5*25=125 строятся UTF-8-байты. Подписать получает именно ДвоичныеДанные и фактически найденный сертификат, ровно два аргумента. Документированный тип по умолчанию — CAdESBES. Явное перечисление CMS в мобильном коде не используется, поскольку его native доступность не включает этот контекст.
ПроверитьПодпись — процедура с четырьмя позициями: исходные байты, настоящая подпись, выходной сертификат и Ложь. Успех проверяется отсутствием исключения и полным DER выходного сертификата. Ложь ограничивает опыт математической подписью; доверие, отзыв, внешние атрибуты и юридическая сила не проверяются.
Только после проверки оригинала создаются другие собственные байты 6*25=150. Прежняя подпись не должна принять их. Исключение означает отказ этого опыта, но может иметь и другую причину провайдера; это не юридическое доказательство подделки. Затем оба исходных файла перечитываются и сравниваются полностью до итогового статуса.
В этом варианте контейнер из файла реально выгружается, полные байты сравниваются и вторым конструктором создаётся фактический контейнер из двоичных данных. Именно второй объект передаётся в импорт.
Как проверить результат
Нормальный будущий ручной запуск в новой лаборатории: исходные PKCS12-байты совпадают целиком; Личное меняется из пустого в один собственный сертификат; найденный и выходной сертификаты совпадают со своим публичным DER; подпись суммы 125 принимается, другие байты суммы 150 не принимаются. Это ожидаемый протокол, не уже выполненный запуск.
Без любого из двух флажков команда останавливается до чтения файлов/хранилища и импорта. С неправильным UUID, чужим CN, отсутствующим собственным файлом или неверным паролем итог Проверено не появляется. Эти пробы выполняйте только на своём новом стенде.
При отсутствующих SHA256/RSA, другом фактическом типе результата и непустом начальном Личном будет СТОП. Нельзя снимать проверки ради существующего устройства, выбирать первый неизвестный сертификат или вручную подделывать обработчики.
Повторное нажатие в том же экземпляре запрещено и после успеха, и после ошибки. Если импорт завершился частично или обработчик не пришёл, успех не подтверждён, ключ может остаться в приложении. Нет автоматического повтора или очистки.
Проверки файлов и списка не являются атомарной блокировкой и не доказывают durable persistence. Очищение поля/ссылки на пароль не гарантирует стирания всех копий секрета из памяти. Синхронный клиентский ХешированиеДанных сюда не перенесён: у него другой native контекст.
Ожидаемый результат
При будущём успешном ручном запуске: исходные PKCS12-байты совпали целиком, свой ключ импортирован, подпись вычисленной суммы 125 проверена фактическим своим DER, изменённые байты отвергнуты. Доверие, отзыв, юридическая сила и runtime здесь не проверены.
На что обратить внимание
КонтейнерКлючейКриптографии доступен только в документированных мобильных контекстах. Это не КонтейнерПодписейКриптографии и не строковое имя Windows CSP-контейнера. Старый Windows NonExportable-ключ не заменяет собственный PKCS12.
Выгрузить без аргументов возвращает секретные PKCS12-данные в память. Для памяти native у async/callback результаты тоже ДвоичныеДанные; не переносите имя файла из другой перегрузки. Здесь файл вывода и поток не используются.
СертификатКриптографии.Выгрузить возвращает только публичный сертификат, даже если он связан с ключом. Это не экспорт закрытого ключа. SHA-1-отпечаток здесь идентификатор поиска, не выбранный алгоритм подписи.
Начальный пустой список и метка собственной CF не доказывают собственность, приватность пути, отсутствие гонки или всех внутренних вызовов платформы. Не заявляем доверие CA, CRL/OCSP/TSA, прочность PKCS12-пароля и юридическую пригодность подписи. Команды HTTP/SMTP/внешних серверов в примере нет.
Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.