1

I'm trying to calculate message rsa signature in python 2 code using rsa or pyopenssl package and verify it with microsoft CryptoApi. Unfortunately, CryptVerifySignature is always reporting error 0x80090006: Invalid signature. My python code:

import rsa
from OpenSSL import crypto

private_key = "-----BEGIN RSA PRIVATE KEY-----\nMIICWwIBAAKBgQDFloLNqx8YZHc8D5Pk6TniJo5nwdvObNilEih2VZtTPCHooa/A\nUhz0mqh/lOKkskDNa5RCz4iTWy7wug2v+1GGlFp9jEtYq6foVu8N9DChvc8OIVV1\n4PgyFCCbCJOi5ccVUh5KBCyO8FtxHiS6a8wE3glSwsUGfzpMdrfKCYENRwIDAQAB\nAoGAJOcHZwIevJ+G5WDDbm1gsiwhTJ+YPeV2UN4jUHaMm+8PJjOMb47meYipD6ru\n6XOhRrxg5Fl+WIcfLTaSd9uoTfYIJArTPF6R2EAkcPGeil3mMSDMwqTz5eStOI/q\nRkMryHN5lCOWkm3dWXNmT/75rnqJ4dFGE1iw5dL4OJbovQECQQDyabjCqIjsTHZW\nIohqQaZAbO+wLvP4IgeUvJ31CR5Xms61FUUOe5WEs6GnSfZlsdzun+58DBEsjo7J\ncqbZxTD5AkEA0KmdPO9LMSweTSqIbH72NcIuW8cQGI2oJKNLG4Ncc7GN6ElyHJ7H\nIbRfrb2UupsLvLTDFLIrOdGWG74JGkoAPwJARGJ+tKtGtSJ835+uTAtpExOoKlOU\nj5NKADOVe+KupJgPaBYv/P3wGBd0qvS6hcW/RbHoXSYqUh+FOF8Xoqd2QQJAJeuN\nHbPHEGqaHx/ppv3ztJVTY25rqGql8fKTBa77sDLGPT6LtFPOkHt9H8/iJX9jxKl9\nAlfWry09gFEqylJEdQJAHEA0/fDR+yHxxx4w9QnfbPtn0RNHQbBzKx0K37hMu/tE\n0wxp8BFWEs5YAWWNw82ft5yOg81MH1n8iCIHzWTKrw==\n-----END RSA PRIVATE KEY-----\n"

message = "testmessage"
key = rsa.PrivateKey.load_pkcs1(private_key)
signature = rsa.sign(message, key, 'MD5')

Signature becomes aa69b87840390e6032e57f4bb... bytes, and the same signature I have via openssl dgst -md5 -sign private.pem -keyform PEM message.txt command

My c++ code (short version):

const char kPublicVerifyKey[] =
  "-----BEGIN PUBLIC KEY-----\n"
  "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDFloLNqx8YZHc8D5Pk6TniJo5n\n"
  "wdvObNilEih2VZtTPCHooa/AUhz0mqh/lOKkskDNa5RCz4iTWy7wug2v+1GGlFp9\n"
  "jEtYq6foVu8N9DChvc8OIVV14PgyFCCbCJOi5ccVUh5KBCyO8FtxHiS6a8wE3glS\n"
  "wsUGfzpMdrfKCYENRwIDAQAB\n"
  "-----END PUBLIC KEY-----\n";

...

  std::vector<unsigned char> key_buffer(public_key.size);
  DWORD key_buffer_size = key_buffer.size();
  if (!CryptStringToBinaryA(static_cast<const char*>(public_key.data),
    public_key.size, CRYPT_STRING_BASE64HEADER,
    key_buffer.data(), &key_buffer_size, NULL, NULL))
  {
    return false;
  }

  CERT_PUBLIC_KEY_INFO* key_info = nullptr;
  DWORD key_info_size = 0;
  if (!CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO,
    key_buffer.data(), key_buffer_size,
    CRYPT_ENCODE_ALLOC_FLAG, NULL, &key_info, &key_info_size))
  {
    return false;
  }

  HCRYPTPROV crypt_context;
  if (!CryptAcquireContext(&crypt_context, NULL, NULL,
    PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
  {
    return false;
  }

  HCRYPTKEY crypt_key;
  if (!::CryptImportPublicKeyInfo(crypt_context,
    X509_ASN_ENCODING, key_info, &crypt_key))
  {
    return false;
  }

  HCRYPTHASH hash;
  if (!::CryptCreateHash(crypt_context, CALG_MD5, 0, 0, &hash)) {
    return false;
  }

  if (!::CryptHashData(hash,
    static_cast<const BYTE*>(message.data), message.size, 0))
  {
    return false;
  }

  BOOL result = ::CryptVerifySignature(hash,
    static_cast<const BYTE*>(signature.data), signature.size,
    crypt_key, NULL, 0);

Why CryptVerifySignature think that my signature is incorrect? A found related questions (php openssl signed string not getting verified by Win CryptoAPI) with advice to reverse just signature or signature and public key data bytes, but doing this with signature changes nothing and reversing public key results in error ASN1 bad tag value met. 0x8009310b by CryptDecodeObjectEx function.

Private key was generated with openssl genrsa -out private.pem 1024 command and public was generated with openssl rsa -in private.pem -inform PEM -outform PEM -pubout

Community
  • 1
  • 1
xpp_T
  • 87
  • 5
  • 1
    *Never* use that private key for anything *ever* again, now that you've published it on the internet. – Jesper Juhl Jun 29 '16 at 16:34
  • @JesperJuhl I have posted it here only to let anyone reproduce the same behaviour with same data. This key was generated just for this post. – xpp_T Jun 29 '16 at 16:47
  • That's fine. All I'm saying is that now that you have done so, that key should *never* be used again for *anything*. – Jesper Juhl Jun 29 '16 at 16:52
  • 2
    @xpp_T - I believe you need to reverse the bytes in the representation of the key. CryptoAPI uses little endian, while OpenSSL uses big endian. Its kind of a well known problem once you learn about it. There's a few Q&A on Stack Overflow on the subject. Also see [Google: openssl cryptoapi reverse bytes](http://www.google.com/search?q=openssl+cryptoapi+reverse+bytes). – jww Jun 29 '16 at 17:12
  • @jww - I have tried this by adding `key_buffer.resize(key_buffer_size); std::reverse(key_buffer.begin(), key_buffer.end());` before CryptDecodeObjectEx call, but it started to return `ASN1 bad tag value met. 0x8009310b` error. Anyway, I will think in that direction, thanks – xpp_T Jun 29 '16 at 17:21
  • @xpp_T - I believe you need to do it for each big integer, and not the whole key data structure. – jww Jun 29 '16 at 18:03

0 Answers0