Не удается проверить подпись RSA с помощью pycryptodome - PullRequest
0 голосов
/ 07 мая 2019

Я пытаюсь подписать сообщение в Android, а затем отправить сообщение, подпись и открытый ключ на сервер Django для проверки. Я использую pycryptodome на сервере для проверки, но проверка всегда завершается неудачей - проблема в том, что я понятия не имею, какой бит я делаю неправильно.

Вот что я делаю на Android:

Создание открытого закрытого ключа RSA

val keyPairGenerator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore")

val certStart = Calendar.getInstance()
val certEnd = Calendar.getInstance()
certEnd.add(Calendar.YEAR, 30)

val spec = KeyPairGeneratorSpec.Builder(context)
     .setAlias("MyKeyAlias")
     .setKeySize(1024)
     .setSubject(X500Principal("CN=CryptoIsHard"))
     .setSerialNumber(BigInteger.ONE)
     .setStartDate(certStart.time)
     .setEndDate(certEnd.time)
     .build()

 keyPairGenerator.initialize(spec, SecureRandom.getInstance("SHA1PRNG"))
 keyPairGenerator.generateKeyPair()

Подпишите сообщение с помощью закрытого ключа, закодируйте подпись как base64

val message = "some message"
val keystoreEntry = keyStore.getEntry("MyKeyAlias", null) as KeyStore.PrivateKeyEntry
val signer= Signature.getInstance("SHA256withRSA")
signer.initSign(keystoreEntry.privateKey)
signer.update(message.toByteArray(Charsets.UTF_8))
val signature = Base64.encodeToString(signature.sign(), 0)

Извлечение открытого ключа как base64

val keystoreEntry = keyStore.getEntry(KEY_ALIAS, null) as KeyStore.PrivateKeyEntry
val publicKey = Base64.encodeToString(keystoreEntry.certificate.publicKey.encoded, 0)

Отправить сообщение, подпись (base64) и открытый ключ (base64) на сервер

Попробуйте проверить подпись

Затем на сервере (используя Python 3, Django 1.11, pycryptodome 3.8.1) я получаю сообщение, подписанное сообщение и открытый ключ:

from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256

def verify_signature(request):

    response_dict = json.loads(str(request.body, encoding='utf-8'))
    public_key = str(response_dict ['publicKey'])
    signed_message = str(response_dict ['signedMessage'])
    message = str(response_dict ['message'])

На данный момент у меня есть что-то вроде этого:

print(public_key)

MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCkmWj9+FLqpCRl35Ac8mFqfJx390kgvhQzNXPp
8/OWqsLitbCQYtw3/sEY60Cz04A3onZNh8iwSms8iZLSZh9Y23/AhSyBXw7XVtCwXQfuZagMwPV2
OhZZGGC8IOsqUOnRswa9L2/SI1i5HQQNqcZ+Su1Po/Xr2+opKps+gHDmPwIDAQAB

print(signed_message)

mdlasq+sN2Hdi+qBTWp6EjMBGYQCCdTSuQlrPozoA3J6dwW0cTXbp7YefD2JlLp8pXkMfYmTivsN
dAKkY/dHEAfUm4YuvaBz72ogwFx8px20JQ0OKVO03FjcZuw1jAGYUrEt8eswTHfmN8yJ/lDVsUlb
UYe/VcD6O/0YWrXGPrI=

Тогда я пытаюсь проверить так:

formatted_public_key = "-----BEGIN RSA KEY-----\n{}-----END RSA KEY-----".format(public_key)
rsa_public_key = RSA.importKey(formatted_public_key)
signature = PKCS1_v1_5.new(rsa_public_key)
digest = SHA256.new()
digest.update(message.encode())

verified = signature.verify(digest, signed_message.encode())

Это всегда терпит неудачу, и я не могу понять, почему. Некоторые вопросы:

  • Я окружил открытый ключ "----- BEGIN RSA KEY -----" и "----- END RSA KEY -----" - это правильно? Без этого я получил ошибку о том, что формат ключа RSA неверен

  • Мой закрытый ключ и подписанное сообщение содержат разрывы строк - я должен удалить их? Я пробовал с ними и без них, похоже, это не имеет значения, но буду признателен за любой совет

  • PyCharm жалуется на signature.verify(digest, signature.encode()):

    Ожидаемый тип ModuleType, вместо него получен SHA256Hash.

В документах говорится, что дайджест должен быть типом из Crypto.Hash, а я использую Crypto.Hash.SHA256Hash, так почему же PyCharm жалуется? Могу ли я игнорировать?

  • Я использую класс PKCS1_v1_5 pycryptodome для создания верификатора подписи. Я понятия не имею, что это такое, я просто следую коду, который видел на SO. Это правильно использовать?

  • Я использую алгоритм "SHA256withRSA" для входа в Android и на сервере, используя PKCS1_v1_5 с ключом RSA и передачей хеша SHA256 сообщения. У меня очень мало понимания всех этих стандартов. Это звучит правильно?

Я понятия не имею, куда идти здесь, любые предложения приветствуются!

1 Ответ

1 голос
/ 07 мая 2019

Я понял это - я забыл, что подпись была закодирована в base64 на стороне Android, поэтому должна быть расшифрована на стороне сервера.Таким образом, изменив это:

signature.verify(digest, signed_message.encode())

на:

signature.verify(digest, base64.b64decode(signed_message))

Решает проблему:)

...