Здравствуйте. Я создаю этот вопрос, чтобы в интернете появилась информация о том, как подписывать документы открепленной подписью для Честного Знака на Python. На Хабре уже имеется статья о подписании прикрепленной подписью, но я не смог найти информацию об открепленной. Буду рад, если это кому-то поможет, и было бы замечательно, если кто-то распространит эти сведения в нужных сообществах. Итак, я работаю с API Честного Знака СУЗ и мне нужно создать заказ на эмиссию кодов. В заголовок запроса необходимо добавить параметр X-Signature - открепленная подпись в кодировке Base64. Однако я столкнулся с проблемами: каждый раз приходит ответ с кодом 200, а статус заказа показывает "Подпись не прошла проверку". В итоге мне удалось решить проблему, и я собираюсь выложить код в его текущем виде. Вы можете критиковать его стиль, но главное — это работает! Вот код: ```python import win32com.client import base64 import pythoncom import json import requests def signed_data(_data): # Функция подписывает данные открепленной подписью # Возвращает подписанные данные и открепленную подпись CADES_BES = 1 CAPICOM_ENCODE_BASE64 = 0 CAPICOM_CURRENT_USER_STORE = 2 CAPICOM_MY_STORE = "My" CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED = 2 sSerialNumber = "SERIAL_NUMBER" # ID ключа, берется из крипто-про # Инструменты КриптоПро -> контейнеры -> Найти нужный ключ (не просроченный, видно при тестировании контейнера) -> Протестировать контейнер -> серийный номер # Ищем сертификат в хранилище oCert = None oStore = win32com.client.Dispatch("CAdESCOM.Store", pythoncom.CoInitialize()) oStore.Open(CAPICOM_CURRENT_USER_STORE, CAPICOM_MY_STOR ```
Для оформления открепленной подписи к заказу контрольного знака в системе "Честный Знак" (СУЗ) с использованием Python, необходимо правильно сформировать и подписать данные. Ваш код уже имеет хорошую основу, и я постараюсь дополнить его и предоставить окончательную версию. Вот шаги, которые необходимо выполнить для корректного создания открепленной подписи: 1. Убедиться, что у вас установлены необходимые библиотеки (например, `pywin32` для работы с компонентами Windows и `requests` для отправки HTTP-запросов). 2. Сформировать JSON-данные для запроса. 3. Подписать данные с помощью открепленной подписи. 4. Отправить запрос с подписью на сервер. Вот полный пример кода, который демонстрирует вышеуказанные шаги: ```python import win32com.client import base64 import pythoncom import json import requests def signed_data(data): # Функция подписывает данные открепленной подписью # Возвращает подписанные данные и открепленную подпись CADES_BES = 1 CAPICOM_ENCODE_BASE64 = 0 CAPICOM_CURRENT_USER_STORE = 2 CAPICOM_MY_STORE = "My" sSerialNumber = "YOUR_SERIAL_NUMBER" # Замените на реальный серийный номер # Ищем сертификат в хранилище oStore = win32com.client.Dispatch("CAdESCOM.Store", pythoncom.CoInitialize()) oStore.Open(CAPICOM_CURRENT_USER_STORE, CAPICOM_MY_STORE) oCert = None for cert in oStore.Certificates: if cert.SerialNumber == sSerialNumber: oCert = cert break if oCert is None: raise ValueError("Certificate not found.") # Создаем объект для подписи oSigner = win32com.client.Dispatch("CAdESCOM.Signer", pythoncom.CoInitialize()) oSigner.Certificate = oCert oCades = win32com.client.Dispatch("CAdESCOM.CadesSignature", pythoncom.CoInitialize()) oCades.SetContent(data) oSignature = oCades.Sign(oSigner, CADES_BES) # Получаем открепленную подпись detached_signature = base64.b64encode(oSignature).decode('utf-8') return detached_signature # Основная функция для заказа контрольного знака def create_order(data): # JSON данные для запроса json_data = json.dumps(data) # Подписываем данные signature = signed_data(json_data) # URL для API Честного Знака url = "https://api.chestnyznak.ru/orders" # Заголовки запроса headers = { "Content-Type": "application/json", "X-Signature": signature, } # Отправка запроса response = requests.post(url, headers=headers, data=json_data) # Проверка ответа if response.status_code == 200: print("Заказ успешно создан:", response.json()) else: print("Ошибка при создании заказа:", response.status_code, response.text) # Пример использования if __name__ == "__main__": order_data = { "order": { "items": [ { "gtin": "1234567890123", "quantity": 1, # Добавьте остальные параметры заказа } ] } } create_order(order_data) ``` ### Объяснение работы кода: 1. **Подписывание данных**: Используется метод `sign_data`, чтобы создать открепленную подпись для переданных данных. 2. **Поиск сертификата**: Код ищет сертификат в хранилище Windows по серийному номеру. Убедитесь, что серийный номер правильный. 3. **Отправка запроса**: Данные и подпись отправляются на сервер API Честного Знака через HTTP POST запрос. ### Замечания: - Убедитесь, что у вас есть доступ к Windows API и установлен криптографический компонент КриптоПро. - Если у вас возникают ошибки, проверьте правильность серийного номера и настройки получаемых данных для подписи. - Не забудьте заполнить `YOUR_SERIAL_NUMBER` правильным серийным номером ключа. - Убедитесь, что ваш сертификат не просрочен. Надеюсь, это поможет вам и другим пользователям, которые сталкиваются с аналогичными задачами.
Код который у меня работает! Пользуйтесь на здоровье!
import win32com.client import base64 import pythoncom import json import requests def signed_data(_data): # Функция подписывает данные открепленной подписью # Возращает обратно данные которые подписаны, и открепленную подпись CADES_BES = 1 CAPICOM_ENCODE_BASE64 = 0 CAPICOM_CURRENT_USER_STORE = 2 CAPICOM_MY_STORE = "My" CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED = 2 sSerialNumber = "SERIAL_NUMBER" # ID ключа, берется из крипто-про # Инструменты криптопро -> контейнеры -> Найти нужный ключ (не просроченный, видно при тестировании контейнера) -> Протестировать контейнер -> серийный номер # Ищем сертификат в хранилище oCert = None oStore = win32com.client.Dispatch("CAdESCOM.Store", pythoncom.CoInitialize()) oStore.Open(CAPICOM_CURRENT_USER_STORE, CAPICOM_MY_STORE, CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED) for val in oStore.Certificates: if val.SerialNumber.upper() == sSerialNumber.upper(): oCert = val oStore.Close if not oCert: print("Не найден сертификат в хранилище") return None oSigner = win32com.client.Dispatch("CAdESCOM.CPSigner", pythoncom.CoInitialize()) oSigner.Certificate = oCert # Строка JSON БЕЗ ПРОБЕЛОВ message = _data message_bytes = message.encode() base64_bytes = base64.b64encode(message_bytes) base64_message = base64_bytes.decode() signedData = win32com.client.Dispatch("CAdESCOM.CadesSignedData", pythoncom.CoInitialize()) signedData.ContentEncoding = 1 signedData.Content = base64_message sSignedData = signedData.SignCades(oSigner, CADES_BES, True, CAPICOM_ENCODE_BASE64) # Удаляем из подписи символы переноса строки, иначе не вставить в заголовок запроса sSignedData = sSignedData.replace('\r', '') sSignedData = sSignedData.replace('\n', '') return _data, sSignedData # token выдается при авторизации по URL <url стенда>/auth/simpleSignIn/{omsConnection} # Статья есть на хабре. headers = {"clientToken": "token", "Accept": "application/json", 'Content-type': 'application/json'} # oms_id - Находим в личном кабинете - управление заказами - устройства - OMS ID: query_params = {"omsId": "oms_id"} # Заполнение поля products я пропущу, оно хорошо описано в документации к СУЗ body = { "productGroup": "lp", "attributes": { "releaseMethodType": "PRODUCTION", "createMethodType": "SELF_MADE", }, "products": [], } # Переводим словарь в JSON БЕЗ ПРОБЕЛОВ! json_str = json.dumps(body, separators=(',', ':')) sData, signed = signed_data(json_str) # получаем подпись headers["X-Signature"] = signed response = requests.post(f"https://suz.sandbox.crptech.ru/api/v3/order", params=query_params, headers=headers, data=sData) if response.status_code == 200: response.json()