Visão geral de criptografia, assinaturas digitais e algoritmos de hash no .NET

Este artigo apresenta uma visão geral dos métodos de criptografia e práticas compatíveis com o .NET, incluindo os manifestos do ClickOnce.

Introdução à criptografia

Redes públicas como a Internet não fornecem um meio de comunicação segura entre entidades. A comunicação por essas redes está suscetível a ser lida ou até mesmo modificada por terceiros não autorizados. A criptografia ajuda a proteger os dados de serem exibidos, fornece maneiras de detectar se os dados foram modificados e ajuda a fornecer um meio seguro de comunicação em canais não seguros. Por exemplo, os dados podem ser criptografados usando um algoritmo de criptografia, transmitidos em um estado criptografado e posteriormente descriptografados pela parte pretendida. Se um terceiro interceptar os dados criptografados, será difícil decifrar.

No .NET, as classes no namespace System.Security.Cryptography gerenciam muitos detalhes de criptografia para você. Alguns são wrappers para implementações do sistema operacional, enquanto outros são implementações puramente gerenciadas. Você não precisa ser um especialista em criptografia para usar essas classes. Quando você cria uma instância de uma das classes de algoritmo de criptografia, as chaves são geradas automaticamente para facilitar o uso e as propriedades padrão são o mais seguras e protegidas possível.

Primitivos criptográficos

Em uma situação típica em que a criptografia é usada, duas partes (Alice e Bob) se comunicam por um canal não seguro. Alice e Bob querem garantir que sua comunicação permaneça incompreensível por qualquer um que possa estar ouvindo. Além disso, como Alice e Bob estão em locais remotos, Alice deve garantir que as informações recebidas de Bob não foram modificadas por ninguém durante a transmissão. Ela também deve ter certeza de que a informação realmente se origina de Bob, e não de alguém que está se passando por Bob.

A criptografia é usada para atingir as seguintes metas:

  • Confidencialidade: ajudar a proteger a identidade ou os dados de um usuário de serem lidos.

  • Integridade dos dados: ajudar a proteger os dados de serem alterados.

  • Autenticação: garantir que os dados sejam originados de uma parte específica.

  • Não repúdio: impedir que uma determinada parte negue que enviou uma mensagem.

Para atingir essas metas, você pode usar uma combinação de algoritmos e práticas conhecidas como primitivos criptográficos para criar um esquema criptográfico. A tabela a seguir lista os primitivos criptográficos e seus usos.

Primitivo criptográfico Use
Criptografia de chave secreta (criptografia simétrica) Executa uma transformação nos dados para evitar que eles sejam lidos por terceiros. Esse tipo de criptografia usa uma só chave secreta compartilhada para criptografar e descriptografar dados.
Criptografia de chave pública (criptografia assimétrica) Executa uma transformação nos dados para evitar que eles sejam lidos por terceiros. Esse tipo de criptografia usa um par de chaves públicas/privadas para criptografar e descriptografar dados.
Assinatura criptográfica Ajuda a verificar se os dados se originam de uma parte específica criando uma assinatura digital exclusiva para essa parte. Esse processo também usa funções de hash.
Hashes criptográficos Mapeia dados de qualquer comprimento para uma sequência de bytes de comprimento fixo. Hashes são estatisticamente exclusivos; uma sequência de dois bytes diferente não terá hash para o mesmo valor.

Criptografia de chave secreta

Algoritmos de criptografia de chave secreta usam uma só chave secreta para criptografar e descriptografar dados. Você deve proteger a chave do acesso por agentes não autorizados, pois qualquer parte que tenha a chave pode usá-la para descriptografar seus dados ou criptografar seus próprios dados, alegando que eles se originaram de você.

A criptografia de chave secreta também é conhecida como criptografia simétrica porque a mesma chave é usada para criptografia e descriptografia. Algoritmos de criptografia de chave secreta são muito rápidos (comparados a algoritmos de chave pública) e são adequados para executar transformações criptográficas em grandes fluxos de dados. Algoritmos de criptografia assimétrica, como o RSA, são limitados matematicamente na quantidade de dados que podem criptografar. Algoritmos de criptografia simétrica geralmente não têm esses problemas.

