I'm trying to sign a message in Android, then send the message, signature and public key to a Django server for verification. I'm using pycryptodome on the server to verify, but verification always fails - problem is I have no idea which bit I'm doing wrong.
This is what I'm doing on Android:
Generate an RSA public private key
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()
Sign a message using the private key, encode signature as 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)
Extract the public key as base64
val keystoreEntry = keyStore.getEntry(KEY_ALIAS, null) as KeyStore.PrivateKeyEntry
val publicKey = Base64.encodeToString(keystoreEntry.certificate.publicKey.encoded, 0)
Send the message, signature (base64) and public key (base64) to server
Try to verify the signature
Then on the server (using Python 3, Django 1.11, pycryptodome 3.8.1), I receive the message, signed message, and public key:
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'])
At this point I have something like this:
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=
Then I try to verify like so:
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())
This always fails, and I can't figure out why. Some questions:
I've surrounded the public key with "-----BEGIN RSA KEY-----" and "-----END RSA KEY-----" - is that right? Without doing that I got an error about the RSA key format being invalid
My private key and signed message both have line breaks in them - should I remove these? I've tried with and without them, it doesn't seem to make a difference but would appreciate any advice
PyCharm complains about
signature.verify(digest, signature.encode())
:Expected type ModuleType, got SHA256Hash instead.
The docs say digest should be a type from Crypto.Hash, and I'm using Crypto.Hash.SHA256Hash, so why is PyCharm complaining? Can I ignore?
I'm using pycryptodome's PKCS1_v1_5 class to create a signature verifier. I have no idea what that is, I'm just following code I've seen on SO. Is it the right thing to use?
I'm using the algorithm "SHA256withRSA" to sign on android, and on the server using PKCS1_v1_5 with an RSA key and passing in a SHA256 hash of the message. I have very little understanding of all these standards. Does this sound right?
I have no idea where to go form here, any suggestions appreciated!