Empresa 100% online (61) 9 8101-2703 contato@clickartweb.com.br

Números primos na criptografia: entenda o RSA com JS e PHP

abril 17, 2026
Rogério Rios

Neste artigo, eu explico de forma simples como os números primos entram na criptografia RSA e mostro um exemplo prático com JavaScript e PHP para você entender chave pública, chave privada e descriptografia.

Números primos na criptografia: entenda o RSA com JS e PHP

Se você já ouviu que números primos são importantes na criptografia, mas nunca entendeu direito o motivo, relaxa: você não está sozinho.

Durante muito tempo, isso também parecia abstrato demais para mim. Afinal, uma coisa é ouvir que “os primos são fundamentais para a segurança digital”. Outra, bem diferente, é entender como isso vira código de verdade, como o navegador participa disso e como o servidor consegue descriptografar a mensagem depois.

Foi justamente por isso que resolvi montar um exemplo simples, didático e sem firula. E, neste artigo, eu vou te mostrar exatamente isso. Depois de entendermos “criptografia RSA” vou mostrar como fazer na prática.

A ideia aqui não é complicar. Pelo contrário. Quero te explicar como os números primos na criptografia fazem sentido na prática, usando um exemplo com JavaScript no front-end e PHP no back-end.

Antes de tudo: o que são números primos?

De forma bem direta, número primo é aquele que só pode ser dividido por 1 e por ele mesmo.

Por exemplo: [2, 3, 5, 7, 11, 13, 17, 19, … ]

Curiosidade: Até a data de escrita deste post, o maior número primo conhecido foi descorberto em outubro de 2024, o maior número primo conhecido é um número primo de Mersenne com 41 024 320 algarismos.

Até aí, tudo bem. No entanto, a parte interessante começa quando esses números entram em um tipo específico de conta que é fácil de fazer, mas difícil de desfazer.

E é justamente aí que mora a mágica da criptografia RSA.

Por que os números primos são importantes na criptografia?

A lógica é simples.

Se eu pegar dois números primos e multiplicar, o resultado sai rápido.

Por exemplo:

  • 17 × 19 = 323

Agora, se eu te entregar apenas o número 323 e perguntar quais primos geraram esse valor, você até consegue descobrir. Só que já ficou mais difícil.

Com números pequenos, isso é tranquilo. Porém, quando estamos falando de números gigantescos, com centenas ou milhares de bits, descobrir os fatores primos se torna um problema matemático muito mais pesado.

Em outras palavras:

  • multiplicar dois primos é fácil
  • descobrir os primos a partir do resultado é difícil

E essa diferença de dificuldade é uma das bases do RSA, um dos algoritmos mais conhecidos da criptografia assimétrica.

O que é RSA?

O RSA é um sistema de criptografia que trabalha com duas chaves:

  • chave pública
  • chave privada

A chave pública pode ficar exposta. Sem problema.

Já a chave privada precisa ficar guardada no servidor.

Na prática, funciona mais ou menos assim:

  1. o navegador recebe ou já possui a chave pública;
  2. a mensagem é criptografada no front-end;
  3. os dados são enviados ao servidor;
  4. o servidor usa a chave privada para descriptografar.

Ou seja, uma pessoa consegue “trancar” a mensagem com a chave pública, mas só quem tem a chave privada consegue “abrir”.

As variáveis do RSA, traduzidas sem enrolação

Quando a gente começa a estudar RSA, aparecem algumas letras que assustam. Só que, na verdade, elas são bem mais simples do que parecem.

p = Primeiro número primo secreto.

q = Segundo número primo secreto.

n = Resultado da multiplicação de p × q.

φ(n) = Lê-se “fi de ene”. É um valor auxiliar usado para montar a relação matemática entre a chave pública e a privada.

Quando p e q são primos:

φ(n) = (p - 1)(q - 1)

e = Expoente público. Faz parte da chave pública.

d = Expoente privado. Faz parte da chave privada.

m = Mensagem original.

c = Mensagem criptografada.

Em resumo, fica assim:

  • p e q: primos secretos
  • n: produto dos dois
  • φ(n): valor interno de apoio
  • e: número público
  • d: número privado
  • m: mensagem
  • c: mensagem criptografada

Exemplo simples de RSA com números pequenos

Para não transformar este artigo em uma aula impossível de acompanhar, eu usei números pequenos só para fins didáticos.

Escolhi:

  • p = 17
  • q = 19

Então:

n = p × q = 323

Depois:

φ(n) = (17 - 1)(19 - 1) = 16 × 18 = 288

Agora escolhemos um valor público:

e = 5

A partir daí, precisamos descobrir d, que é o número que satisfaz esta condição:

e × d ≡ 1 mod φ(n)

Substituindo:

5 × d ≡ 1 mod 288

O valor correto é:

d = 173

Inclusive, esse ponto é importante. Em uma conta inicial, eu havia chegado a um valor errado. Depois, revisando a matemática, o valor certo ficou claro: 173.

E isso é bom porque mostra uma coisa importante: em criptografia, um detalhe errado derruba o sistema inteiro. Não existe “quase certo”.

Inclusive a forma correta de se chegar a d é usando o raciocínio “Aritmética do relógio”.

Nesse caso, imagine um relógio com 288 horas. Se você der x voltas de 5 horas cada, você quer parar exatamente na hora 1.

Como o 5 e o 288 são “primos entre si” (não compartilham divisores além do 1), sabemos que existe uma resposta única para x dentro desse intervalo.

O Método Prático

Para resolver isso, queremos que 5x seja igual a algum múltiplo de 288 mais 1. Ou seja: 5x = 288k + 1

Vamos testar valores para k (número de voltas no “relógio” de 288):

  • Se k = 1: 288 x 1 + 1 = 289.
    • 289 é divisível por 5? Não (termina em 9).
  • Se k = 2: 288 x 2 + 1 = 576 + 1 = 577.
    • 577 é divisível por 5? Não (termina em 7).
  • Se k = 3: 288 x 3 + 1 = 864 + 1 = 865.
    • 865 é divisível por 5? Sim! (termina em 5).

O Resultado

Agora, basta dividir o resultado por 5:

x=865/5=173x = 865/5 = 173

Resposta: x=173x = 173.

Então a chave fica assim

Chave pública

  • e = 5
  • n = 323

Chave privada

  • d = 173
  • n = 323

Perceba uma coisa: o valor n aparece nas duas chaves. O que muda mesmo é o expoente.

Como a mensagem vira números?

Aqui entra um ponto que costuma confundir muita gente.

Você não criptografa texto puro diretamente como se o computador entendesse letras do mesmo jeito que a gente entende. Na prática, a mensagem precisa ser transformada em números.

No meu exemplo, a solução mais limpa foi trabalhar com bytes UTF-8.

Então, em vez de inventar uma tabela tipo:

  • a = 1
  • b = 2
  • c = 3

eu deixei o navegador converter o texto em bytes.

Por exemplo, a letra A vira 65. Já um caractere com acento pode virar mais de um byte, porque depende da codificação UTF-8.

Isso é melhor porque deixa o exemplo mais próximo do mundo real e permite trabalhar com texto normal, incluindo acentos.

Como o JavaScript criptografa a mensagem

No front-end, o processo foi este:

  1. pegar o texto digitado no input;
  2. converter para bytes com TextEncoder;
  3. aplicar a fórmula do RSA em cada byte;
  4. enviar os números para o PHP.

A fórmula usada para criptografar é:

c = m^e mod n

Ou seja, para cada byte da mensagem, o JavaScript faz uma exponenciação modular usando a chave pública.

Na prática, a chave pública pode ficar no JavaScript sem problema. Isso, por si só, não é falha. O que não pode vazar é a chave privada.

Como o PHP descriptografa a mensagem

Do outro lado, no servidor, o PHP recebe a sequência numérica e faz o caminho inverso.

A fórmula da descriptografia é:

m = c^d mod n

Então, para cada valor criptografado, o PHP aplica a chave privada, recupera o byte original e depois reconstrói a string.

Ou seja, o fluxo completo fica assim:

  • usuário digita a mensagem;
  • o JavaScript criptografa no navegador;
  • o servidor recebe os números;
  • o PHP descriptografa com a chave privada;
  • a mensagem original volta a aparecer.

Onde os números primos entram nisso tudo?

Agora a resposta fica muito mais clara.

Os números primos na criptografia são importantes porque ajudam a montar uma estrutura matemática em que:

  • é fácil gerar a chave;
  • é fácil criptografar;
  • é fácil descriptografar para quem tem a chave privada;
  • mas é difícil quebrar o sistema sem as informações secretas.

No RSA, os primos p e q são a base de tudo. A segurança nasce justamente do fato de que fatorar o produto de dois primos grandes é um problema difícil.

Claro: no meu exemplo, usei números pequenos. Portanto, ele serve para estudar, não para proteger dados reais.

Este exemplo é seguro?

NÃO. E aqui eu preciso ser honesto.

Esse tipo de implementação é didática, não profissional para produção.

Ela tem várias limitações:

  • usa números muito pequenos;
  • criptografa byte por byte;
  • não usa padding;
  • não substitui HTTPS;
  • não deve ser usada para proteger dados reais.