Um tipo de algoritmo de chave secreta chamado codificação de bloco é usado para criptografar um bloco de dados por vez. Bloquear criptografias como DES (Data Encryption Standard), TripleDES e AES (Advanced Encryption Standard) transformam criptograficamente um bloco de entrada de n bytes em um bloco de saída de bytes criptografados. Se você quiser criptografar ou descriptografar uma sequência de bytes, deverá fazer isso bloco a bloco. Como n é pequeno (8 bytes para DES e TripleDES; 16 bytes [o padrão], 24 bytes ou 32 bytes para AES), os valores de dados maiores que n precisam ser criptografados um bloco por vez. Os valores de dados menores que n precisam ser expandidos para n para serem processados.

Uma forma simples de codificação de bloco é chamada de modo BCE (codebook eletrônico). O modo BCE não é considerado seguro, pois não usa um vetor de inicialização para inicializar o primeiro bloco de texto sem formatação. Para uma determinada chave secreta k, uma codificação de bloco simples que não usa um vetor de inicialização criptografará o mesmo bloco de entrada de texto sem formatação no mesmo bloco de saída de texto cifrado. Portanto, se você tiver blocos duplicados no fluxo de texto sem formatação de entrada, terá blocos duplicados no fluxo de criptografia de saída. Esses blocos de saída duplicados alertam os usuários não autorizados para a criptografia fraca que usavam os algoritmos que poderiam ter sido empregados e os possíveis modos de ataque. O modo de criptografia do BCE é, portanto, bastante vulnerável à análise e, em última análise, à descoberta de chaves.

As classes de criptografia de bloco fornecidas na biblioteca de classes base usam um modo de encadeamento padrão chamado CBC (encadeamento de bloco de criptografia), embora você possa alterar esse padrão se desejar.

As criptografias CBC superam os problemas associados às criptografias do BCE usando um IV (vetor de inicialização) para criptografar o primeiro bloco de texto sem formatação. Cada bloco subsequente de texto sem formatação passa por uma operação OR (XOR) exclusiva bit a bit com o bloco de criptografia anterior antes de ser criptografado. Cada bloco de criptografia depende, portanto, de todos os blocos anteriores. Quando esse sistema é usado, cabeçalhos de mensagem comuns que podem ser conhecidos por um usuário não autorizado não podem ser usados para fazer engenharia reversa de uma chave.

Um modo de comprometer dados criptografados com uma criptografia CBC é executar uma pesquisa exaustiva de cada chave possível. Dependendo do tamanho da chave usada para executar a criptografia, esse tipo de pesquisa é muito demorado até mesmo usando os computadores mais rápidos, portanto, é inviável. Tamanhos de chave maiores são mais difíceis de decifrar. Embora a criptografia teoricamente não impossibilite um adversário de recuperar os dados criptografados, ela aumenta o custo de fazer isso. Se levarem três meses para executar uma pesquisa exaustiva para recuperar dados significativos apenas por alguns dias, o método de pesquisa exaustivo será impraticável.

A desvantagem da criptografia de chave secreta é que ela presume que duas partes concordaram com uma chave e IV e comunicaram seus valores. O IV não é considerado um segredo e pode ser transmitido sem texto sem formatação com a mensagem. No entanto, a chave deve ser mantida em segredo de usuários não autorizados. Devido a esses problemas, a criptografia de chave secreta geralmente é usada junto com a criptografia de chave pública para comunicar privadamente os valores da chave e IV.

Supondo que Alice e Bob sejam duas partes que desejam se comunicar usando um canal não seguro, é possível usar a criptografia de chave secreta da seguinte maneira: Alice e Bob concordam em usar um algoritmo específico (AES, por exemplo) com uma chave específica e IV. Alice compõe uma mensagem e cria um fluxo de rede (talvez um pipe nomeado ou um email de rede) no qual enviar a mensagem. Em seguida, ela criptografa o texto usando a chave e o IV e envia a mensagem criptografada e p IV para Bob pela intranet. Bob recebe o texto criptografado e o descriptografa usando o IV e a chave acordada anteriormente. Se a transmissão for interceptada, o interceptador não poderá recuperar a mensagem original porque não conhece a chave. Nesse cenário, somente a chave deve permanecer em segredo. Em um cenário do mundo real, Alice ou Bob gera uma chave secreta e usa a criptografia de chave pública (assimétrica) para transferir a chave secreta (simétrica) para a outra parte. Para mais informações sobre criptografia de chave pública, confira a próxima seção.

