Criptografia em linguagem simples: analisamos criptografia simétrica e assimétrica usando o gráfico Star Wars como exemplo (Atualizado)

Olá a todos os leitores da Habr! Há pouco tempo, decidi lidar com algoritmos de criptografia e com os princípios da assinatura eletrônica. O tópico, eu acho, é interessante e relevante. No processo de estudo, tentei várias bibliotecas, mas a biblioteca PyCrypto é a mais conveniente do meu ponto de vista. Ela possui excelente documentação, acompanhada de exemplos.

imagem

Depois de ler o material, você aprenderá os seguintes pontos:

  1. O que é criptografia;
  2. Qual é a diferença entre criptografia simétrica e assimétrica;
  3. Nesse caso, é mais eficiente usar simétrico e em que criptografia assimétrica;
  4. O que é um hash de dados e por que é usado na criptografia;

A relevância do tópico está em constante crescimento. O uso da criptografia há muito tempo não se limita à criptografia de informações. De uma forma ou de outra, usamos algoritmos de criptografia diariamente ao visitar sites por meio do protocolo HTTPS, ao fazer compras com cartão de crédito e ao comunicar em mensagens instantâneas. Nos últimos anos, as tecnologias blockchain, que também são baseadas em criptografia, atraíram grande atenção.

O objetivo deste artigo é apresentar ao leitor algoritmos básicos de criptografia. Ao escrever um artigo, tentei prestar o máximo de atenção possível à questão da aplicação prática. Para programação, foi utilizado o Python 3.6. Ao escrever o código, tentei dividi-lo em partes separadas e comentar sobre todos os pontos-chave.

Neste artigo, não analisei a assinatura digital, mas depois de entender a criptografia assimétrica, o significado dessa tecnologia ficará claro.

Traçar


Vamos viajar mentalmente para o universo de Guerra nas Estrelas antes dos eventos do Episódio 6, quando as forças de resistência tomam conhecimento do início da construção de uma nova Estrela da Morte. O comando planeja introduzir um grupo de inteligência sob o disfarce de construtores. A operação é muito perigosa, a comunicação com a sede será difícil. No caso de uma situação de emergência, cada membro do grupo pode enviar e receber mensagens da sede em uma frequência desprotegida.

O objetivo do grupo de reconhecimento é qualquer dado que possa esclarecer a configuração, as armas e o objetivo da futura estação. Para armazenar dados, está planejado o desenvolvimento de equipamentos e softwares especiais.

A sede aprovou duas opções para esta operação:

Plano A - Retorno de agentes com determinadas forças rebeldes;
Plano B - transmissão remota de planos da própria Estrela da Morte usando equipamentos da estação. A transferência de informações será rápida, mas após a transferência, o agente provavelmente será calculado e capturado.

Esquema
imagem

Você é programador em uma equipe responsável pelo desenvolvimento de software.

Ao planejar uma operação, vários cenários negativos possíveis são considerados:

  • O inimigo interceptará o sinal, entenderá seu conteúdo sobre o planejamento do ataque e levará o objeto para mais perto das forças leais ao Império. Nesse caso, as perdas entre a resistência serão maiores;
  • Um dos espiões será capturado e durante o interrogatório revelará o plano de operação, o que pode levar ao comprometimento das chaves de criptografia (falaremos sobre elas abaixo);
  • Um espião com dados baixados pode ser interceptado por forças imperiais, que farão alterações no conteúdo, desinformando a resistência sobre as fraquezas da estação. Nesse caso, durante o ataque, a frota rebelde será enviada em uma direção falsa e gradualmente destruída;

A partir desses cenários, as tarefas são formuladas:

  1. O conteúdo deve ser criptografado e protegido com segurança contra alterações;
  2. Em caso de perda de chaves de criptografia ou seu comprometimento, deve ser possível obter novas chaves de criptografia remotamente, com uma frequência que pode ser tocada pelo adversário.

Criptografia de informações


Vamos resolver o problema da criptografia de informações:

Uma chave de criptografia é usada para criptografar e descriptografar informações. É a chave que torna a criptografia reversível. Cada agente receberá uma chave de criptografia. Após o download dos dados, o agente os criptografará e os enviará para a sede da resistência.

Descrição do princípio de criptografia
O método no qual a mensagem é criptografada e descriptografada usando uma única chave é chamada de criptografia simétrica .

imagem

O ponto fraco da criptografia simétrica é a chave de criptografia, ou melhor, sua entrega ao destinatário. Se a chave for comprometida durante a entrega, um terceiro poderá facilmente decodificar a mensagem. A força da criptografia simétrica é sua velocidade, o que possibilita a codificação de grandes quantidades de dados.

A criptografia assimétrica usa duas chaves conectadas uma à outra: pública e privada.

imagem

O mecanismo de ação é o seguinte:

  1. o destinatário envia a chave ABRIR ao remetente;
  2. o remetente codifica a mensagem usando a chave pública recebida. Ao mesmo tempo, a mensagem agora pode ser decodificada apenas com a chave privada;
  3. após o recebimento da mensagem criptografada, o destinatário a decodifica com a chave CLOSED (que foi gerada em conjunto com o público).


Vamos começar a programação! Para desenvolver o software necessário, usaremos a biblioteca Python chamada pycrypto . Ela possui excelente documentação e apresenta todos os algoritmos de criptografia comuns.

Para começar, desenvolveremos uma funcionalidade para criptografia simétrica chamada Advanced Encryption Standard (AES) . É um dos algoritmos de criptografia simétrica mais comuns.

from Crypto.Cipher import AES #   from Crypto.Hash import SHA256 #        SHA. from Crypto.Hash import MD5 #            32  from Crypto import Random def transform_password(password_str): """Transform the password string into 32 bit MD5 hash :param password_str: <str> password in plain text; :return: <str> Transformed password fixed length """ h = MD5.new() h.update(key.encode()) return h.hexdigest() def symmetric_encrypt(message, key, verbose = True): """Encripts the message using symmetric AES algorythm. :param message: <str> Message for encryption; :param key: <object> symmetric key; :return: <object> Message encrypted with key """ key_MD5 = transform_password(key) #      32  message_hash = SHA256.new(message.encode()) message_with_hash = message.encode() + message_hash.hexdigest().encode() #     .      iv = Random.new().read(AES.block_size) cipher = AES.new(key_MD5, AES.MODE_CFB, iv) #     . AES.MODE_CFB -   ,      iv. https://www.dlitz.net/software/pycrypto/api/current/Crypto.Cipher.blockalgo-module.html#MODE_CFB encrypted_message = iv + cipher.encrypt(message_with_hash) #       .  ,       ,       . if verbose: print(f'Message was encrypted into: {encrypted_message.hex()}') return encrypted_message def symmetric_decrypt(encr_message, key): """Decripts the message using private_key and check it's hash :param encrypted_message: <object> Encrypted message :param key: <object> symmetric key; :return: <object> Message decripted with key """ key_MD5 = transform_password(key) #   ,      bsize = AES.block_size dsize = SHA256.digest_size*2 iv = Random.new().read(bsize) cipher = AES.new(key_MD5, AES.MODE_CFB, iv) decrypted_message_with_hesh = cipher.decrypt(encr_message)[bsize:] #     ,      decrypted_message = decrypted_message_with_hesh[:-dsize] #   ,      digest = SHA256.new(decrypted_message).hexdigest() #    .     ,     . if digest==decrypted_message_with_hesh[-dsize:].decode(): #      ,      ,   print(f"Success!\nEncrypted hash is {decrypted_message_with_hesh[-dsize:].decode()}\nDecrypted hash is {digest}") return decrypted_message.decode() else: print(f"Encryption was not correct: the hash of decripted message doesn't match with encrypted hash\nEncrypted hash is {decrypted_message_with_hesh[-dsize:]}\nDecrypted hash is {digest}") 

