12

Intro à criptografia: simétrica vs. assimétrica, RSA, e o porquê de números primos serem importantes [SEGURANÇA DA INFORMAÇÃO]

Fala, pessoal, tudo bem? Hoje vamos entender a diferença entre criptografia simétrica e assimétrica, entender criptografia RSA, que é o sistema assimétrico mais famoso hoje em dia, e ao fim, vamos entender a importância de números primos. Creio que o público-alvo já deve ter pelo menos uma noção do que é criptografia e para o que serve, então não vou me ater a esses floreios, vamos direto para o que importa aqui.

Vou começar explicando a cifra de César, um sistema criptográfico muito simples. Assim como o 'Hello, world!' está para a programação, a cifra de César está para a área de criptografia, então nós vamos usá-lo apenas como um pretexto para explicar simetria, e depois assimetria.

Cifra de César

A cifra de César é um tipo de criptografia de substituição, que consiste basicamente em uma troca de letras como vemos abaixo:

abcdefghijklm
defghijklmnop
nopqrstuvwxyz
qrstuvwxyzabc

Analisando a tabela, você consegue deduzir como a encriptação é feita?

Exatamente, a encriptação será simplesmente pegar a letra que deseja criptografar e ver qual é a 3º letra à sua frente:
C(g)=kC(g) = k
C(y)=bC(y) = b (se não há mais letras à frente, voltamos para o início do alfabeto)

E a decriptação será o processo inverso da encriptação: pegar a letra criptografada e ver qual é a 3º letra que a precede.
C(g)=k=>D(C(g))=D(k)=>G=D(k)C(g) = k => D(C(g)) = D(k) => G = D(k)

A cifra de César é um tipo muito fraco de criptografia. Por exemplo, se fizermos uma criptoanálise de frequência de letras, nós conseguimos inferir qual letra encriptada corresponde à original, e logo conseguimos pegar o esquema.

Porém há outro fator além deste que pode torná-la insegura para envio de dados, que é o fato de ser simétrica. Abaixo vamos entender o que significa ser simétrica, e porque isso pode ser um problema dependendo do contexto.

Criptografia simétrica vs. assimétrica

Na cifra de César, tanto o emissor quanto o receptor precisam simultaneamente ter ciência de quantas posições devem se deslocar no alfabeto para encriptar/decriptar a informação. Este segredo de quantas posições deslocar, nós vamos chamar de chave (há controvérsias se o tamanho do deslocamento na cifra de César é uma chave, pois é intrínseco no método que o deslocamento é sempre 3, mas para a explicação, vamos considerar o tamanho do deslocamento como uma chave).

Note que ambos os envolvidos na comunicação precisam ter em posse a mesma chave para estabelecer uma comunicação. Note também que esta chave serve tanto para encriptar quanto para decriptar uma mensagem. Então mesmo que por ventura uma parte deseje só criptografar para enviar à outra parte, ela ainda assim precisará de uma chave que desempenha as duas funções.

Quando ambos os envolvidos na comunicação são capazes de criptografar e descriptografar usando a mesma chave para isto, nós chamamos a chave de chave única, e dizemos que a criptografia é simétrica. A chave é única justamente porque ela é usada tanto na hora de criptografar quanto na hora de descriptografar.

Você pode levantar o seguinte questionamento: "E se dissermos ao receptor somente a informação que ele precisa para descriptografar, que é regredir 3 posições para achar a letra original? Assim não falaríamos a ele qual é a regra de encriptação."

Pois, bem, note que anteriormente eu coloquei "encriptar/decriptar" junto, pois no fim das contas, a regra de ambas as funções é a mesma coisa, já que sabendo uma delas, conseguimos chegar imediatamente na outra:

  1. \Rightarrow Se para descriptografar, eu preciso regredir 3 posições, é lógico que para criptografar, eu preciso progredir 3 posições.
  2. \Leftarrow Se para criptografar, eu preciso progredir 3 posições, é lógico que para descriptografar, eu preciso regredir 3 posições.
  3. \Leftrightarrow Logo, eu descriptografo regredindo 3 posições se, e somente se, eu criptografo progredindo 3 posições.