O .NET fornece as seguintes classes que implementam algoritmos de criptografia de chave secreta:

  • Aes

  • HMACSHA256, HMACSHA384 e HMACSHA512 (Estes são algoritmos tecnicamente de chave secreta porque representam códigos de autenticação de mensagem calculados usando uma função de hash criptográfica combinada a uma chave secreta. Confira Valores de Hash mais adiante neste artigo.)

Criptografia de chave pública

A criptografia de chave pública usa uma chave privada que deve ser mantida em segredo de usuários não autorizados e uma chave pública que pode ser tornada pública para qualquer pessoa. A chave pública e a chave privada estão matematicamente vinculadas; os dados criptografados com a chave pública só podem ser descriptografados com a chave privada, e os dados assinados com a chave privada só podem ser verificados com a chave pública. A chave pública pode ser disponibilizada para qualquer pessoa; ela é usada para criptografar dados a serem enviados para o guardião da chave privada. Algoritmos criptográficos de chave pública também são conhecidos como algoritmos assimétricos porque é necessária uma chave para criptografar dados e outra chave para descriptografar dados. Uma regra criptográfica básica proíbe a reutilização de chaves e ambas as chaves devem ser exclusivas para cada sessão de comunicação. No entanto, na prática, as chaves assimétricas geralmente são de longa duração.

Duas partes (Alice e Bob) podem usar a criptografia de chave pública da seguinte maneira: primeiro, Alice gera um par de chaves públicas/privadas. Se Bob quer enviar uma mensagem criptografada para Alice, ele pede a ela a chave pública. Alice envia a chave pública a Bob por uma rede não seguras, e Bob usa essa chave para criptografar uma mensagem. Bob envia a mensagem criptografada para Alice, e ela a descriptografa usando sua chave privada. Se Bob recebeu a chave de Alice em um canal não seguro, como uma rede pública, Bob está aberto a um ataque man-in-the-middle. Portanto, Bob deve verificar com Alice que ele tem uma cópia correta de sua chave pública.

Durante a transmissão da chave pública de Alice, um agente não autorizado pode interceptar a chave. Além disso, o mesmo agente pode interceptar a mensagem criptografada de Bob. No entanto, o agente não pode descriptografar a mensagem com a chave pública. A mensagem só pode ser descriptografada com a chave privada de Alice, que não foi transmitida. Alice não usa sua chave privada para criptografar uma mensagem de resposta para Bob, porque qualquer pessoa com a chave pública poderia descriptografar a mensagem. Se Alice quer enviar uma mensagem de volta para Bob, ela pede a Bob a chave pública dele e criptografa sua mensagem usando essa chave pública. Bob então descriptografa a mensagem usando sua chave privada associada.

Nesse cenário, Alice e Bob usam a criptografia de chave pública (assimétrica) para transferir uma chave secreta (simétrica) e usam a criptografia de chave secreta para o restante da sessão.