Se você quiser segurança de verdade, o caminho correto é outro:

  • usar HTTPS/TLS;
  • usar OpenSSL ou libsodium;
  • trabalhar com criptografia híbrida;
  • deixar a troca de chaves e a proteção do tráfego nas mãos de soluções maduras.

Ou seja, estudar RSA na unha é excelente para entender o conceito. Mas inventar criptografia própria em produção é um erro.

Então vale a pena estudar isso?

Com certeza.

Inclusive, eu diria que entender RSA, chave pública, chave privada e o papel dos números primos ajuda muito a amadurecer como desenvolvedor.

Primeiro, porque você deixa de tratar segurança como magia.

Segundo, porque começa a enxergar melhor o que o navegador, o servidor e os protocolos realmente estão fazendo por baixo dos panos.

E, por fim, porque entender a base te ajuda a tomar decisões melhores na hora de projetar sistemas reais.

Conclusão

Quando alguém diz que os números primos são importantes para a criptografia, isso não é papo acadêmico solto. Existe uma aplicação prática e concreta por trás.

No RSA, os primos ajudam a criar uma estrutura matemática que permite:

  • expor uma chave pública;
  • manter outra privada;
  • criptografar dados no cliente;
  • descriptografar no servidor.

No meu exemplo, eu usei JavaScript no front-end e PHP no back-end para mostrar isso de forma simples. Não é um sistema seguro para produção, mas é um ótimo laboratório mental para entender o mecanismo.

E, sinceramente, quando a ficha cai, a criptografia para de parecer um bicho de sete cabeças.

Ela continua complexa, claro. Só que deixa de ser abstrata.

Se você curte desenvolvimento web, segurança, PHP, JavaScript e quer entender melhor o que realmente acontece nos bastidores, estudar esse tipo de exemplo vale muito a pena.

Criando criptografia RSA na prática

Para nosso exemplo você precisa ter PHP instalado em sua máquina, caso contrário não vai funcionar.

1º vamos criar o nosso index.html, ele vai ser a base para criptografar a mensagem e enviar para o servidor descriptografar.

Crie uma pasta chamada, por exemplo, “cripyt_rsa” e dentro dela um arquivo “index.html” e cole o código abaixo.

Para facilitar, vou usar a lib Bootstrap, assim a gente não perde tempo com HTML/CSS

<!doctype html>
<html lang="pt-BR">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Criptografia RSA - Versão de brinquedo</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
</head>

<body>
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container">
            <a class="navbar-brand" href="./">Navbar</a>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarSupportedContent">
                <ul class="navbar-nav me-auto mb-2 mb-lg-0">
                    <li class="nav-item">
                        <a class="nav-link active" aria-current="page" href="#">Home</a>
                    </li>
                </ul>
            </div>
        </div>
    </nav>

    <div class="container my-5">
        <h1>RSA didático: JS criptografa e PHP descriptografa</h1>
        <div class="col-lg-8 px-0">
            <p class="fs-5"></p>
            <p>Sistema de criptografia RSA para estudos - Versão de brinquedo.</p>

            <hr class="col-1 my-4">
            <form id="cryptoForm" action="decrypt.php" method="POST">
                <div class="mb-3">
                    <label for="message" class="form-label">Texto a ser criptografado</label>
                    <input type="text" class="form-control" id="message" name="inputText" required>
                </div>
                <input type="hidden" name="encrypted_message" id="encrypted_message">
                <button type="submit" class="btn btn-primary">Criptografar e Enviar</button>
            </form>

            <div class="box">
                <strong>Chave pública no JavaScript:</strong>
                <div><code>e = 5, n = 323</code></div>
            </div>

            <div class="box">
                <strong>Prévia da mensagem criptografada:</strong>
                <div id="preview">(ainda vazia)</div>
            </div>

        </div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js" integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI" crossorigin="anonymous"></script>
    <script src="main.js"></script>
</body>

</html>

2º Crie o arquivo main.js

Reposável por coletar a mensagem do formulário, criptografar e enviar para o servidor

// Chave pública
const publicKey = {
    e: 5,
    n: 323
};

/**
 * Calcula (base^exp) % mod usando o método de exponenciação rápida.
 *
 * @param mixed base
 * @param mixed exp
 * @param mixed mod
 * 
 * @return [type]
 * 
 */
function modPow(base, exp, mod) {
    let result = 1;
    base = base % mod;

    while (exp > 0) {
        if (exp % 2 === 1) {
            result = (result * base) % mod;
        }

        exp = Math.floor(exp / 2);
        base = (base * base) % mod;
    }

    return result;
}

function encryptText(text) {
    // converte texto em bytes UTF-88
    const encoder = new TextEncoder();
    const bytes = Array.from(encoder.encode(text));

    // Criptografa byte por byte: c = (m^e) % n
    return bytes.map(byte => modPow(byte, publicKey.e, publicKey.n));
}

