Python RSA подписывает строку с помощью NONEWithRSA

Я работаю над проектом, в котором код на стороне клиента находится на Java, и они используют алгоритм NONEWithRSA для подписи и проверки строки.

Я использую PyCryptodome для создания знака для данной строки. https://pycryptodome.readthedocs.io/en/latest/src/signature/pkcs1_v1_5.html

Но я не обнаружил, что он использует алгоритм NONEWithRSA, поэтому он дает другую подпись, чем то, что мои клиенты ожидают на сервере.

Поддерживает ли какая-либо библиотека на питоне этот алгоритм?


person varad    schedule 03.01.2020    source источник
comment
Соответствующая проблема: github.com/Legrandin/pycryptodome/issues/274   -  person sshashank124    schedule 03.01.2020
comment
@ sshashank124 Означает ли это, что он не поддерживается в python ??   -  person varad    schedule 03.01.2020
comment
Я не могу знать наверняка. Вам останется только поискать в Интернете.   -  person sshashank124    schedule 03.01.2020
comment
Хеширование перед подписью является необходимой частью безопасности подписи. Java по какой-то непонятной для меня причине предоставляет подписи RSA и другие подписи без хеширования. Это не означает, что это безопасно. Прекратите использовать NONEWithRSA и отдайте предпочтение серии SHA. PyCryptodome хорош в этом вопросе, так как не пускает людей на ложный путь.   -  person kelalaka    schedule 03.01.2020
comment
Есть ли шанс, что вы можете просто получить серверный код вместо того, чтобы пытаться выяснить, что нужно, шаг за шагом?   -  person President James K. Polk    schedule 03.01.2020
comment
@kelalaka В основном это позволяет людям реализовывать свой собственный способ хеширования, например. был хеш OpenSSL, который сочетал SHA-1 и MD-5 в дополнении PKCS#1 v1.5, но без указания хэша в кодировке DER, если я правильно помню.   -  person Maarten Bodewes    schedule 03.01.2020
comment
@Maarten-reinstateMonica Я подозревал об этом, но ждал, пока кто-нибудь что-нибудь об этом знает. Спасибо.   -  person kelalaka    schedule 03.01.2020
comment
В конце концов, это просто текст RSA. Из стандартных имен документ: алгоритм подписи RSA, который не использует алгоритм переваривания и использует только примитивы RSASP1/RSAVP1, как определено в PKCS #1 v2.2. Между прочим, это не полностью правильно, очевидно, он также выполняет функции I2OSP и OS2IP для кодирования больших целых чисел из байтов и в байты.   -  person Maarten Bodewes    schedule 03.01.2020
comment
Похоже, что все наборы инструментов придерживаются PKCS#1 v1.5 подписей PSS. Я даже нашел запросы на GitHub, в которых функция использования необработанной подписи RSA преднамеренно не принимается. Я предполагаю, что вам нужно будет использовать специализированные библиотеки, такие как ChillKat, или испытывать мод самостоятельно. @kelalaka Современным использованием, конечно, будет RSA-KEM, хотя он предназначен для шифрования. Старые схемы, такие как ISO 979 — э-э, 6 или 7 — подписи были бы другими.   -  person Maarten Bodewes    schedule 03.01.2020
comment
Эх, извините за много комментариев. Я думаю, вам нужно скомпилировать такую ​​функцию, как эта один (PyCryptoDome _encrypt), который использует параметры CRT для шифрования дополненного хэша. Я предполагаю, что Python нуждается в библиотеке Extreme Hazmat :|   -  person Maarten Bodewes    schedule 03.01.2020
comment
@Maarten-reinstateMonica, поэтому вывод таков: на данный момент у нас нет библиотеки на python, которая поддерживает NONEWithRSA. Если мне нужно реализовать это, что может быть решением ??   -  person varad    schedule 06.01.2020
comment
Ну, должны быть библиотеки (например, Chilkat2 для Python), но это платное ПО. Однако я не могу найти его в стандартных библиотеках. Так что мой совет — взять его из реализации PKCS#1 v1.5 или OAEP и надеяться, что он хоть как-то устойчив к атакам по сторонним каналам… Может быть, имеет смысл провести немного больше исследований, я мог пропустить библиотека...   -  person Maarten Bodewes    schedule 06.01.2020
comment
@Maarten-reinstateMonica Мне не удалось найти библиотеку, которая делает это. Поэтому я написал эту часть кода на Java. Это работает нормально.   -  person varad    schedule 06.01.2020


