Aller au contenu principal

Rechercher sur CooLWJ

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

Langue

L’art du chiffrement et du déchiffrement

I Chiffrement symétrique

Le chiffrement symétrique et le chiffrement asymétrique sont deux méthodes de chiffrement différentes qui jouent un rôle important dans la sécurité des données et la transmission des informations. Je vais présenter chacune de ces deux techniques :

1.1 Chiffrement symétrique(chiffrement à clé symétrique)

Le chiffrement symétrique utilise la même clé pour chiffrer et déchiffrer. Cela signifie que l’expéditeur et le destinataire doivent tous deux posséder cette clé pour effectuer ces opérations.

Algorithmes courants de chiffrement symétrique :

  • AES(norme de chiffrement avancé)

  • DES(norme de chiffrement des données)

  • 3DES(algorithme de triple chiffrement des données)

1.2 Caractéristiques du chiffrement symétrique

En général, le chiffrement symétrique présente les caractéristiques suivantes :

  1. Assez rapide:puisque le chiffrement et le déchiffrement utilisent la même clé, le chiffrement symétrique est généralement plus rapide que le chiffrement asymétrique.

  2. Gestion des clés:Le principal défi du chiffrement symétrique réside dans la distribution et la gestion des clés. Si la clé est divulguée, la sécurité du chiffrement est menacée.

  3. Adapté aux grands volumes de données:grâce à sa rapidité, le chiffrement symétrique convient au chiffrement de grandes quantités de données.

On constate ici que le chiffrement symétrique présente deux grands avantages : premièrement, sa rapidité de calcul ; deuxièmement, son adaptation à de grands volumes de données.

Cependant, le chiffrement symétrique présente un problème crucial : la gestion des clés. Comment transmettre la clé du serveur au client en toute sécurité ? C’est un vrai problème ! De plus, lors de communications d’un émetteur vers plusieurs destinataires, gérer correctement les clés pour éviter leur divulgation constitue également un défi. C’est la faiblesse du chiffrement symétrique.

Illustration artistique d’un mur bleu-vert et d’un rouleau à peinture

1.3 Exemple de code

Ensuite, votre ami Song va vous montrer comment effectuer un chiffrement et un déchiffrement symétriques en Java.

En Java, le chiffrement symétrique est généralement mis en œuvre à l’aide des classes et interfaces fournies par l’architecture cryptographique Java (Java Cryptography Architecture, JCA).

Voici un exemple simple de chiffrement et de déchiffrement symétriques utilisant l’algorithme AES(norme de chiffrement avancé):

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. Générer les clés:utiliser KeyGenerator  pour générer une clé AES.

  2. Chiffrement:utiliser Cipher pour chiffrer les données après leur conversion en octets, puis utiliser l’encodage Base64 pour les convertir en chaîne de caractères afin de faciliter leur stockage ou leur transmission.

  3. Décryptage:décoder la chaîne chiffrée pour retrouver les octets, puis les déchiffrer avec la même clé.

Voici les points auxquels il faut prêter attention dans le code ci-dessus :

  • La longueur de la clé(par exemple 128 bits)doit être choisie en fonction des exigences de sécurité.

  • Veillez à stocker les clés de manière sécurisée et à ne pas les coder en dur.

  • Pour un environnement de production, il convient d’envisager une stratégie de gestion des clés plus sûre.

Pour des raisons de sécurité, nous utilisons généralement la méthode ci-dessus pour générer la clé. Les clés générées de cette manière ont une particularité : elles changent à chaque redémarrage du système. Si vous souhaitez contrôler vous-même la génération de la clé, vous pouvez procéder comme suit :

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");
}

Vous pouvez ainsi contrôler la clé au moyen des paramètres que vous transmettez vous-même.

1.4 Association du frontal et du serveur

Lorsqu’on utilise l’algorithme AES pour chiffrer et déchiffrer entre plusieurs langages (comme JavaScript et Java), l’essentiel est de s’assurer que les deux côtés emploient la même clé, le même mode de chiffrement (CBC, ECB, etc.), le même mode de bourrage (PKCS5Padding, PKCS7Padding, etc.) et le même vecteur d’initialisation (IV, si le mode utilisé en exige un, comme CBC).