A seguinte lista oferece comparações entre algoritmos criptográficos de chave pública e chave secreta:

  • Algoritmos criptográficos de chave pública usam um tamanho do buffer fixo, enquanto algoritmos criptográficos de chave secreta usam um buffer de comprimento variável.

  • Algoritmos de chave pública não podem ser usados para encadear dados em fluxos como os algoritmos de chave secreta podem, porque apenas pequenas quantidades de dados podem ser criptografadas. Portanto, as operações assimétricas não usam o mesmo modelo de streaming que operações simétricas.

  • A criptografia de chave pública tem um keyspace muito maior (intervalo de valores possíveis para a chave) do que a criptografia de chave secreta. Portanto, a criptografia de chave pública é menos suscetível a ataques exaustivos que tentam todas as chaves possíveis.

  • Chaves públicas são fáceis de distribuir porque não precisam ser protegidas, desde que exista algum modo de verificar a identidade do remetente.

  • Alguns algoritmos de chave pública (como RSA e DSA, mas não Diffie-Hellman) podem ser usados para criar assinaturas digitais para verificar a identidade do remetente de dados.

  • Algoritmos de chave pública são muito lentos em comparação a algoritmos de chave secreta e não são projetados para criptografar grandes quantidades de dados. Algoritmos de chave pública são úteis apenas para transferir quantidades muito pequenas de dados. Normalmente, a criptografia de chave pública é usada para criptografar uma chave e um IV a serem usados por um algoritmo de chave secreta. Depois que a chave e o IV são transferidos, a criptografia de chave secreta é usada para o restante da sessão.

O .NET fornece as seguintes classes que implementam algoritmos de chave pública:

O RSA permite criptografia e assinatura, mas o DSA só pode ser usado para assinatura. O DSA não é tão seguro quanto o RSA, e recomendamos RSA. Diffie-Hellman só pode ser usado para geração de chaves. Em geral, algoritmos de chave pública são mais limitados em seus usos do que algoritmos de chave privada.

Assinaturas digitais

Algoritmos de chave pública também podem ser usados para formar assinaturas digitais. As assinaturas digitais autenticam a identidade de um remetente (se você confiar na chave pública do remetente) e ajudam a proteger a integridade dos dados. Usando uma chave pública gerada por Alice, o destinatário dos dados de Alice pode verificar se Alice a enviou comparando a assinatura digital com os dados de Alice e a chave pública de Alice.

Para usar a criptografia de chave pública para assinar digitalmente uma mensagem, Alice primeiro aplica um algoritmo de hash à mensagem para criar um resumo da mensagem. O resumo da mensagem é uma representação compacta e exclusiva dos dados. Alice então criptografa o resumo da mensagem com sua chave privada para criar sua assinatura pessoal. Ao receber a mensagem e a assinatura, Bob descriptografa a assinatura usando a chave pública de Alice para recuperar o resumo da mensagem e hashes da mensagem usando o mesmo algoritmo de hash que Alice usou. Se o resumo da mensagem que Bob calcula corresponder exatamente ao resumo da mensagem recebido de Alice, Bob terá certeza de que a mensagem veio do proprietário da chave privada e que os dados não foram modificados. Se Bob confia que Alice detém a chave privada, ele saberá que a mensagem veio de Alice.

Observação

Uma assinatura pode ser verificada por qualquer pessoa porque a chave pública do remetente é de conhecimento comum e normalmente está incluída no formato de assinatura digital. Esse método não mantém o sigilo da mensagem; para que a mensagem seja secreta, ela também deve ser criptografada.

O .NET fornece as seguintes classes que implementam algoritmos de assinatura digital:

Valores de hash

Os algoritmos de hash mapeiam valores binários de um comprimento arbitrário para valores binários menores de um comprimento fixo, conhecidos como valores de hash. Um valor de hash é uma representação numérica de um dado. Se você criar um parágrafo de texto sem formatação e alterar até mesmo uma letra do parágrafo, um hash subsequente produzirá um valor diferente. Se o hash for criptograficamente forte, seu valor mudará significativamente. Por exemplo, se um só bit de uma mensagem for alterado, uma função hash forte poderá produzir uma saída que difere em 50%. Muitos valores de entrada podem hash para o mesmo valor de saída. No entanto, é computacionalmente inviável encontrar duas entradas distintas que fazem hash para o mesmo valor.