Em alguns casos em que os canais de comunicação são poucos, e temos garantia que são confiáveis, pode ser interessante usar uma criptografia simétrica, considerando também que elas são bem mais rápidas na hora de serem geradas quando comparadas com as assimétricas. Porém na grande maioria dos casos é um problema usar chaves únicas.

Imagine um banco que possui vários clientes, e sempre que esses clientes precisam fazer uma transferência bancária, eles precisam enviar informações sigilosas pela rede. Seria insustentável uma criptografia simétrica nesse caso, pois se um monte de pessoas tem uma chave que encripta/decripta, basta uma pessoa maliciosa interceptar uma mensagem criptografada, e usar a própria chave dada a ele pelo banco para descriptografá-la.

Como o banco pode consertar esta falha?

Se o banco quiser continuar insistindo em chaves únicas, há várias maneiras de dificultar o trabalho do hacker, como por exemplo, criar chaves diferentes para cada usuário. Porém estas maneiras seriam muito mais uma solução paliativa do que uma solução de fato. Devemos considerar também que os métodos de criptografia simétrica hoje em dia são razoavelmente fáceis de serem burlados usando criptoanálise.

Já que os clientes apenas enviarão informações ao banco, a melhor opção então seria dar um jeito de delegar a eles somente a função de criptografar, e restringir ao banco a função de descriptografar os dados, concorda?

Pois bem, é justamente essa a ideia de uma criptografia assimétrica: você criar uma assimetria na comunicação onde somente uma parte tem poder de decriptar, e a outra parte pode apenas encriptar os dados e enviar para a parte capaz de decriptar.

Logo ao invés de haver uma chave única capaz de encriptar/decriptar, na criptografia assimétrica há duas chaves:

  1. chave pública, que é capaz apenas de encriptar.
  2. chave privada, que é capaz apenas de decriptar.

Vamos agora entender como funciona um método assimétrico.

Criptografia RSA

Apesar de ser necessário um conhecimento matemático para entender a criptografia RSA, a maioria dos conhecimentos serão tranquilos e intuitivos, como por exemplo, saber o que é máximo divisor comum (MDC), números primos, função de Euler, etc. Caso você não conheça algum desses assuntos, eu vou me esforçar ao máximo para explicá-los conforme eles forem demandados durante a explicação.

Primeiro passo é escolhermos dois números primos pp e qq com no mínimo do mínimo 150 casas decimais e guardá-los em segredo. A segurança do método está intimamente ligada à magnitude dos números primos, por isso devemos escolher números muito grandes.

Números Primos

Números primos são números que são divisíveis somente por 1 e por ele mesmo.

Exemplo:
2 é primo, pois só é divisível por 1 e por 2 (ele mesmo).
7 é primo, pois só é divisível por 1 e por 7.
6 não é primo, pois é divisível por 1, por 2, por 3 e por 6.

Os primos também precisam estarem distantes um do outro. Caso eles estejam muito próximos, conseguimos descobri-los por meio de um método de tentativa e erro.

Após isso, vamos multiplicar pp e qq para criar um número nn que só é divísivel por pp e qq:
n=pqn = p \cdot q

Agora devemos escolher um número natural ee tal que mdc(e,φ(n))=1mdc(e, φ(n)) = 1
Onde φ(n)φ(n) é a função de Euler. Ela também deve ser guardada em segredo.

Máximo Divisor Comum (MDC)

Como o próprio nome já diz, o mdcmdc de dois números inteiros é o maior divisor capaz de dividir ambos simultaneamente.

Exemplo:
12=22312 = 2^2\cdot3
10=2510 = 2\cdot5
Logo mdc(12,10)=2mdc(12, 10) = 2, já que o fator máximo que ambos compartilham é 212^1.

Outro exemplo:
154=2711154 = 2\cdot7\cdot11
660 = 2235112^2\cdot3\cdot5\cdot11
Logo mdc(154,660)=211=22mdc(154, 660) = 2\cdot11 = 22, já que ambos têm simultaneamente 212^1 e 11111^1 como fatores.

Função φ(n)φ(n) de Euler

φ(n)φ(n) nos informa a quantidade de números inteiros positivos znz \leq n, tal que mdc(z,n)=1mdc(z, n) = 1, isto é, tal que zz e nn são primos relativos entre si.