Vamos verificar o desempenho do código usando um exemplo
 message = """  120   120  / 120   10     30-5 (2)    4.0 """ key = 'Traveling through hyperspace ain't like dusting crops, farm boy.' encr_message = symmetric_encrypt(message, key, verbose = True) print('\n') print('DECRIPTION') decr_message = symmetric_decrypt(encr_message, key) print(decr_message) 

 Message was encrypted into: ed10e4c65358bb9e351c801c3b3200b21fa86a24021c317bb5c9d8b3f76bdf9f3a7d26781a22402f0e4f41ca831b6d2da9e1e6878c34c79ddc7959af3ae9fc2ba0cfff1c0180a7e0f637f1aa5b24507d552d5dfe7625e7b81d817b5882b2b19bb95f3988a03c78f850098dfc8e6089863deaa39b887eaea4c1d4ba006edaec90205d54b27ed4ac70ed75cdd01732e1176bf04218beb8ae742ff708a201a9d1cb57dd5f2e70dc3239208d23705f7a3aae3e315c4df6d73c871b66c4995cce5f19738f731cd58755d21ed92612c44197f875cddf3f7aa1d60e435ce1492679b9d60c4b8538f52408f321711ac1d2daa6dbbc33dc655abca10e2f5fd3ff27823995b9dcdb62c0bafc1963ab539ccb466f1c140479df34b0005f578f72fcdd76b17391332037b801f74f733a08 DECRIPTION Success! Encrypted hash is b0dbb35b28fbff258350a50c39282b73e31f408c9da937c81d8d48115b491026 Decrypted hash is b0dbb35b28fbff258350a50c39282b73e31f408c9da937c81d8d48115b491026  120   120  / 120   10     30-5 (2)    4.0 


Como você pode ver, criptografamos a mensagem com sucesso usando a chave pública.
Execute o código várias vezes. Você verá que toda vez que a parte criptografada é alterada. Isso ocorre porque, ao criptografar, usamos o modo Cipher FeedBack (AES.MODECFB), no qual os blocos de texto sem formatação são misturados com os blocos de texto com criptografia. iv - vetor de inicialização (leia mais sobre o modo aqui ). Ao descriptografar a mensagem, vemos que o hash da mensagem descriptografada corresponde ao hash adicionado durante a criptografia. Isso significa que a descriptografia estava correta.

O que é um hash
Um hash de documento é simplesmente uma sequência de caracteres que é exclusiva para qualquer conjunto de dados. Com qualquer alteração de dados, o hash muda muito. Em outras palavras, um hash é um tipo de "impressão digital" para qualquer conjunto de dados.



Mas e se as chaves de criptografia estiverem comprometidas por algum motivo? Então qualquer um pode descriptografar a informação.

Nesse caso, precisamos alterar de alguma forma as chaves de criptografia remotamente a uma frequência que pode ser tocada pelo adversário. Vamos assumir que eles já a estão ouvindo. Então, como fazemos isso? Aqui outro método vem ao resgate, chamado criptografia assimétrica (ou um sistema criptográfico de chave pública). Ao contrário da criptografia simétrica, ele usa duas chaves: pública e privada. A mensagem é criptografada com a chave pública, após a qual só pode ser descriptografada com a chave privada. A chave pública de descriptografia será inútil. No entanto, há um ponto importante: a chave privada certamente deve ser do par público gerado. A presença de uma chave pública é uma das várias propriedades importantes e interessantes da criptografia assimétrica. Ou seja, podemos transmitir a chave pública por qualquer canal e não ter medo de que ela seja usada para descriptografar a mensagem.

Ao mesmo tempo, em relação à nossa tarefa, há uma nuance - a criptografia assimétrica é adequada para pequenos dados, por exemplo, mensagens curtas. Só podemos adivinhar a quantidade de dados obtidos pela inteligência. Obviamente, podemos dividir todos os dados recebidos em pequenos fragmentos e codificar cada um deles com uma chave privada, mas existe uma solução melhor. (O problema está resolvido aqui ).

Solução

O usuário Akela_wolf observou com razão que qualquer um pode gerar e enviar a chave pública. Fiz alguns ajustes no plano.

