Ir para o conteúdo principal

Buscar no CooLWJ

选择搜索范围,输入关键词后按回车。

Idioma

A arte de criptografar e descriptografar

Um Criptografia simétrica

A criptografia simétrica e a criptografia assimétrica são dois métodos distintos de criptografia que desempenham papéis importantes na segurança dos dados e na transmissão de informações. A seguir, apresentarei cada uma dessas tecnologias de criptografia:

1.1 Criptografia simétrica(Symmetric Encryption)

Criptografia simétrica significa usar a mesma chave tanto para criptografar quanto para descriptografar. Isso significa que o remetente e o destinatário precisam ter essa chave para realizar as operações de criptografia e descriptografia.

Algoritmos comuns de criptografia simétrica:

  • AES (Padrão de Criptografia Avançada)

  • DES (Padrão de Criptografia de Dados)

  • 3DES(Algoritmo Triplo de Criptografia de Dados)

1.2 Características da criptografia simétrica

Em geral, a criptografia simétrica apresenta as seguintes características:

  1. Velocidade relativamente alta:Como a criptografia e a descriptografia usam a mesma chave, a criptografia simétrica costuma ser mais rápida que a assimétrica.

  2. Gerenciamento de chaves:o principal desafio da criptografia simétrica está na distribuição e no gerenciamento das chaves. Se a chave vazar, a segurança da criptografia ficará comprometida.

  3. Adequado para grandes volumes de dados:por ser rápida, a criptografia simétrica é adequada para criptografar grandes volumes de dados.

Podemos ver aqui que a criptografia simétrica tem duas grandes vantagens:a primeira é a rapidez de processamento; a segunda é sua adequação a grandes volumes de dados.

Porém, a criptografia simétrica tem um problema fatal: o gerenciamento de chaves. Como transmitir a chave com segurança do servidor para o cliente é um problema! Além disso, na comunicação de um para muitos, gerenciar as chaves para impedir que sejam expostas também é um desafio. Essa é uma limitação da criptografia simétrica.

Ilustração artística de uma parede verde-azulada e um rolo de pintura

1.3 Exemplo de código

A seguir, o irmão Song vai mostrar como realizar criptografia e descriptografia simétricas com código Java.

Para implementar criptografia simétrica em Java, normalmente são usadas as classes e interfaces fornecidas pela Arquitetura de Criptografia Java (Java Cryptography Architecture, JCA).

A seguir, um exemplo simples de criptografia e descriptografia simétricas usando o algoritmo AES(Padrão de Criptografia Avançada):