Certains lecteurs disaient auparavant que leur serveur ne parvenait jamais à déchiffrer les données chiffrées côté interface. Moi, Song, je vous propose donc aussi un exemple associant les deux côtés.

Chiffrement côté frontend, déchiffrement côté backend

Code 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>


Code côté serveur :

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();
        }
    }
}

Attention : le chiffrement côté frontend produit ici un paramètre iv, qui doit être transmis au backend avec le résultat du chiffrement.

Chiffrement côté serveur, déchiffrement côté client

Lorsqu’on chiffre en AES dans Java et qu’on déchiffre dans JavaScript, il faut également s’assurer que les deux côtés utilisent la même clé, le même mode de chiffrement(comme CBC, ECB, etc.), le même mode de bourrage(comme PKCS5Padding, PKCS7Padding, etc.)ainsi que, le cas échéant, le même vecteur d’initialisation(IV).

Code côté serveur :

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();
        }
    }
}

Code 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>

Frère Song a personnellement testé tout le code de chiffrement et de déchiffrement des échanges entre frontend et backend présenté ci-dessus : il fonctionne correctement. Si vous en avez besoin, pensez à enregistrer cet article dans vos favoris pour vous y référer.

II Chiffrement asymétrique

2.1 Qu’est-ce que le chiffrement asymétrique

Le chiffrement asymétrique, également appelé chiffrement à clé publique, est une méthode de chiffrement qui utilise deux clés différentes : une clé publique et une clé privée.

En général, le chiffrement asymétrique présente les caractéristiques suivantes :

  1. Clé publique et clé privée

  • Génération des clés publique et privée : le chiffrement asymétrique utilise une paire de clés. La clé publique est accessible à tous, tandis que la clé privée est réservée à son détenteur. Ces deux clés sont générées par des algorithmes mathématiques et sont liées entre elles, mais l’une ne permet pas de déduire l’autre.

  1. Sécurité élevée

  • Difficile à casser : la relation mathématique complexe entre la clé publique et la clé privée confère au chiffrement asymétrique un niveau de sécurité élevé. Il est très difficile pour un attaquant de déduire la clé privée à partir de la clé publique, ce qui garantit la sécurité des données chiffrées.

  • Résistance aux attaques quantiques : certains projets de blockchain commencent à adopter des algorithmes cryptographiques résistants aux attaques quantiques, tels que des variantes à sécurité quantique de l’algorithme de signature numérique sur courbes elliptiques (ECDSA), pour faire face à la menace future de l’informatique quantique.

  1. La clé publique est facile à distribuer

  • Caractère public de la clé publique : la clé publique peut être communiquée à n’importe qui, ce qui facilite grandement sa distribution. Chacun peut l’utiliser pour chiffrer des données, mais seul le détenteur de la clé privée peut les déchiffrer.

  1. Authentification et signature numérique

  • Authentification : la clé publique peut servir d’identifiant à un utilisateur, permettant aux autres de vérifier son identité sans connaître sa clé privée. Cela contribue à établir des identités fiables dans les réseaux décentralisés comme les blockchains.

  • Signature numérique : le détenteur de la clé privée peut l’utiliser pour signer un message, et les autres peuvent utiliser la clé publique pour vérifier l’authenticité de la signature. Cela garantit l’intégrité du message et la fiabilité de sa provenance.

  1. Communications sécurisées

  • Communication chiffrée : une clé publique peut servir à chiffrer des messages, que seul le détenteur de la clé privée correspondante peut déchiffrer. Cela garantit la sécurité des données pendant la communication et empêche les personnes non autorisées d’accéder aux informations.

  1. Contrôle des actifs numériques

  • Adresse de blockchain et clé privée:une adresse de blockchain est généralement dérivée d’une clé publique. L’utilisateur contrôle les actifs numériques associés à cette adresse au moyen de sa clé privée. La sécurité de la clé privée est essentielle à celle des actifs numériques.

  1. Vitesse de chiffrement et de déchiffrement

  • Relativement lent:le chiffrement et le déchiffrement asymétriques sont beaucoup plus lents que leurs équivalents symétriques, car ils nécessitent des calculs mathématiques plus complexes. Cela ne remet toutefois pas en cause leur utilisation dans les communications sécurisées, l’authentification et d’autres domaines.

2.2 Utilisations du chiffrement asymétrique