Será correto se, antes de enviar agentes, a matriz gerar vários pares de chaves e atribuir uma chave privada a cada agente. É melhor gerar apenas alguns pares para que cada agente tenha uma chave individual. Isso é necessário para personificar com precisão o proprietário da chave.
Então, se as chaves estiverem comprometidas, o centro criará uma nova chave SIMÉTRICA, a codificará para cada agente com chaves públicas e a enviará pelo canal aberto.
Esquema de solução geral






Decisão antiga
  1. O agente gerará um par de chaves (público e privado) e depois enviará a chave pública para as forças rebeldes;
  2. Na sede da resistência, eles criarão uma nova chave para criptografia SIMÉTRICA;
  3. A chave simétrica é codificada usando a chave pública que o agente enviou;
  4. A chave simétrica criptografada será enviada ao agente, que a decodificará usando a chave privada.

Preste atenção ao nosso programa no canal aberto:

  1. O agente envia a chave ABRIR do par, a chave FECHADA está em sua posse;
  2. O quartel-general da resistência envia uma chave de criptografia simétrica, criptografada com a chave pública enviada pelo agente.


Nem a primeira nem a segunda mensagem têm valor na interceptação.


Vamos escrever o código:

 #          RSA. #        SHA256 from Crypto.PublicKey import RSA #      def generate_keys(bits = 2048): """Generates the pair of private and public keys. :param bits: <int> Key length, or size (in bits) of the RSA modulus (default 2048) :return: <object> private_key, <object> public_key """ private_key = RSA.generate(bits) public_key = private_key.publickey() return private_key, public_key private_key, public_key = generate_keys(bits = 2048) 

Vamos ver como são as teclas.
 print(private_key.exportKey(format='PEM').decode()) print('\n') print('#'*65) print('\n') print(public_key.exportKey(format='PEM').decode()) 

-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA4JDLu7Vtvg2yqbH6Y0eJPfoEsOlKzgmOodqhA1CqkEG4OpKi
sGW7ciGP4v37GE6edHBCEy4UNkVQtnpPBjzTHvKd1pO70B84vD5OSrS7uNw2EYkj
d/ZwhrJMrcQKRwPkkM4OiewaaAaK0vPWJIKwlW61DY9X7LfNz7aOKMTbKnm1vdR0
919AV98FUmNoQBgka6nXFGmNbi7D43MtLwxBZIXfFupEiANSvOs+57hgaCho7OWM
GUOjLkG6HBscPhJ2W1H5DU9GjwL24ynTvKifgo1/2ue61MV1Pzh5CVaicJKNaRtg
Pd99gFhBGINsXV2X6Jh/W5nNsCddU4EI0AlO8wIDAQABAoIBAARM4YnjrIlSK9Sy
EtBp40frjMFyhjsx1ahlzmWI2utORt/gRPtJx3AlEmNPZ8qMXt5t8+X4IOz1INmN
uAuviH90N++O/q66mlSIgOlPUrT0ipiFXseCUZ9StMMzGNtJSMw5FfAwNEU/stLd
VoF2ezkxWIg88XsX/fn3Tfub4XKLvu4raJGcJ+Fo2GI9hYEGKnHhSuHvDHekTLlQ
z46O+cIwtehbFGcKesyK3zDD1uP5YLPIWpiqt1TgKjJzRF0l4ZJLk+RT7kU2pGIQ
mosOnr+06WyMIg724yQyAIwtS9X0czKBGUESrtTTb1HCXLeTwnncOTxh6q2z42LF
tn34+DECgYEA6EEp4oTvjfTQfUQPMByuAjF1hpdFHQqRymygiFgoF+Mg3QmL0w8j
/84H/q7s8FSx+3th8MK87bFq4lrry+h/mYwmvF5zZbhxcnl2uaX+KUPgpT6TgvAo
WOv2wc4BSaoo9DrxrZId86vpO2qbopw6gkBsvw47HSoQ+FSqXtZ0p8kCgYEA94Zj
b1ulctUjybiszO93TAjkzx3lU3yL+B1eZiQXtJa3mgG+ka1R/uMfr0NlT+Jzo0My
wHV30YRJDxziCrDol9OgSSU0sXwEcUxUIBLBwXLCp1EmMsYG9PB/x4OTWve35a8F
O+rMxuvWaZeIOfVCfL8UEcWweYaVdWIonJN+ltsCgYEAjeSZ2UlMLZce9RjqioNL
EA31dlfeoqJ9dYUuAn6RaB6cSk51vWlnnfXazo9CNIYaAsFbkcL3t+QHn+jaXEZc
BowocjbmG4Q20zBAB6XRBJbynSIA7yMYE1N9+uOHx+CMisGkO12krOUfZex4zzzR
RhhkF8ly9htoKL9ZIv20YXkCgYBzH3UF6PkVZJ5lhtgP5Nx2Z7iLwBrV7ppnBrnO
BcFkw6iXH3KT7KmzQ82LxWvMcMVZzLpBGyFkOAOG3OchE9DKNKpa+sv8NHMYguip
li+5mneAPFTozoOTznuPvtl9OLO2RuXHTVh6uFub9tdsJW8L+A8MiQagLwE6fDHp
SQxaewKBgQDIyzL1THpW3+AMNrOZuI/d3Em5wpGJiZbDSBRosvsfGm/sHaz4Ik5E
nWnftgktmsAD60eORTTh9/ww/nm7f3q9kzT8Sv1MmqeRXq9VFIOeP/+8SSE/7LzD
izlb5xEtVD8LuY54jHyiOxiZC++TQswMnOKKi0Gx26MDoO7Tx9akVw==
-----END RSA PRIVATE KEY-----

