Проверка подписи rsa с помощью CryptoApi

Я пытаюсь рассчитать подпись сообщения rsa в коде python 2, используя пакет rsa или pyopenssl, и проверить его с помощью Microsoft CryptoApi. К сожалению, CryptVerifySignature всегда сообщает об ошибке 0x80090006: Invalid signature. Мой код питона:

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')

Подпись становится aa69b87840390e6032e57f4bb... байт, и такая же подпись у меня есть с помощью команды openssl dgst -md5 -sign private.pem -keyform PEM message.txt

Мой код С++ (короткая версия):

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);

Почему CryptVerifySignature считает, что моя подпись неверна? Найдены связанные вопросы (подписанная строка php openssl не проверяется от Win CryptoAPI) с советом инвертировать только байты данных подписи или подписи и открытого ключа, но выполнение этого с подписью ничего не меняет, а изменение открытого ключа приводит к ошибке ASN1 bad tag value met. 0x8009310b функцией CryptDecodeObjectEx.

Закрытый ключ был создан с помощью команды openssl genrsa -out private.pem 1024, а открытый — с помощью openssl rsa -in private.pem -inform PEM -outform PEM -pubout.


person xpp_T    schedule 29.06.2016    source источник
comment
Никогда не используйте этот закрытый ключ для чего-либо когда-либо после того, как вы опубликовали его в Интернете.   -  person Jesper Juhl    schedule 29.06.2016
comment
@JesperJuhl Я разместил его здесь только для того, чтобы любой мог воспроизвести такое же поведение с теми же данными. Этот ключ был сгенерирован только для этого поста.   -  person xpp_T    schedule 29.06.2016
comment
Это нормально. Все, что я хочу сказать, это то, что теперь, когда вы это сделали, этот ключ никогда не должен использоваться снова для ничего.   -  person Jesper Juhl    schedule 29.06.2016
comment
@xpp_T - я считаю, что вам нужно поменять местами байты в представлении ключа. CryptoAPI использует обратный порядок байтов, в то время как OpenSSL использует обратный порядок байтов. Это своего рода хорошо известная проблема, как только вы узнаете об этом. На эту тему есть несколько вопросов и ответов о переполнении стека. См. также Google: обратные байты openssl cryptoapi.   -  person jww    schedule 29.06.2016
comment
@jww - я пробовал это, добавляя key_buffer.resize(key_buffer_size); std::reverse(key_buffer.begin(), key_buffer.end()); перед вызовом CryptDecodeObjectEx, но он начал возвращать ошибку ASN1 bad tag value met. 0x8009310b. В любом случае буду думать в этом направлении, спасибо   -  person xpp_T    schedule 29.06.2016
comment
@xpp_T - я считаю, что вам нужно делать это для каждого большого целого числа, а не для всей ключевой структуры данных.   -  person jww    schedule 29.06.2016