public class SymmetricEncryptionExample {    // 生成密钥
    public static SecretKey generateKey() throws Exception {     
       KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        keyGenerator.init(128); // 可以是128, 192或256位
        return keyGenerator.generateKey();
    }    
    // 加密方法
    public static String encrypt(String data, SecretKey key) throws Exception {      
      Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, key);       
         byte[] encryptedBytes = cipher.doFinal(data.getBytes());        
         return Base64.getEncoder().encodeToString(encryptedBytes);
    }    
    // 解密方法
    public static String decrypt(String encryptedData, SecretKey key) throws Exception {        
    Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.DECRYPT_MODE, key);       
         byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedData));       
          return new String(decryptedBytes);
    }    
    public static void main(String[] args) {        
    try {           
             // 生成密钥
            SecretKey key = generateKey();           
             // 原始数据
            String originalData = "Hello, JavaBoy!";            
            // 加密
            String encryptedData = encrypt(originalData, key);
            System.out.println("加密数据: " + encryptedData);            
            // 解密
            String decryptedData = decrypt(encryptedData, key);
            System.out.println("解密数据: " + decryptedData);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  1. Gerar chave:usar KeyGenerator Gerar uma chave AES.

  2. Criptografia:usar Cipher para criptografar, convertendo os dados em bytes antes da criptografia e usando a codificação Base64 para convertê-los em uma string, facilitando o armazenamento ou a transmissão.

  3. Descriptografia:decodifique a string criptografada de volta para bytes e depois use a mesma chave para descriptografá-la.

No código acima, é preciso prestar atenção ao seguinte:

  • O comprimento da chave (por exemplo, 128 bits) deve ser escolhido conforme os requisitos de segurança.

  • Garanta o armazenamento seguro das chaves e não as insira diretamente no código.

  • Em ambientes de produção, deve-se considerar uma estratégia mais segura de gerenciamento de chaves.

Por segurança, geralmente usamos a abordagem acima para gerar a chave. Uma característica das chaves geradas dessa maneira é que elas mudam sempre que o sistema é reiniciado. Se você quiser controlar a geração da chave, poderá gerá-la da seguinte forma:

public static SecretKey generateKeyFromPassword(String password, int keySize) throws NoSuchAlgorithmException, InvalidKeySpecException {   
  SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");   
   KeySpec spec = new PBEKeySpec(password.toCharArray(), "salt".getBytes(), 65536, keySize);   
   return new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
}

Assim, é possível controlar a chave por meio dos parâmetros que você mesmo fornece.

1.4 Integração entre frontend e backend

Ao usar o algoritmo AES para criptografar e descriptografar entre linguagens diferentes(como JavaScript e Java), o essencial é garantir que os dois lados usem a mesma chave, o mesmo modo de operação(como CBC, ECB etc.), o mesmo esquema de preenchimento(como PKCS5Padding, PKCS7Padding etc.)e o mesmo vetor de inicialização(IV, caso seja usado um modo que exija IV, como CBC).

Alguns leitores disseram anteriormente que o backend nunca conseguia descriptografar os dados criptografados no frontend. Aqui, o irmão Song também apresenta um exemplo que combina frontend e backend.

Criptografia no frontend e descriptografia no backend

Código do frontend:

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>AES加密示例</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
    </head>
    <body>
    <script>
    function encryptAES(text, secretKey) {        
    const key = CryptoJS.enc.Utf8.parse(secretKey);        
    const iv = CryptoJS.lib.WordArray.random(128 / 8); 
    // 对于CBC模式,需要IV
    
        const encrypted = CryptoJS.AES.encrypt(text, key, {           
         iv: iv,           
          mode: CryptoJS.mode.CBC,            
          padding: CryptoJS.pad.Pkcs7
        });        
        // 返回加密后的文本和IV(Base64格式),实际使用中可能需要安全地传输这些值
        return {          
          ciphertext: encrypted.toString(),          
            iv: iv.toString(CryptoJS.enc.Base64)
        };
    }    
    const secretKey = 'helloworldhelloworldhelloworld11'; /
    / 确保密钥是32个字符长(256位)
    const text = 'Hello, javaboy!';    
    const result = encryptAES(text, secretKey);    
    console.log(result);
    </script>
    </body>
    </html>


Código do backend:

import javax.crypto.Cipher;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;import java.util.Base64;public class AESDecrypt {    public static String decryptAES(String encryptedData, String secretKey, String iv) throws Exception {        IvParameterSpec ivParameterSpec = new IvParameterSpec(Base64.getDecoder().decode(iv));        SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes("UTF-8"), "AES");        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);        byte[] decodedValue = Base64.getDecoder().decode(encryptedData);        byte[] decryptedValue = cipher.doFinal(decodedValue);        return new String(decryptedValue, "UTF-8");
    }    public static void main(String[] args) {        try {            //前端加密后的文本
            String encryptedText = "PYANpAjMsRnBIEhovtEXQw==";            String secretKey = "helloworldhelloworldhelloworld11";            //前端 IV,要和加密后的文本一起传到后端
            String iv = "y/jUHcgSOpOiyNlsfjNUBg==";            String decryptedText = decryptAES(encryptedText, secretKey, iv);
            System.out.println("解密文本: " + decryptedText);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Observe que a criptografia no frontend gera um parâmetro iv, que deve ser enviado ao backend junto com o resultado da criptografia realizada no frontend.

Criptografia no backend e descriptografia no frontend

Ao criptografar com AES em Java e descriptografar em JavaScript, também é necessário garantir que ambos os lados usem a mesma chave, o mesmo modo do algoritmo (como CBC, ECB etc.), o mesmo esquema de preenchimento (como PKCS5Padding, PKCS7Padding etc.) e, quando aplicável, o mesmo vetor de inicialização (IV).

Código do backend:

public class AESEncrypt {    public static String encryptAES(String plainText, String secretKey, String iv) throws Exception {        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        keyGenerator.init(256); // 对于AES-256
        SecretKey secret = new SecretKeySpec(secretKey.getBytes("UTF-8"), "AES");        IvParameterSpec ivParameterSpec = new IvParameterSpec(Base64.getDecoder().decode(iv));        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secret, ivParameterSpec);        byte[] encrypted = cipher.doFinal(plainText.getBytes("UTF-8"));        return Base64.getEncoder().encodeToString(encrypted);
    }    public static void main(String[] args) {        try {            String plainText = "Hello, 江南一点雨!";            String secretKey = "helloworldhelloworldhelloworld11"; // 确保密钥是32个字符长(256位)
            String iv = Base64.getEncoder().encodeToString(new byte[16]); // 示例IV,实际应用中应更安全地生成

            String encryptedText = encryptAES(plainText, secretKey, iv);
            System.out.println("Encrypted text: " + encryptedText);
            System.out.println("IV (Base64): " + iv); // 确保将IV发送给解密方

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Código do frontend:

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
    <script>
        function decryptAES(ciphertext, secretKey, iv) {            const key = CryptoJS.enc.Utf8.parse(secretKey);            const ivParsed = CryptoJS.enc.Base64.parse(iv);            const decrypted = CryptoJS.AES.decrypt(
                {                    ciphertext: CryptoJS.enc.Base64.parse(ciphertext)
                },
                key,
                {                    iv: ivParsed,                    mode: CryptoJS.mode.CBC,                    padding: CryptoJS.pad.Pkcs7
                }
            );            return decrypted.toString(CryptoJS.enc.Utf8);
        }        const secretKey = 'helloworldhelloworldhelloworld11';        const iv = 'AAAAAAAAAAAAAAAAAAAAAA=='; // 从Java代码获取
        const ciphertext = '1lPNVF1injas78KUWeKp5FusEr6f0pGgcrLAg9ELFr8='; // 从Java代码获取

        const decryptedText = decryptAES(ciphertext, secretKey, iv);        console.log(decryptedText);    </script></head><body></body></html>

O irmão Song testou pessoalmente todo o código de criptografia e descriptografia para a comunicação entre frontend e backend apresentado acima e confirmou que funciona sem problemas. Se você precisar disso, salve este artigo para usá-lo como referência.

II Criptografia assimétrica

2.1 O que é criptografia assimétrica

A criptografia assimétrica, também chamada de criptografia de chave pública, é uma forma de criptografia que usa duas chaves diferentes (uma chave pública e uma chave privada).

De modo geral, a criptografia assimétrica apresenta as seguintes características distintas:

  1. Chave pública e chave privada

  • Geração das chaves pública e privada: a criptografia assimétrica usa um par de chaves. A chave pública é aberta e qualquer pessoa pode acessá-la, enquanto a chave privada é restrita e apenas seu detentor pode acessá-la. Essas duas chaves são geradas por algoritmos matemáticos e estão relacionadas entre si, mas não é possível derivar uma a partir da outra.

  1. Alta segurança

  • Difícil de quebrar: devido à complexa relação matemática entre as chaves pública e privada, a criptografia assimétrica oferece alta segurança. É muito difícil para um invasor deduzir a chave privada a partir da chave pública, o que garante a segurança dos dados criptografados.

  • Resistência a ataques de computação quântica:alguns projetos de blockchain começaram a adotar algoritmos criptográficos resistentes a ataques de computação quântica, como variantes quânticas seguras do algoritmo de assinatura digital de curva elíptica(ECDSA), para enfrentar a futura ameaça da computação quântica.

  1. Facilidade de distribuição de chaves públicas

  • Caráter público da chave pública: a chave pública pode ser disponibilizada a qualquer pessoa, o que facilita muito sua distribuição. Qualquer pessoa pode usá-la para criptografar dados, mas somente quem possui a chave privada pode descriptografá-los.

  1. Autenticação e assinatura digital

  • Autenticação de identidade: a chave pública pode servir como identificador de um usuário, permitindo que outras pessoas verifiquem sua identidade sem conhecer sua chave privada. Isso ajuda a estabelecer identidades confiáveis em redes descentralizadas, como as de blockchain.

  • Assinatura digital: o titular da chave privada pode usá-la para assinar mensagens, enquanto outras pessoas podem usar a chave pública para verificar a autenticidade da assinatura. Isso garante a integridade da mensagem e a confiabilidade de sua origem.

  1. Comunicação segura

  • Comunicação criptografada:a chave pública pode ser usada para criptografar mensagens, e somente quem possui a chave privada correspondente pode descriptografá-las. Isso garante a segurança dos dados durante a comunicação e impede que pessoas não autorizadas acessem as informações.

  1. Controle de ativos digitais

  • Endereço de blockchain e chave privada: o endereço de blockchain geralmente é derivado da chave pública, e o usuário controla os ativos digitais associados a esse endereço por meio da chave privada. A segurança da chave privada é essencial para a segurança dos ativos digitais.

  1. Velocidade de criptografia e descriptografia

  • Relativamente lenta: a criptografia assimétrica é muito mais lenta para criptografar e descriptografar do que a criptografia simétrica, pois exige cálculos matemáticos mais complexos. No entanto, isso não impede seu uso em comunicação segura, autenticação e outras áreas.

2.2 Aplicações da criptografia assimétrica

A criptografia assimétrica tem dois cenários clássicos de uso.

  1. Criptografia: este é o uso que mais conhecemos, ou seja, criptografar com a chave pública e descriptografar com a chave privada.

  2. Assinatura:considerando que a rede não é confiável e que os dados podem ser adulterados durante a transmissão, as chaves pública e privada podem ser usadas no sentido inverso:a chave privada assina os dados e a chave pública verifica a assinatura, garantindo a segurança e a integridade dos dados.

Quanto ao segundo uso, algumas pessoas o descrevem como criptografar com a chave privada e descriptografar com a chave pública;de qualquer forma, todo mundo sabe que estão falando da mesma coisa.

Embora os algoritmos de criptografia assimétrica possam, em teoria, ser usados para criptografar dados e gerar assinaturas digitais, na prática sua alta complexidade computacional e baixa eficiência se tornam os principais obstáculos.

Em comparação com os algoritmos de criptografia simétrica, a criptografia assimétrica é várias ordens de grandeza mais lenta, o que afeta muito sua capacidade de processar grandes volumes de dados. Além disso, como os processos de criptografia e descriptografia dos algoritmos assimétricos estão estreitamente relacionados ao comprimento da chave e não oferecem suporte a modos de criptografia em blocos, eles só conseguem processar pequenas quantidades de dados que não excedam o comprimento da chave, impossibilitando a criptografia de grandes volumes de dados.

Para superar as limitações de desempenho da criptografia assimétrica, os sistemas modernos de criptografia geralmente adotam uma estratégia híbrida, combinando as vantagens da criptografia simétrica e da assimétrica. Nessa estratégia, a criptografia assimétrica é usada principalmente para transmitir com segurança uma chave de criptografia simétrica à outra parte(a chamada “negociação de chaves”). Depois que ambas as partes compartilham essa chave simétrica com segurança, podem usar algoritmos eficientes de criptografia simétrica para criptografar e descriptografar grandes volumes de dados. Essa abordagem combinada aumenta tanto a eficiência da criptografia quanto a segurança da comunicação e é amplamente utilizada em diversos protocolos de comunicação segura, como SSL/TLS.

No campo das assinaturas digitais, também foram feitas algumas adaptações para melhorar a eficiência da criptografia assimétrica. O procedimento consiste em primeiro calcular um resumo dos dados originais (usando MD5, SHA etc.), obtendo um valor de comprimento fixo. Em seguida, um algoritmo de criptografia assimétrica é usado para criptografar esse resumo e gerar a assinatura digital. Como os algoritmos de resumo conseguem condensar dados de qualquer tamanho em um resumo de comprimento fixo de forma eficiente, o comprimento do resumo é sempre o mesmo, independentemente do tamanho dos dados originais, mantendo o processo de assinatura eficiente. Ao verificar a assinatura, basta recalcular o resumo dos dados originais e compará-lo com a assinatura descriptografada para determinar rapidamente se os dados foram adulterados.

2.3 Exemplo de criptografia

Exemplo de código Java usando RSA para criptografar e descriptografar:

public class RsaDemo {    public static void main(String[] args) {        try {            KeyPair keyPair = generateRSAKeyPair();            PublicKey publicKey = keyPair.getPublic();            PrivateKey privateKey = keyPair.getPrivate();            String originalData = "Hello, 江南一点雨!";            String encryptedData = encrypt(publicKey, originalData);            String decryptedData = decrypt(privateKey, encryptedData);

            System.out.println("加密后的数据: " + encryptedData);
            System.out.println("解密后的数据: " + decryptedData);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }    public static String decrypt(PrivateKey privateKey, String encryptedData) throws Exception {        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);        byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedData));        return new String(decryptedBytes);
    }    public static String encrypt(PublicKey publicKey, String data) throws Exception {        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);        byte[] bytes = cipher.doFinal(data.getBytes());        return Base64.getEncoder().encodeToString(bytes);
    }    public static KeyPair generateRSAKeyPair() throws NoSuchAlgorithmException {        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
        keyGen.initialize(2048); // 可以指定密钥长度,如2048位
        return keyGen.generateKeyPair();
    }
}

2.4 Integração entre frontend e backend

Criptografia no frontend e descriptografia no backend

O código do backend é igual ao do exemplo acima. A diferença é que, depois de obter a chave pública, podemos imprimi-la; essa chave pública deverá ser enviada ao frontend:

public static void main(String[] args) {    try {        KeyPair keyPair = generateRSAKeyPair();        PublicKey publicKey = keyPair.getPublic();        PrivateKey privateKey = keyPair.getPrivate();
        System.out.println("公钥: " + Base64.getEncoder().encodeToString(publicKey.getEncoded()));        String originalData = "Hello, 江南一点雨!";        String encryptedData = encrypt(publicKey, originalData);        String decryptedData = decrypt(privateKey, encryptedData);
        System.out.println("加密后的数据: " + encryptedData);
        System.out.println("解密后的数据: " + decryptedData);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

O código do frontend é o seguinte:

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>RSA Encryption Example</title>
    <!-- 引入jsencrypt库 -->
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/bin/jsencrypt.min.js"></script></head><body><script>
    // 重写前端加密方法
    function encryptData(publicKey, data) {        // 创建一个新的JSEncrypt对象
        var encryptor = new JSEncrypt();        // 设置公钥
        encryptor.setPublicKey(publicKey);        // 加密数据
        var encrypted = encryptor.encrypt(data);        return encrypted;
    }    // 示例公钥(实际使用时应该替换为服务器提供的公钥)
    var publicKey = `MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnP5FOXIghidLDE2FgFwzi4Y+qTdYjnc0eMoiL5i4gdOeTE+7Uu9YvDB51GcBWKD9lvmZr5rX8z0OBpwe526qsTekNpXoIVk9DB34US0HOrAXEEwpUTVuSR656cJGAwmWBkVaalQynEz4Dlzrm53zExVYueruYUuzFyuZDaQcFnl3rWUH/XDkCIe23z0R1TfT3Q2OYNKft0u56r0S/ko99utXuYJK9yowe7QGT6q4cSJwsITQTomCARAq9q+bSNuGEYa4FlYCKIKWIhKbMhz0FYIMB2fJN10GyZbbvKASqeMkuCoD2Efgd8/6uMwOaMcx9LgEkcFaQ3qgDutsPXNUswIDAQAB`;    // 要加密的数据
    var data = 'Hello, javaboy!';    // 调用加密方法
    var encryptedData = encryptData(publicKey, data);    // 输出加密后的数据
    console.log('Encrypted Data:', encryptedData);</script></body></html>

Criptografia no backend e descriptografia no frontend

Usar o algoritmo RSA para criptografar dados em Java e descriptografá-los em JavaScript significa que o servidor usa a chave pública do front-end para criptografar, e o front-end usa sua própria chave privada para descriptografar. Nesse cenário, a chave privada do front-end pode ser roubada com facilidade, por isso não recomendo essa abordagem. Assim, também não darei um exemplo.

Pronto, pessoal! Conversamos mais uma vez sobre criptografia simétrica e assimétrica. Todos os exemplos de código acima foram testados com sucesso pelo Song e vocês podem usá-los como referência.


Comentários

Enviando

Escolha seu tema

O tema é aplicado a todo o site.