document.getElementById('cryptoForm').addEventListener('submit', function(event) {
    event.preventDefault();

    const messageInput = document.getElementById('message');
    const hiddenInput = document.getElementById('encrypted_message');
    const preview = document.getElementById('preview');

    const text = messageInput.value;

    try {
        const encryptedArray = encryptText(text);
        const payload = JSON.stringify(encryptedArray);

        hiddenInput.value = payload;
        preview.textContent = `Encrypted Message: ${payload}`;

        this.submit();
    } catch (error) {
        console.error("Error encrypting message:", error);
        alert("An error occurred while encrypting the message. Please try again.");
    }
});

3º Agora crie o arquivo decrypt.php

Esse, por sua vez, vai receber a mensagem, descriptografar e mostrar a mensgem original.

<?php

$privateKey = [
    'd' => 29,
    'n' => 323,
];

function modPow(int $base, int $exp, int $mod): int
{
    $result = 1;
    $base = $base % $mod;

    while ($exp > 0) {
        if ($exp % 2 === 1) {
            $result = ($result * $base) % $mod;
        }

        $exp = intdiv($exp, 2);
        $base = ($base * $base) % $mod;
    }

    return $result;
}

$rawEncrypted = $_POST['encrypted_message'] ?? '[]';
$encryptedArray = json_decode($rawEncrypted, true);

$error = null;
$decryptedText = '';
$decryptedBytes = [];

if (!is_array($encryptedArray)) {
    $error = 'Payload inválido.';
    $encryptedArray = [];
} else {
    foreach ($encryptedArray as $item) {
        if (!is_numeric($item)) {
            $error = 'Payload contém valores inválidos.';
            break;
        }

        $c = (int) $item;

        // m = c^d mod n
        $m = modPow($c, $privateKey['d'], $privateKey['n']);

        if ($m < 0 || $m > 255) {
            $error = 'Byte descriptografado fora do intervalo válido.';
            break;
        }

        $decryptedBytes[] = $m;
    }
}

if (!$error) {
    if (!empty($decryptedBytes)) {
        $decryptedText = pack('C*', ...$decryptedBytes);
    } else {
        $decryptedText = '';
    }
}
?>
<!DOCTYPE html>
<html lang="pt-BR">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Criptografia RSA - Versão de brinquedo</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
</head>

<body>
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container">
            <a class="navbar-brand" href="./">Navbar</a>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarSupportedContent">
                <ul class="navbar-nav me-auto mb-2 mb-lg-0">
                    <li class="nav-item">
                        <a class="nav-link active" aria-current="page" href="#">Home</a>
                    </li>
                </ul>
            </div>
        </div>
    </nav>

    <div class="container my-5">
        <h1>Resultado no servidor PHP</h1>

        <div class="alert alert-warning">
            <strong>Chave privada no PHP:</strong>
            <div><code>d = 29, n = 323</code></div>
        </div>

        <div class="alert alert-primary">
            <strong>Mensagem criptografada recebida:</strong>
            <div><?= htmlspecialchars($rawEncrypted, ENT_QUOTES, 'UTF-8') ?></div>
        </div>

        <?php if ($error): ?>
            <div class="alert alert-danger">
                <strong>Erro:</strong>
                <div><?= htmlspecialchars($error, ENT_QUOTES, 'UTF-8') ?></div>
            </div>
        <?php else: ?>
            <div class="alert alert-success">
                <strong>Bytes descriptografados:</strong>
                <div><?= htmlspecialchars(json_encode($decryptedBytes), ENT_QUOTES, 'UTF-8') ?></div>
            </div>

            <div class="alert alert-info">
                <strong>Mensagem descriptografada:</strong>
                <div><?= htmlspecialchars($decryptedText, ENT_QUOTES, 'UTF-8') ?></div>
            </div>
        <?php endif; ?>

        <p style="margin-top: 20px;">
            <a class="btn btn-success" href="index.php">Voltar</a>
        </p>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js" integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI" crossorigin="anonymous"></script>
</body>

</html>

Basicamente é isso pessoal. Estude esse código, melhore, faça do seu jeito. Existe muitas aplicações para isso, mas tenha bastante cuidado porque isso é só a pontinha do iceberg da criptografia.

Rogério Rios Júnior
Rogério Rios

Eu trabalho com desenvolvimento web há muitos anos e, nesse caminho, acabei me especializando naquilo que mais gosto de fazer: criar soluções em WordPress e WooCommerce que realmente ajudem empresas a vender melhor.

Saiba mais

Contatos

Empresa 100% online

contato@clickartweb.com.br

(61) 9 8101-2703

Links populares

© Criação de sites e sistemas web. Todos os direitos reservados. Criado por ClickArt