Exemplo:
φ(12)=4φ(12) = 4, pois há 4 números menores ou iguais a 1212 que são primos relativos a 12:
mdc(1,12)=1mdc(1, 12) = 1
mdc(2,12)=2mdc(2, 12) = 2
mdc(3,12)=3mdc(3, 12) = 3
mdc(4,12)=4mdc(4, 12) = 4
mdc(5,12)=1mdc(5, 12) = 1
mdc(6,12)=6mdc(6, 12) = 6
mdc(7,12)=1mdc(7, 12) = 1
mdc(8,12)=4mdc(8, 12) = 4
mdc(9,12)=3mdc(9, 12) = 3
mdc(10,12)=2mdc(10, 12) = 2
mdc(11,12)=1mdc(11, 12) = 1
mdc(12,12)=12mdc(12, 12) = 12

Não precisamos testar um por um, há uma fórmula para calcular φ(n)φ(n) de forma analítica:

  1. Decompor nn em seus fatores primos.
  2. Calcular φ(p)φ(p) para cada primo usando a fórmula φ(pa)=papa1φ(p^a) = p^a - p^{a-1}
  3. Multiplicar todos os φ(p)φ(p)

12=22312 = 2^2\cdot3
φ(22)=2221=2φ(2^2) = 2^2-2^1 = 2
φ(3)=3130=31=2φ(3) = 3^1 - 3^0 = 3 - 1 = 2
logo φ(12)=φ(22)φ(3)=22=4φ(12) = φ(2^2)\cdotφ(3) = 2\cdot2 = 4

Encriptação

A chave pública consistirá de:

  • o número nn
  • o número ee

A chave pública será usada por quem quiser. Vamos ver a seguir como usar a chave pública para criptografar um bloco bb de dados.

Primeiro devemos pegar um bloco bb onde bb é um número entre   1  \;1\; e   n1\;n-1:
1<b<n11 < b < n - 1
Caso contrário, a encriptação não dará certa. Lembrando que qualquer informação pode ser representada como um número inteiro em um computador.

C(b)=C(b) = resto da divisão de beb^e por nn

Alguns detalhes importantes:
Se e=1,  bee = 1,\;b^e = b1b^1
Como C(b)C(b) é igual ao resto da divisão de beb^e por nn,
E como b<nb < n, o resto de b1b^1 por nn é igual bb
Logo C(b)=bC(b) = b, o que significa que não criptografamos bb

1<e<φ(n)1 < e < φ(n)

Em termos de pseudocódigo, poderíamos expressar C(b)C(b) da seguinte forma:
bloco_cript = exponencial_modular(base = b, expoente = e, mod = n)

Decriptação

A chave privada consistirá de:

  • o número nn
  • um inverso positivo de ee módulo φ(n)φ(n). Chamaremos este número de " dd "

nn é usada tanto na chave pública quanto na privada, então ela é uma informação pública. Já dd deve ser guardada em segredo. E por isso a importância de guardar φ(n)φ(n) em segredo também, pois se não fosse, poderíamos usá-la em conjunto com ee, que é uma informação pública, para descobrir dd. Sem contar que a partir de apenas φ(n)φ(n), nós conseguimos usar um método para descobrir os números primos pp e qq escolhidos.

Inverso de um número módulo mm

O inverso de um número inteiro xx módulo mm é o número inteiro ii que multiplicado por xx fica congruente a 11 módulo mm, isto é, o resultado da multiplicação entre xx e ii deixa resto 11 na divisão por mm:

Um número xx só tem inverso módulo mm se, e somente se, mdc(x,m)=1mdc(x, m) = 1 (isto é, se xx e mm forem primos relativos entre si)

Exemplo:
13i1(mod  17)  ;  mdc(13,17)=113 \cdot i \equiv 1 (mod\;17)\;;\;mdc(13, 17) = 1, então existe inverso ii
Usando algoritmo de Euclides para descobrir ii:
17=131+44=1713117 = 13\cdot1 + 4 \Rightarrow 4 = 17 - 13\cdot1
13=43+11=134313 = 4\cdot3 + 1 \Rightarrow 1 = 13 - 4\cdot3
1=13(17131)3=13173+1331 = 13 - (17 - 13\cdot1)\cdot3 = 13 - 17\cdot3 + 13\cdot3
1=134+17(3)=5251=1\Rightarrow 1 = 13\cdot4 + 17(-3) = 52 - 51 = 1
Logo 44 é o inverso de 1313 módulo 1717

