Interoperabilidade RSA: Descodificando BLOBs de Chave Privada CAPI do C# em Python

Visão Geral da Desencriptação RSA no C# com BLOBs de Chave Privada

Ao traablhar com criptografia RSA em abmientes Windows, frequentemente encontramos o uso de BLOBs (Binary Large Objects) de chave RSA da API Criptográfica (CAPI) para representar chaves públicas e privadas. O framework .NET, através de classes como RSACryptoServiceProvider, pode facilmente importar e utilizar estas chaves. O fragmento de código C# abaixo demonstra um método simples para desencriptar dados utilizando uma chave privada RSA fornecida no formato BLOB da Microsoft, codificada em Base64.

using System;
using System.Security.Cryptography; // Necessário para operações RSA

namespace ServicoCriptografia
{
    public class CriptoRSA
    {
        /// <summary>
        /// Desencripta um array de bytes utilizando uma chave privada RSA fornecida como BLOB codificado em Base64.
        /// </summary>
        /// <param name="dadosCifrados">Os dados a serem desencriptados.</param>
        /// <param name="chavePrivadaBase64">A chave privada RSA no formato BLOB da Microsoft, codificada em Base64.</param>
        /// <returns>Os dados desencriptados.</returns>
        public byte[] DesencriptarDados(byte[] dadosCifrados, string chavePrivadaBase64)
        {
            using (RSACryptoServiceProvider rsaProvedor = new RSACryptoServiceProvider())
            {
                // Converte a string Base64 do BLOB da chave privada para bytes e a importa.
                rsaProvedor.ImportCspBlob(Convert.FromBase64String(chavePrivadaBase64));

                // Realiza a desencriptação. 'false' indica que o padding deve ser PKCS#1 v1.5.
                return rsaProvedor.Decrypt(dadosCifrados, false);
            }
        }
    }
}

Analisando BLOBs de Chave Privada RSA do CAPI em Python

Embora o C# lide nativamente com estes BLOBs, outras linguagens como Python exigem uma análise manual da estrutura binária para extrair os componentes da chave (módulo, expoente público, expoente privado). Estes BLOBs seguem uma estrutura específica definida pela Microsoft CAPI:

  1. Um cabeçalho BLOB (BLOBHEADER / PUBLICKEYSTRUC) que identifica o tipo e algoritmo da chave.
  2. Uma estrutura RSAPUBKEY que detalha o tamanho da chave em bits e o expoente público.
  3. Os componentes numéricos da chave privada, como módulo (n), expoente público (e), expoente privado (d), e outros fatores auxiliares (p, q, dp, dq, iq), armazenados como arrays de bytes.

É crucial notar que todos os componentes numéricos dentro do BLOB são armazenados em ordem de bytes "little-endian". A classe Python a seguir demonstra como analisar um BLOB de chave privada RSA, extraindo os componentes necessários para construir um objeto de chave RSA utilizando a biblioteca PyCryptodome.

import struct
import base64
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5 as PKCS1_cipher