Le chiffrement asymétrique possède deux cas d’utilisation classiques.

  1. Chiffrement : c’est l’utilisation qui nous est la plus familière, à savoir chiffrer avec la clé publique et déchiffrer avec la clé privée.

  2. Signature : puisque le réseau n’est pas fiable, les données peuvent être altérées pendant leur transmission. On peut alors inverser les rôles des clés publique et privée : la clé privée sert à signer les données, et la clé publique à vérifier la signature, afin de garantir la sécurité et l’intégrité des données.

Pour le second usage, certains parlent de chiffrement avec la clé privée et de déchiffrement avec la clé publique ; de toute façon, tout le monde comprend qu’il est question de la même chose.

Bien que les algorithmes de chiffrement asymétrique puissent, en théorie, servir au chiffrement des données et aux signatures numériques, leur forte complexité de calcul et leur faible efficacité constituent en pratique des obstacles majeurs.

Par rapport aux algorithmes de chiffrement symétrique, les opérations de chiffrement asymétrique sont plus lentes de plusieurs ordres de grandeur, ce qui limite considérablement leur capacité à traiter de grands volumes de données. En outre, les processus de chiffrement et de déchiffrement des algorithmes asymétriques étant étroitement liés à la longueur de la clé et ne prenant pas en charge les modes de chiffrement par blocs, ils ne peuvent traiter que de petites quantités de données ne dépassant pas la longueur de la clé et ne permettent pas de chiffrer de grands volumes de données.

Pour pallier les limites du chiffrement asymétrique en matière de performances, les systèmes de chiffrement modernes adoptent généralement une stratégie hybride, qui combine les avantages du chiffrement symétrique et du chiffrement asymétrique. Dans cette stratégie, le chiffrement asymétrique sert principalement à transmettre de manière sécurisée une clé de chiffrement symétrique à l’autre partie (c’est la « négociation de clé »). Une fois cette clé symétrique partagée en toute sécurité, les deux parties peuvent utiliser un algorithme de chiffrement symétrique performant pour chiffrer et déchiffrer de grandes quantités de données. Cette utilisation combinée améliore l’efficacité du chiffrement tout en renforçant la sécurité des communications ; elle est largement employée dans divers protocoles de communication sécurisée, comme SSL/TLS.

Dans le domaine des signatures numériques, des adaptations ont également été apportées pour améliorer l’efficacité du chiffrement asymétrique. Concrètement, on calcule d’abord une empreinte des données d’origine (avec MD5, SHA, etc.) afin d’obtenir une valeur de longueur fixe. On chiffre ensuite cette empreinte à l’aide d’un algorithme de chiffrement asymétrique pour générer la signature numérique. Comme les algorithmes de hachage peuvent efficacement réduire des données de longueur quelconque à une empreinte de longueur fixe, la taille de l’empreinte reste identique quelle que soit la taille des données d’origine, ce qui permet au processus de signature de rester efficace. Pour vérifier la signature, il suffit de recalculer l’empreinte des données d’origine et de la comparer à la signature déchiffrée pour déterminer rapidement si les données ont été altérées.

2.3 Exemple de chiffrement

Exemple de chiffrement et de déchiffrement RSA en Java :

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 Association du frontend et du backend

Chiffrement côté frontend, déchiffrement côté backend

Le code côté serveur est identique à celui de l’exemple précédent. La différence est qu’après avoir obtenu la clé publique, nous pouvons l’afficher ; cette clé devra ensuite être transmise à l’interface :

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();
    }
}

Le code frontend est le suivant :

<!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>

Chiffrement côté serveur, déchiffrement côté client

Chiffrer des données en Java avec l’algorithme RSA, puis les déchiffrer en JavaScript, signifie que le serveur les chiffre avec la clé publique du client et que le client les déchiffre avec sa propre clé privée. Dans ce scénario, la clé privée du client peut facilement être dérobée ; cette utilisation est donc déconseillée. Je ne donnerai donc pas d’exemple.

Voilà, nous avons encore fait le tour du chiffrement symétrique et asymétrique ensemble. J’ai testé avec succès tout le code des exemples ci-dessus ; vous pouvez vous en servir comme référence. — Song


Commentaires

Envoi en cours

Choisissez votre thème

Le thème s’applique à tout le site.