#################################################################

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4JDLu7Vtvg2yqbH6Y0eJ
PfoEsOlKzgmOodqhA1CqkEG4OpKisGW7ciGP4v37GE6edHBCEy4UNkVQtnpPBjzT
HvKd1pO70B84vD5OSrS7uNw2EYkjd/ZwhrJMrcQKRwPkkM4OiewaaAaK0vPWJIKw
lW61DY9X7LfNz7aOKMTbKnm1vdR0919AV98FUmNoQBgka6nXFGmNbi7D43MtLwxB
ZIXfFupEiANSvOs+57hgaCho7OWMGUOjLkG6HBscPhJ2W1H5DU9GjwL24ynTvKif
go1/2ue61MV1Pzh5CVaicJKNaRtgPd99gFhBGINsXV2X6Jh/W5nNsCddU4EI0AlO
8wIDAQAB
-----END PUBLIC KEY-----

Como você pode ver, as chaves de criptografia assimétricas são longas sequências de caracteres geradas matematicamente.

Então, geramos as chaves. Agora vamos escrever uma função para codificar dados:

 from Crypto.PublicKey import RSA #        RSA. from Crypto.Hash import SHA256 #        SHA256.    #       from Crypto.Cipher import PKCS1_OAEP #   def encrypt_message(message, public_key, verbose = True): """Encripts the message using public_key. :param message: <str> Message for encryption :param public_key: <object> public_key :param verbose: <bool> Print description; :return: <object> Message encrypted with public_key """ message_hash = SHA256.new(message.encode()) #   . cipher = PKCS1_OAEP.new(public_key) message_with_hash = message.encode() + message_hash.hexdigest().encode() #      ,          encrypted_message = cipher.encrypt(message_with_hash) if verbose: print(f'Message: {message} was encrypted to\n{encrypted_message.hex()}') return encrypted_message def decrypt_message(encrypted_message, private_key): """Decripts the message using private_key and check it's hash :param encrypted_message: <object> Encrypted message :param private_key: <object> private_key :return: <object> Message decripted with private_key """ dsize = SHA256.digest_size*2 cipher = PKCS1_OAEP.new(private_key) decrypted_message_with_hesh = cipher.decrypt(encrypted_message) #   (  ) decrypted_message = decrypted_message_with_hesh[:-dsize] #      digest = SHA256.new(decrypted_message).hexdigest() #     if digest==decrypted_message_with_hesh[-dsize:].decode(): #      ,      ,   print(f"Success!\nEncrypted hash is {decrypted_message_with_hesh[-dsize:].decode()}\nDecrypted hash is {digest}") return decrypted_message.decode() else: print(f"Encryption was not correct: the hash of decripted message doesn't match with encrypted hash\nEncrypted hash is {decrypted_message_with_hesh[-dsize:]}\nDecrypted hash is {digest}") 