class InterpretadorBlobChaveRSA:
    """
    Analisa um BLOB de chave privada RSA no formato CAPI da Microsoft,
    usado por RSACryptoServiceProvider no C#, para extrair os componentes da chave.
    """
    def __init__(self, dados_blob: bytes):
        self._dados_originais = dados_blob
        self.modulus = None
        self.public_exponent = None
        self.private_exponent = None
        self._executar_analise()

    def _executar_analise(self):
        # 1. Analisar o BLOBHEADER (equivalente a PUBLICKEYSTRUC)
        # Formato: bType (BYTE), bVersion (BYTE), reserved (WORD), aiKeyAlg (DWORD)
        # Os valores esperados para uma chave privada RSA são:
        # bType = 0x07 (PRIVATEKEYBLOB)
        # aiKeyAlg = 0x0000a400 (CALG_RSA_KEYX)
        header_formato = '<bbhi a="" aikeyalg="struct.unpack_from(header_formato," all="" analisados="" analisar="" and="" arrays="" b="" bitlen="" bitlen:="" blob="" btype="" bversion="" bytes="" bytes.="" bytes_n="bitlen" bytes_pq="bitlen" capi="" chave="" chaves="" coefficient="" completa.="" componentes="" componentes_formato="f'<{bytes_n}s{bytes_pq}s{bytes_pq}s{bytes_pq}s{bytes_pq}s{bytes_pq}s{bytes_n}s'" constr="" constru="" construir_chave_rsa="" converte="" corresponde="" corretamente.="" d="" da="" dados_componentes="struct.unpack_from(componentes_formato," de="" def="" dentro="" dependem="" do="" dos="" e="" encontrado.="" espera="" esperado="" etc.="" exponent1="" exponent2="" fixa:="" foi="" foram="" formato:="" fornecido="" fun="" if="" ignorados="" inicio_componentes="tamanho_header" inteiros="" little-endian="" magic="" modulus="" n="" not="" num="" objeto="" ordem="" os="" outros="" para="" prime1="" prime2="" privada="" privadas="" privateexponent="" pubexp="" pycryptodome.="" raise="" reserved="" retorna="" return="" rsa="" rsa.construct="" rsa_pubkey_formato="<III" rsapubkey="" runtimeerror="" s="" self._dados_originais="" self.modulus="int.from_bytes(dados_componentes[0]," self.private_exponent="int.from_bytes(dados_componentes[6]," self.public_exponent="pubexp" t="" tamanho_header="struct.calcsize(header_formato)" tamanho_rsa_pubkey="struct.calcsize(rsa_pubkey_formato)" tamanhos="" tupla="" um="" uma="" utilizando="" v="" valueerror=""></bbhi>

Desencriptando Dados em Python

Com a chave privada RSA devidametne extraída e construída no Python, podemos prosseguir com a desencriptação dos dados. É importante lembrar de duas particularidades ao lidar com dados cifrados por RSACryptoServiceProvider no C#:

  1. Ordem dos Bytes: O C# pode gerar o texto cifrado em ordem de bytes "little-endian", enquanto bibliotecas Python geralmente esperam "big-endian". Se a desencriptação falhar, uma inversão do texto cifrado pode ser necessária.
  2. Preenchimento (Padding): O RSACryptoServiceProvider.Decrypt(..., false) usa o esquema de preenchimento PKCS#1 v1.5. Certifique-se de que a biblioteca Python esteja configurada para usar o mesmo esquema.

O exemplo a seguir demonstra como usar a chave construída para desencriptar um texto cifrado em Python.

# Exemplo de uso:
# Suponha que 'chave_privada_blob_base64' é a string Base64 da chave privada C#
# e 'texto_cifrado_base64' é o texto cifrado em Base64 gerado pelo C#.

# Decodifica o BLOB da chave privada de Base64 para bytes
chave_blob_bytes = base64.b64decode(chave_privada_blob_base64)

# Cria uma instância do interpretador e analisa o BLOB
interpretador = InterpretadorBlobChaveRSA(chave_blob_bytes)

# Constrói o objeto de chave privada RSA para uso com PyCryptodome
chave_rsa_privada = interpretador.construir_chave_rsa()

# Cria o objeto cipher para desencriptação com PKCS#1 v1.5 padding
cipher_rsa = PKCS1_cipher.new(chave_rsa_privada)

# Decodifica o texto cifrado de Base64 para bytes
texto_cifrado_bytes = base64.b64decode(texto_cifrado_base64)

# IMPORTANTE: Inverte a ordem dos bytes do texto cifrado, se necessário,
# devido à convenção little-endian do CAPI/Windows para CryptDecrypt.
texto_cifrado_para_desencriptar = texto_cifrado_bytes[::-1] 

# Desencripta os dados. O segundo argumento 'None' (ou 0) para decrypt é um marcador
# para o caso de falha de padding, que retornaria 'None' em vez de levantar uma exceção.
dados_desencriptados = cipher_rsa.decrypt(texto_cifrado_para_desencriptar, None)

print(f"Dados Desencriptados: {dados_desencriptados.decode('utf-8')}")

Tags: C# Python rsa Cryptodome PKCS1_v1_5

Publicado em 10-20 19:09