dd então é um número positivo que quando multiplicado por ee, o resultado dessa multiplicação é congruente a 11 módulo φ(n)φ(n):
ed1  (mod  φ(n))ed \equiv 1\;(mod\;φ(n))

A chave privada será usada somente por nós. Vamos ver a seguir como usar a chave privada para descriptografar um bloco bb de dados criptografados.

Vamos pegar um bloco bb criptografado, isto é, pegar C(b)C(b), e aplicar a decriptação.

D(C(b))D(C(b)) = resto da divisão de [C(b)]d[C(b)]^d por nn

Prova

Se C(b)=C(b) = resto da divisão de beb^e por nn
Isto significa que C(b)be  (mod  n)C(b) \equiv b^e\;(mod\;n)

Se D(C(b))D(C(b)) = resto da divisão de [C(b)]d[C(b)]^d por nn
Isto significa que D(C(b))[C(b)]d  (mod  n)D(C(b)) \equiv [C(b)]^d\;(mod\;n)

A notação " \equiv " significa "congruente a".
Um número xx é congruente a yy módulo mm se, e somente se, eles deixam o mesmo resto de divisão quando divididos por mm.

Exemplo:
1010 é congruente a 3434 módulo 88, pois o resto da divisão por 88 de ambos vale 22.
10÷8=18+210\div8 = 1\cdot8 + 2
34÷8=48+234\div8 = 4\cdot8 + 2

Como C(b)be,  D(C(b))(be)d  (mod  n)C(b) \equiv b^e, \;D(C(b)) \equiv (b^e)^d\;(mod\;n)

**Não confunda: C(b)C(b) é congruente a beb^e, ele NÃO É igual a beb^e

ed1  (mod  φ(n))ed \equiv 1\;(mod\;φ(n)) implica em ed1=φ(n)ked=φ(n)k+1  ;    kZ  ed - 1 = φ(n)\cdot k \Rightarrow ed = φ(n)k + 1\;;\;\; k \in\mathbb{Z}\;

Se xy  (mod  m)x \equiv y \; (mod\;m), então xx e yy deixam o mesmo resto em uma divisão por mm:
x=mk+rx = m\cdot k + r
y=mk+ry = m\cdot k' + r