Duas partes (Alice e Bob) poderiam usar uma função de hash para garantir a integridade da mensagem. Eles selecionariam um algoritmo de hash para assinar suas mensagens. Alice escreveria uma mensagem e então criaria um hash dessa mensagem usando o algoritmo selecionado. Eles seguiriam um dos seguintes métodos:

  • Alice envia a mensagem de texto sem formatação e a mensagem de hash (assinatura digital) para Bob. Bob recebe e faz hashes da mensagem, então compara seu valor de hash com o valor de hash que recebeu de Alice. Se os valores de hash forem idênticos, a mensagem não foi alterada. Se os valores não forem idênticos, a mensagem será alterada depois que Alice a escreveu.

    Infelizmente, esse método não estabelece a autenticidade do remetente. Qualquer um pode se passar por Alice e enviar uma mensagem para Bob. Eles podem usar o mesmo algoritmo de hash para assinar sua mensagem, e tudo o que Bob pode determinar é que a mensagem corresponde à assinatura. Esta é uma forma de um ataque man-in-the-middle. Para obter mais informações, confira o exemplo de comunicação segura CNG (Cryptography Next Generation).

  • Alice envia a mensagem de texto sem formatação para Bob por um canal público não seguro. Ela envia a mensagem de hash para Bob por um canal privado seguro. Bob recebe a mensagem de texto sem formatação, aplica hash a ela e compara o hash com o hash trocado privadamente. Se os hashes corresponderem, Bob saberá de duas coisas:

    • A mensagem não foi alterada.

    • O remetente da mensagem (Alice) é autêntico.

    Para que esse sistema funcione, Alice deve ocultar seu valor de hash original de todas as partes, exceto Bob.

  • Alice envia a mensagem de texto sem formatação para Bob por meio de um canal público não seguro e coloca a mensagem hash em seu site que pode ser visualizado publicamente.

    Esse método impede a violação de mensagens impedindo que qualquer pessoa modifique o valor de hash. Embora a mensagem e seu hash possam ser lidos por qualquer pessoa, o valor de hash só pode ser alterado por Alice. Um invasor que queira se passar por Alice precisaria acesso ao site de Alice.

Nenhum dos métodos anteriores impedirá alguém de ler as mensagens de Alice, pois elas são transmitidas sem texto. A segurança total normalmente requer assinaturas digitais (assinatura de mensagem) e criptografia.

O .NET fornece as seguintes classes que implementam algoritmos de hash:

O .NET também fornece MD5 e SHA1. Porém, os algoritmos MD5 e SHA-1 foram considerados inseguros, e SHA-2 agora é recomendado. SHA-2 inclui SHA256, SHA384 e SHA512.

Geração de número aleatório

A geração de número aleatório é parte integral de muitas operações criptográficas. Por exemplo, as chaves criptográficas precisam ser o mais aleatórias possível para que seja inviável reproduzi-las. Os geradores de números aleatórios criptográficos devem gerar uma saída computacionalmente inviável para prever com uma probabilidade melhor que metade. Portanto, qualquer método de previsão do próximo bit de saída não deve ter um desempenho melhor do que a adivinhação aleatória. As classes no .NET usam geradores de número aleatório para gerar chaves criptográficas.

A classe RandomNumberGenerator é uma implementação de um algoritmo de gerador de número aleatório.

Manifestos do ClickOnce

As seguintes classes de criptografia permitem obter e verificar informações sobre assinaturas de manifesto para aplicativos implantados usando a tecnologia ClickOnce:

Além disso, as seguintes classes fornecem informações de assinatura específicas:

Classes CNG (Cryptography Next Generation)

As classes CNG (Cryptography Next Generation) fornecem um wrapper gerenciado em torno das funções CNG nativas. (O CNG é a substituição de CryptoAPI.) Essas classes têm "Cng" como parte do nome. É fundamental para as classes de wrapper CNG a classe de contêiner de chave CngKey, que abstrai o armazenamento e o uso de chaves CNG. Essa classe permite armazenar um par de chaves ou uma chave pública com segurança e fazer referência a ela usando um nome de cadeia de caracteres simples. A classe de assinatura ECDsaCng baseada em curva elíptica e a classe de criptografia ECDiffieHellmanCng podem usar objetos CngKey.

A classe CngKey é usada para uma variedade de operações adicionais, incluindo abrir, criar, excluir e exportar chaves. Ela também dá acesso ao identificador de chave subjacente a ser usado ao chamar funções nativas diretamente.

O .NET também inclui uma variedade de classes CNG de suporte, como as seguintes:

Confira também