Etapas do fluxo de trabalho
  1. O agente gera um par de chaves:
     private_key, public_key = generate_keys() 

  2. Envia a chave OPEN para a sede;
  3. Na sede, usando a chave pública, codifique a chave para criptografia simétrica:
     new_symmetric_key = 'SOME_KEY_asdfasdfasdfasdfsdfgrtwhetynt' encr_msg = encrypt_message(new_symmetric_key, public_key) 

    Conclusão
    Message: SOME_KEY_asdfasdfasdfasdfsdfgrtwhetynt was encrypted to
    41e940507c96397e3feb4a53390c982633bb1775a52957996a8069bd22063086a0e831bf775a17909276aba0d0478ee6c953837c8ea5d20d40e1c8eb463aaa1bc5c93c71677b1a85e90439c9dbda8a98ce168acb38368155437c66815b84aa2fbdda0eb909e4e6079b4410c720eddd955ed048193bf87f8f9976a17ee32a58a71dfddf3db116343d949d29c25f72c511a440a50a5d4f1e01c37b24a1cb4127e191d3231328b2f120c7dbd0cb5bf19823f0978b8ed17d25952de4b146ef9724fff359eb2af503fdfd72b91525a5503b076ba9aaaeac55af3f8d210c12d579d45dd70362123c0b4b36ef9c2f7705e6f884a25553eb0e11e5077f11fa986d0ff280


  4. Essa longa sequência é enviada de volta ao agente;
  5. O agente descriptografa a mensagem recebida usando a chave privada:
     recieved_symmetric_key = decrypt_message(encr_msg, private_key) print('\n') print(f"New symmetric key is: {recieved_symmetric_key}") 

    Conclusão
     Success! Encrypted hash is 42ad66445a05ac09e684bb21f9b487d95b9cfa11d02e0b459931321ee02f7c1c Decrypted hash is 42ad66445a05ac09e684bb21f9b487d95b9cfa11d02e0b459931321ee02f7c1c New symmetric key is: SOME_KEY_asdfasdfasdfasdfsdfgrtwhetynt 


  6. Em seguida, usando uma nova chave simétrica, o agente criptografa os dados recebidos:

     message = """  120   120  / 120   10     30-5 (2)    4.0 """ encr_message = symmetric_encrypt(message, recieved_symmetric_key, verbose = True) 

    Conclusão
     Message was encrypted into: 


  7. A sede descriptografa:
     print('DECRIPTION') decr_message = symmetric_decrypt(encr_message, new_symmetric_key) print(decr_message) 

    Conclusão
     DECRIPTION Success! Encrypted hash is b0dbb35b28fbff258350a50c39282b73e31f408c9da937c81d8d48115b491026 Decrypted hash is b0dbb35b28fbff258350a50c39282b73e31f408c9da937c81d8d48115b491026  120   120  / 120   10     30-5 (2)    4.0 




Voila!

Neste exemplo abstrato, vimos o trabalho de algoritmos de criptografia comuns. Criptografia simétrica e assimétrica, bem como hash, são usadas no trabalho da web , assinatura eletrônica , blockchain e criptomoedas . Espero que o material tenha sido útil para entender o funcionamento dessas tecnologias.

Posfácio


Como resultado, a inteligência rebelde conseguiu obter informações precisas sobre a vulnerabilidade da estação e seu caminho, a presença do Imperador para inspeção, a presença de um escudo de energia e sua fonte em Endor. O império avistou os espiões, os informou mal da capacidade de combate da estação. A estação também foi atribuída ao satélite de Endor, de onde estava protegida por um escudo.

Mas sabemos como tudo terminou;)

Source: https://habr.com/ru/post/pt452042/


All Articles