Sendo assim xy=mk+r(mk+r)=mk+rmkr=m(kk)=mkx - y = mk + r - (mk' + r) = mk + \cancel{r} - mk' - \cancel{r} = m(k - k') = mk''

Logo   xy=mk\;x - y = mk''
(este " kk'' " é um número inteiro que multiplicado por mm, resulta em xyx - y)

Sabemos que φ(n)=(p1)(q1)  φ(n) = (p-1)(q-1)\;, logo   ed=(p1)(q1)k+1\;ed = (p-1)(q-1)k + 1

Sabemos também que D(C(b))bed  (mod  n)D(C(b)) \equiv b^{ed}\;(mod\;n)

Então D(C(b))b(p1)(q1)k+1=bb(p1)(q1)k  (mod  n)D(C(b)) \equiv b^{(p-1)(q-1)k + 1} = b\cdot b^{(p-1)(q-1)k}\;(mod \;n)

Agora basta nós analisarmos algumas situações para simplificarmos a expressão acima:

Sobre os símbolos que aparecerão abaixo:

  • " \mid " significa “divide”
  • " \nmid " significa "não divide"
  • Se pbp \nmid b:
    • Pelo pequeno teorema de Fermat, temos que b(p1)1  (mod  p)b^{(p - 1)} \equiv 1\;(mod\;p)
      Então b(b(p1))(q1)kb(1)(q1)k=b  (mod  p)b\cdot(b^{(p - 1)})^{(q - 1)k} ≡ b\cdot(1)^{(q - 1)k} = b\;(mod\;p)

      Teorema de Fermat: se pp é um número primo e pap \nmid a, então ap11  (mod  p)a^{p−1} \equiv 1\;(mod\;p).

  • Se pb:p \mid b:
    • b0  (mod  p)bed0ed  (mod  p)b \equiv 0\;(mod\;p) \Rightarrow b^{ed} \equiv 0^{ed}\;(mod\;p)

      Sejam mm e xx números inteiros. Se mxm \mid x, então  x0  (mod  m)\;x \equiv 0\;(mod\;m)

      Se b0  (mod  p)  b \equiv 0 \;(mod\;p)\; e   bed0  (mod  p)\;b^{ed} \equiv 0\;(mod\;p)
      Entao bedb  (mod  p)b^{ed} ≡ b \;(mod\;p)

Analogamente, o mesmo vale para o primo qq:   bedb  (mod  q)\;b^{ed} \equiv b\;(mod\;q)

Pelas expressões modulares, sabemos que:

  • bedb=pk  b^{ed} - b = p\cdot k\;, portanto pbedbp \mid b^{ed} - b
  • bedb=qk  b^{ed} - b = q\cdot k\;, portanto qbedbq \mid b^{ed} - b

Já que pp e qq dividem   bedb  \;b^{ed} - b\;,   \;e mdc(p,q)=1mdc(p, q) = 1 - pois são dois números primos -, nós podemos afirmar que:

  • pqbedbbedb  (mod  pq)p\cdot q \mid b^{ed} - b \Rightarrow b^{ed} \equiv b \;(mod\;pq)

n=pqn = pq

Como D(C(b))bed  (mod  n)  D(C(b)) ≡ b^{ed}\;(mod\;n)\; e   bedb  (mod  n)\;b^{ed} \equiv b \;(mod\;n)

D(C(b))b  (mod  n)D(C(b)) ≡ b\;(mod\;n)
D(C(b))\Rightarrow D(C(b)) = resto da divisão de [C(b)]d[C(b)]^d por n        n\;\;\;\;\blacksquare

Em termos de pseudocódigo, poderíamos expressar D(C(b))D(C(b)) da seguinte forma:
bloco_decript = exponencial_modular(base = bloco_cript, expoente = d, mod = n)

Agora vocês conseguem compreender a importância dos números primos? Toda a criptografia RSA está baseada no fato de não ser possível encontrar os divisores de um número de maneira analítica. A única forma é por meio da tentativa e erro.
Então apesar de nn ser um número conhecido por qualquer um, a única forma de descobrir seus divisores pp e qq, é tentando dividi-lo por uma quantidade avassaladora de possibilidades.

Porém, se por um acaso conseguirmos descobrir um divisor de nn \rightarrow automaticamente temos o outro divisor \rightarrow então tendo os dois divisores de nn, isto é, os primos pp e qq, conseguimos calcular φ(n)φ(n) \rightarrow tendo φ(n)φ(n), conseguimos calcular dd \rightarrow e tendo dd, conseguimos decriptar o número.

Vamos agora a um exemplo prático:

**Visando praticidade, nós vamos usar apenas números de pequena magnitude

Escolhendo os primos:

p=11p = 11 e q=13q = 13

Calculando nn:

n=1113=143n = 11\cdot13 = 143

Calculando φ(n)φ(n):

φ(143)=(111)(131)=120φ(143) = (11 - 1)\cdot(13 - 1) = 120

Escolhendo ee tal que mdc(e,120)=1mdc(e, 120) = 1:

e=7e = 7

Você pode conferir que de fato mdc(7,120)=1mdc(7, 120) = 1

Calculando dd:

ed1  (mod  φ(n))7d1  (mod  120)e \cdot d \equiv 1\;(mod\;φ(n)) \Rightarrow 7 \cdot d \equiv 1\;(mod\;120)

Usando algoritmo de Euclides para descobrir dd:
120=717+1120 = 7\cdot17 + 1
1201+7(17)=1\Rightarrow 120\cdot1 + 7(-17) = 1
120(17)+7(17+120)=1\Rightarrow 120(1-7) + 7(-17 + 120) = 1 (Se não entendeu este passo, não se preocupe)
120(6)+7103=1\Rightarrow 120(-6) + 7\cdot103 = 1
d=103d = 103

ppqqnnφφeedd
11131431207103

Aplicando a criptografia em um bloco bb de dados qualquer

b=54    (1<54<1431)b = 54\;\;(1 < 54 < 143 - 1)

be=547b^e = 54^7

resto da divisão de 54754^7 por 143143:   r=76\;r = 76

C(b)=76C(b) = 76

Aplicando a descriptografia no bloco bb criptografado

[C(b)]d=76103[C(b)]^d = 76^{103}

resto da divisão de 7610376^{103} por 143143:   r=54\;r = 54

D(C(b))=54=bD(C(b)) = 54 = b

Resultado

Isso é tudo, pessoal! Obrigado a todos que leram até aqui. Espero ter sido claro em minhas explicações.

Os conceitos matemáticos que usei aqui, como módulo, algoritmo de Euclides, etc, são vistos em uma disciplina chamada "Teoria dos Números". Caso queira se aprofundar, recomendo o livro do IMPA: Teoria dos Números - um passeio com primos e outros números familiares pelo mundo inteiro

Caso tenha gostado deste material, considere dar uma lida neste meu outro post também: Como um computador soma números? Clique aqui e entenda! [CIRCUITOS DIGITAIS].

Carregando publicação patrocinada...
6

Muito bom, mas só um detalhe sobre o RSA. Foi dito que:

  1. chave pública, que é capaz apenas de encriptar.
  2. chave privada, que é capaz apenas de decriptar.

Na verdade, ambas as chaves servem para encriptar e decriptar (e o que é encriptado com uma, só pode ser decriptado por outra). O que muda é o propósito e a terminologia.

Quando usamos a chave pública para encriptar (que é o seu exemplo), quer dizer que somente quem possui a chave privada conseguirá decriptar. Isso garante que ninguém mais vai conseguir ler os dados.

Já o oposto não parece fazer sentido, afinal, se eu uso a chave privada para encriptar, qualquer um que possui a chave pública vai conseguir decriptar. Então pra que isso serviria? Bem, o propósito é de garantir a autenticidade dos dados. Por exemplo, se vc conseguiu usar a minha chave pública para decriptar, então os dados só podem ter sido encriptados pela minha chave privada. Ou seja, isso garante que fui eu quem encriptei aqueles dados (assumindo, claro, que ninguém teve acesso indevido à minha chave privada).

Essa é a ideia básica por trás da assinatura digital. A chave privada é usada para encriptar o hash de determinado conteúdo. Depois, qualquer um pode decriptar com a respectiva chave pública, garantindo que aquilo foi gerado pelo portador da chave privada (e calcula-se novamente o hash do conteúdo para verificar se é o mesmo - ou seja, garanto a autoria e integridade do conteúdo). Mais ainda, o dono da chave privada não pode alegar que não foi usada a chave dele (afinal, se conseguimos usar sua chave pública para decriptar, só pode ter sido encripado pela chave privada correspondente) - esta propriedade é chamada de "não-repúdio".

Neste caso, a terminologia diz que estamos assinando com a chave privada e verificando a assinatura com a chave pública. Por este motivo, a frase "encriptar com a chave privada" é considerada errada (embora tecnicamente esteja ocorrendo uma encriptação dos dados). Além disso, também é comum o uso de pares de chaves diferentes para encriptar e assinar.


Por fim, também vale mencionar que a criptografia assimétrica pode ser usada em conjunto com a simétrica. Por exemplo, o SSL/TLS faz isso: primeiro o cliente gera uma chave simétrica, que é encriptada com a chave pública do servidor e enviada para ele. O servidor decripta com sua chave privada, obtendo a chave simétrica, que é usada posteriormente para trocar mensagens com o cliente.

Inclusive, esta é uma forma de uso comum do RSA (transmitir uma chave simétrica), já que ele é considerado lento para encriptar/decriptar.

E como leitura adicional, seguem alguns links abaixo. Afinal, o assunto é amplo, eu mesmo reconheço que não sei nem 1% (e tudo que eu disse acima foi ultra-simplificado e tem vários poréns e detalhes que, apesar de saber que existem, ainda não conheço a fundo):

2
2
2
2
1

Muito obrigado! Que bom que foi útil para você. Agora vai ficar mais complicado de postar conteúdo, pois acabou as minhas férias, mas vou tentar trazer outros. Planejo falar sobre programação paralela no próximo.