windows cryptography openssl rsa cryptoapi

windows - RSA_public_decrypt y MS Crypto API equivalent



cryptography openssl (3)

Estoy tratando de desarrollar una solución de verificación de licencia. Las licencias están codificadas en el servidor utilizando la función RSA_private_encrypt de OpenSSL.

Para Mac OX XI, use RSA_public_decrypt y funciona como un amuleto. En Windows debo usar muy poco código, así que no puedo enlazar con OpenSSL u otra lib Y tengo que usar MS Crypto API.

He pasado varios días tratando de descubrir qué está mal, pero sin suerte. Puedo importar con éxito la clave pública, pero aquí mi éxito termina. Soy consciente de que necesito revertir el orden de bytes con CAPI, por lo que este podría no ser el problema.

He intentado todo, incluso CryptVerifyMessageSignatureWithKey y CryptDecodeObject para cargar el blob con diferentes params, pero todavía no CryptDecodeObject suerte.

Siempre termina con GetLastError() == CRYPT_E_ASN1_BADTAG , lo que supongo que significa que el BLOB no tiene formato ASN1 ... Google no dice nada en el formato de salida de RSA_private_encrypt ... por lo que estoy completamente perdido aquí.

Aquí está el código OS X basado en OpenSSL:

void cr_license_init(const char* lic) { __cr_license_ = lic; unsigned char lic_encoded[CR_LIC_LEN]; BIO* b64 = BIO_new(BIO_f_base64()); BIO* licIn = BIO_new_mem_buf((void*)lic, -1); licIn = BIO_push(b64, licIn); if(BIO_read(licIn, lic_encoded, CR_LIC_LEN) == CR_LIC_LEN) { const unsigned char* key_data = license_pub_der; RSA* r = d2i_RSA_PUBKEY(NULL, &key_data, sizeof(license_pub_der)); if(r != NULL) { if(__cr_license_data_ != NULL) { free((void*)__cr_license_data_); } __cr_license_data_ = malloc(CR_LIC_LEN); if(RSA_public_decrypt(CR_LIC_LEN, lic_encoded, (unsigned char*)__cr_license_data_, r, RSA_PKCS1_PADDING) &lt= 0) { free((void*)__cr_license_data_); __cr_license_data_ = NULL; } RSA_free(r); } } BIO_free_all(licIn); }

Esta parte del código en Windows funciona bien, así que asumo que la clave pública no es un problema.

__cr_license_ = lic; unsigned char lic_encoded[CR_LIC_LEN]; DWORD dwSize; if(CryptStringToBinaryA(__cr_license_, 0/*autocalculate*/, CRYPT_STRING_BASE64, lic_encoded, &dwSize, NULL, NULL) && dwSize == CR_LIC_LEN) { HCRYPTPROV hProv; if(CryptAcquireContext(&hProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { PCERT_PUBLIC_KEY_INFO pki = NULL; DWORD dwKeySize; if(CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, license_pub_der, sizeof(license_pub_der), CRYPT_ENCODE_ALLOC_FLAG, NULL, &pki, &dwKeySize)) { HCRYPTKEY hKey = 0; if(CryptImportPublicKeyInfo( hProv, X509_ASN_ENCODING, pki, &hKey)) {

Pero después de eso, todo lo que trato de hacer con el mensaje me lleva a CRYPT_E_ASN1_BADTAG . Intenté CryptMsgOpenToDecode con CryptMsgUpdate , CryptDecodeObject , CryptVerifyMessageSignatureWithKey - nada funciona.

Básicamente, creo que el problema está en la incompatibilidad pkcs1 y pkcs7 como se mencionó en owlstead. ¿Alguien tiene experiencia trabajando con el formato pkcs1 importando / convirtiendo / etc. con MS CAPI?

Cualquier ayuda o incluso una pista se aprecia mucho! ¡Gracias por adelantado!


A menos que sea tan obvio que ha intentado pero omitió enumerarlo o no entiendo su pregunta de otra manera, creo que debería usar CryptDecrypt para descifrar la licencia, no las funciones que menciona en la pregunta. Tenga en cuenta que dado que parece estar usando OpenSSL con PKCS # 1 v1.5 padding y CryptoAPI no parece respaldar eso (no lo han probado, pero las especificaciones solo incluyen PKCS # 1 v2 OAEP), probablemente tendrá que usar CRYPT_DECRYPT_RSA_NO_PADDING_CHECK y verifique y elimine manualmente el relleno de PKCS # 1 v1.5 después del descifrado.


Está mezclando formatos de firma de nivel superior e inferior. OpenSSL asume las firmas PKCS # 1 v1.5 de forma predeterminada, que contiene solo los datos de firma. Windows parece apropiarse de los contenedores PKCS # 7. Estos pueden contener un PKCS # 1 v1.5, pero esos y otros datos se envuelven utilizando el formato de etiqueta / longitud BER ASN.1. Si la API de Microsoft intenta decodificar esto, asumirá que la firma cruda es el formato del contenedor y la decodificación fallará.


OpenSSL exporta claves con un encabezado adicional que CryptoAPI no espera.

Encabezado para clave privada (en notación ASN.1):

Offset| Len |LenByte| ======+======+=======+====================================================================== 0| 630| 3| SEQUENCE : 4| 1| 1| INTEGER : 0 7| 13| 1| SEQUENCE : 9| 9| 1| OBJECT IDENTIFIER : rsaEncryption [1.2.840.113549.1.1.1] 20| 0| 1| NULL : 22| 608| 3| OCTET STRING : ... actual key data go here ...

Encabezado para clave pública (en notación ASN.1):

Offset| Len |LenByte| ======+======+=======+====================================================================== 0| 159| 2| SEQUENCE : 3| 13| 1| SEQUENCE : 5| 9| 1| OBJECT IDENTIFIER : rsaEncryption [1.2.840.113549.1.1.1] 16| 0| 1| NULL : 18| 141| 2| BIT STRING UnusedBits:0 : ... actual key data go here ...

Estos encabezados son lo que hace que CryptDecodeObjectEx se ahogue. Espera datos RAW clave, sin ningún encabezado.

Entonces, básicamente, necesitas:

  1. (Opcional) Convierta .PEM en .DER con CryptStringToBinary.
  2. Compruebe si DER comienza con los encabezados mencionados anteriormente. Para eso, necesita leer datos codificados en ASN.1.
  3. (Opcional) Omita el encabezado mencionado anteriormente y busque directamente los datos de la clave (comienza con SECUENCIA que incluye 2 INTEGER para clave pública o 9 INTEGER para clave privada).
  4. Alimente el resultado a CryptDecodeObjectEx (X509_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB / PKCS_RSA_PRIVATE_KEY).
  5. Importar claves con CryptImportKey.