Ответы (2)


Недавно я столкнулся с той же проблемой, пытаясь поддержать генерацию подписи для приложения Android, которое ожидает эквивалент NONEwithRSA. В моем случае на самом деле это была ситуация, когда приложение использует хеш, но в нем явно отсутствует префикс кода ASN.1.

Оказывается, шифр Java RSA работает как по волшебству за кулисами и автоматически использует заполнение блока подписи типа PKCS#1 (так что каждая комбинация сообщения/ключа создает одну и ту же подпись).

Это вызвало у меня проблемы, потому что я изначально думал, что смогу просто зашифровать необработанный хеш, используя закрытый ключ в Python, и тогда он будет совместим с Java. Однако при шифровании с помощью закрытого ключа в PyCryptodome используется тип блока шифрования PKCS#1, который вызывает исключение неподдерживаемой версии v15 при проверке с помощью Java.

Самым простым решением, которое я нашел, было использование пакета rsa (https://pypi.org/project/rsa/) и получить прямой доступ к некоторым из его внутренних методов.

    key = rsa.PrivateKey.load_pkcs1(
        RSA.import_key(base64.b64decode(private_key)).export_key()
    )
    keylength = rsa.pkcs1.common.byte_size(key.n)
    padded = rsa.pkcs1._pad_for_signing(message, keylength)
    payload = rsa.pkcs1.transform.bytes2int(padded)
    encrypted = key.blinded_encrypt(payload)
    block = rsa.pkcs1.transform.int2bytes(encrypted, keylength)
    signature = base64.b64encode(block).decode()

По сути, этот код возникает в методе sign_hash модуля rsa/pkcs1.py. Мы просто не добавляем код ASN.1.

Это решение, по общему признанию, хрупкое, но оно должно дать вам хорошую рабочую ссылку, чтобы сделать что-то более надежное.

Проверка

Эта процедура генерации подписи Python создает подпись, идентичную алгоритму Java NONEwithRSA, для того же ключа/ввода.

private_key = "MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDhqFoTCc5FEgL3OiM4yEmN/rLkS+bSBigRLW+2N+TdbR/n1O48fUqUoPPa4sEETz7KRHI8EIl+FISyJCk07sks47ucbdfmy0d9omb/GmDnwiRwKe0vFtoTZnrO+sW+l45VEmOymfPYeo6E5pdi9jp7oVZufGiFGryp9DX6mzmpw6unjeZpSfqWaQfba4+EjxO8uiN2GKNlFFxyL6ohrKafGsS9rPaMZv9U4dGpY2ls1855kt8WG4RCHxv8Dh5e0Uj2R3VVnuM/ZvwrW/KLvAf6aUWOZxZhqLknBLUrN+Wh+1KcwZ3PsUoZWAWtY/IIisy/EWatNZYQ0nbk9vFYeEYvAgMBAAECggEBANCJuXxAopwYMh9xXd160ubji0UJk+1h/SMqv0NBUkMDNuoTUHscOpEyUUlmJoQqC9f2+y7hBX87zQmn+2DMj6PpbZb5t/20vIOxUsfMA+QJ+YyiYmwTDUPCgBnJjHc/Li0WSoZiWgQj9pOBWkP9Uzyvc+W1GQ5xEAtsAXpwexCTZB/4Ce6Kmlps4K7QyAJhZnefDNOdtvDzqDlYP0hCfolNK9Y7w0+eGYRBMw7eoxtPlWhAwW+/49/Kljf6YOGYPDY9fP+X+U06YNyuP4kbWLRSaW+uYfLzol2lN1U9y9O83FbvIYI/m4fJVq0xveTkpTj6nwAm6FHcumsoIXEEQykCgYEA937skZ01b6/ea04erQA4kCZZIdAVXrZBSuFHsK/XozBLwVxE3rIIiGAVP5F27GFYsQZzqavx70IIJ/jjYYTc2HRxkT+yU4eXJKTwZTsyuGgR9YAqQrTgfA38NIJ3fYMcwzLOd5gR32GRWJELZI+DT4h+y2PjNPckXfuDQXurMQUCgYEA6WlUgic4yuhnjsT6lZVHCS+RFcp4rgSMBufn24zVkFLS5ajiczth2qMKQquewawoHnGWPQrD1JC1uPtZVL1Xu8oySx13BFlY2Mr6uTJG2TIMY2sh1Nk85QAUXye2nfS1LcuZ3hr6De/B2OCtf+F23dHZ9domxMzhyVQ6hklU0KMCgYEA2+izW09Mw8iI4N+w8hdYKv1oBVtXIajfqyExChW0jRPEvDK4Hwgh/MsjFjCycqasn98TtpdJiUYq6RmFMIlVkh8lKeJnGUFJUw4bbFeFYcVW78TJyCvfq1rh1eZ3NdbiH69tNWaxusJ+2ytF7Dx4eJrXzvEaWV2fusJYhj7UheECgYEAimIHV273jd1B4HRKeLrbNcf8xaWX7NwHROTjItiZmYpxPPXm5Bi1yFFXePWVFxABbSM8xo4GUpVFGBn0+XeUd2LGdKBQesSaF1YoVfpH5Od2Ts8qbK4rGz3z1gKOk5GMhU0ruXHXBMI79GAonSPIPHT5O3iN+anax2lnlUwOu+8CgYEAj/I5LNVX+ocHvM/VaaDAXPPEvcv00Wl84PWDJnKe2kOz7ouKU2fqvmv1LUHBDiPaTL3vK5REKFA0NQSQ4SzbHX1BAdNuZAOMP3Fmtd5CigOJx4lr508rutyCDNBlGjvXLuV7IzjelT4C6o0QoHLPIKN7r57jxP+uOcbMgmuVNmI="
message = "PLWReC8yuVEViE6kpS9tV2ueldyruoCMBLthuPgKDnw="

Декодируйте сообщение из базы 64 в массив байтов на каждом языке. Предоставленный ранее метод подписи Python создаст следующую подпись:

signature_result = "WhM77ROa7Dl0HmO6QdMQjkQ336N2gfi4c2QuLKkIHtxWZQDm7xmLbzbcVxzN3A6hrSdZIS/v5HIpuLe5MovEno6t5QhTyONL4J0evYeO3Ou784qQZORXRlgoOCbkZu25Ty9hBy043HaeUprkOC5f7hcJjRmrel1+izD25NFEDreCnC3paPJqzMXQyUI6gioqdaxth3H0lRW9rAu4pgH/Hgt0/ljd0ch8FJPhfEUNKAV6wltp4h53D9gSlJVBCW2/VFDpzfTHkgkNK+egA2GTc/glWKQ0jm2tEVWniR9nia2bJfNJ/LWrr19ktejex+7g93569HX8w+jp6dAlSIMk9Q=="

Следующий код Java дает тот же результат:

    Signature instance = Signature.getInstance("NONEwithRSA");
    instance.initSign(privateKey);
    instance.update(Base64.decode(data, Base64.NO_WRAP));
    byte[] signature = instance.sign();
    String b64Signature = Base64.encodeToString(signature, Base64.NO_WRAP);
person dmartens    schedule 08.07.2021

Да, вы можете использовать библиотеку cryptography с Предварительно хеширован.

Это предполагает, что ваши данные уже являются выходом хэша (поэтому он не добавит дополнительного хеширования):

import hashlib
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding, rsa, utils

private_key = rsa.generate_private_key(
    public_exponent = 65537,
    key_size = 2048,
    backend = default_backend()
)
public_key = private_key.public_key()

# Compute the hash here
prehashed_msg = hashlib.sha256(b"A message I want to sign").digest()

# Sign the hash (do not perform any hashing)
signature = private_key.sign(
    prehashed_msg,
    padding.PSS(
        mgf = padding.MGF1(hashes.SHA256()),
        salt_length = padding.PSS.MAX_LENGTH),
    utils.Prehashed(hashes.SHA256())
)

# Verify
public_key.verify(
    signature,
    prehashed_msg,
    padding.PSS(
        mgf = padding.MGF1(hashes.SHA256()),
        salt_length = padding.PSS.MAX_LENGTH),
    utils.Prehashed(hashes.SHA256())
)
person nicolas    schedule 03.01.2020
comment
Это не тот режим NoneWithRSA, который был явно запрошен. Боюсь, просто пример RSA не подойдет. - person Maarten Bodewes; 03.01.2020