简体   繁体   中英

How to verify RSA signature generated by Python-RSA using Crypto++

I have a server written in Python, and a C++ client. The Python server has a private RSA key, and the redistributable C++ client has the paired public key. The C++ client sends a string to the Python server, the server generates a signature by encoding this string with its private key, and sends it to the client in ASCII format. Finally, the C++ client verifies this signature to ensure a) the signature comes from the paired key and no other, and b) the signature was made based on this specific string, and no other.

The Python side looks like this:

import rsa
from base64 import b64encode

str = "message"

pub, priv = rsa.newkeys(2048)

keyB64 = rsa.sign(str.encode('utf-8'), privkey, 'SHA-1')    
signature = b64encode(keyB64).decode('ascii')

with open("public_key.txt", "w") as file:
        file.write(pub.save_pkcs1().decode('utf8'))
        file.close()

With the generated public key file looking like this (just an example):

-----BEGIN RSA PUBLIC KEY----- MIGJAoGBALqrXqb17/TiXmGGbvbFwRMV+mbCqPtvnD0zlvIKxpJ4NSBVZ2Lz87SU Ww69uFILy19G6prThJAzHha9pa3fWRKRv5epMXcP6TFZ3er0h0uaxOKxle+OtpnC xyW+QMzkhuDL1gR1OrgVW6jCV6lmVdca63+m2PfTjQj1Vc64OyWBAgMBAAE= -----END RSA PUBLIC KEY-----

On the client side, I read this file and store the characters between the two tags in a string. Then it looks like this:

#include <./Cryptopp/rsa.h>
#include <./Cryptopp/hex.h>
#include <./Cryptopp/pssr.h>

inline bool RsaVerifyString(const std::string &aPublicKeyStrASCII,
                            const std::string &str,
                            const std::string &aSignatureStrASCII)
{

  // decode and load public key (using pipeline)
  CryptoPP::RSA::PublicKey publicKey;
  publicKey.Load(CryptoPP::StringSource(aPublicKeyStrASCII, true).Ref());

  // decode signature
  std::string decodedSignature;
  CryptoPP::StringSource ss(aSignatureStrASCII, true);

  // verify message
  bool result = false;
  CryptoPP::RSASS<CryptoPP::PSSR, CryptoPP::SHA1>::Verifier verifier(publicKey);
  CryptoPP::StringSource ss2(decodedSignature + str, true,
                             new CryptoPP::SignatureVerificationFilter(verifier,
                               new CryptoPP::ArraySink((unsigned char*)&result,
                                                       sizeof(result))));

  return result;
}

//...

std::string message("message");

if(RsaVerifyString(publicKeyASCII, message, signatureASCII))
{
    std::cout << "OK" << std::endl;
}

But it doesn't work: it always returns false, and CryptoPP's architecture is too complicated for me to debug - whereas I'm sure it's actually very simple and just a matter of adapting parameters.

Anyone with experience in these could tell me what I'm doing wrong?

Update

Trying to port it to PyCryptoDome to increase compatibility upon recommendation of a comment:

from Crypto import Random
from Crypto.Hash import SHA
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5

PRIV_PATH = '../priv.pem'
PUB_PATH= '../pub.pem'


def gen_key_pair():
    random_generator = Random.new().read
    key = RSA.generate(2048, random_generator)
    print(key.exportKey(), key.publickey().exportKey())
    with open(PRIV_PATH, 'wb') as file:
        file.write(key.exportKey())
    with open(PUB_PATH, 'wb') as file:
        file.write(key.publickey().exportKey())
    return key.exportKey(), key.publickey().exportKey()


def sign_message(message):
    key = RSA.importKey(open(PRIV_PATH, 'rb').read())
    h = SHA.new(message)
    signer = PKCS1_v1_5.new(key)
    signature = signer.sign(h)
    return signature


def verify_sign(message, signature):
    key = RSA.importKey(open(PUB_PATH, 'rb').read())
    h = SHA.new(message)
    verifier = PKCS1_v1_5.new(key)
    if verifier.verify(h, signature):
       print("The signature is authentic.")
    else:
       print("The signature is not authentic.")
       
# TEST CRYPTO
gen_key_pair()
message = 'Hello pycrypto!'.encode('utf-8')
signature = sign_message(message).hex()
print('signature='+signature)
verify_sign(message, bytes.fromhex(signature))

On the client side I expect to have the following changes to make (before cleaning the code):

inline bool RsaVerifyString(const std::string &aPublicKeyStrASCII,
                            const std::string &str,
                            const std::string &aSignatureStrASCII)
{

  // decode and load public key (using pipeline)
  CryptoPP::RSA::PublicKey publicKey;
  publicKey.Load(CryptoPP::StringSource(aPublicKeyStrASCII, true).Ref());

  // decode signature
  std::string decodedSignature;
  CryptoPP::StringSource ss(aSignatureStrASCII, true);

  // verify message
  bool result = false;
  CryptoPP::RSASS<CryptoPP::PKCS1v15, CryptoPP::SHA256>::Verifier verifier(publicKey);
  CryptoPP::StringSource ss2(decodedSignature + str, true,
                             new CryptoPP::SignatureVerificationFilter(verifier,
                               new CryptoPP::ArraySink((unsigned char*)&result,
                                                       sizeof(result))));

  return result;
}

I can't yet compile Crypto++ (it's throwing failures to make functions inline) but I doubt it would work as-is.

I abandoned Crypto++ because I couldn't get it to work on QtCreator + Windows, and used OpenSSL instead. It's horribly counterintuitive to code, but there is a lot of support and I got it to work with a member's help in this thread: Verify in OpenSSL C++ a signature generated in PyCryptoDome

Use this if you are flexible on the C++ implementation of the verifying process and you can't get Crypto++ to work, all the code is there.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM