自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

我們一起聊聊加解密的藝術(shù)

安全 應用安全
對稱加密是指加密和解密都使用相同的密鑰。這意味著發(fā)送方和接收方都必須擁有這個密鑰才能進行加密和解密操作。

加密這個事情其實之前和小伙伴們聊過很多次,不過最近松哥又想到一些細節(jié)問題,再和小伙伴們補充聊一聊。

一 對稱加密

對稱加密和非對稱加密是兩種不同的加密方法,它們在數(shù)據(jù)安全和信息傳輸中扮演著重要的角色。下面我將分別介紹這兩種加密技術(shù):

1.1 對稱加密(Symmetric Encryption)

對稱加密是指加密和解密都使用相同的密鑰。這意味著發(fā)送方和接收方都必須擁有這個密鑰才能進行加密和解密操作。

常見的對稱加密算法:

  • AES(高級加密標準)
  • DES(數(shù)據(jù)加密標準)
  • 3DES(三重數(shù)據(jù)加密算法)

1.2 對稱加密特點

一般來說,對稱加密具有如下特點:

  • 速度較快:由于加密和解密使用相同的密鑰,對稱加密通常比非對稱加密要快。
  • 密鑰管理:對稱加密的主要挑戰(zhàn)在于密鑰的分發(fā)和管理。如果密鑰泄露,加密的安全性就會受到威脅。
  • 適用于大量數(shù)據(jù):由于速度快,對稱加密適合加密大量數(shù)據(jù)。

從這里可以看到,對稱加密主要有兩大優(yōu)勢:第一就是運算速度快;第二就是適用于大量數(shù)據(jù)。

但是,對稱加密有一個致命的問題,就是密鑰管理。如何從服務端將密鑰安全的傳輸?shù)娇蛻舳耸莻€問題!另外就是當一對多通信的時候,如何管理好密鑰不被泄露也是一個考驗。這是對稱加密的不足之處。

1.3 代碼案例

接下來松哥給大家演示下 Java 代碼如何做對稱加解密。

在 Java 中實現(xiàn)對稱加密,通常使用 Java 加密架構(gòu)(Java Cryptography Architecture, JCA)提供的類和接口。

下面是一個使用 AES(高級加密標準)算法進行對稱加密和解密的簡單示例:

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

            // 原始數(shù)據(jù)
            String originalData = "Hello, JavaBoy!";

            // 加密
            String encryptedData = encrypt(originalData, key);
            System.out.println("加密數(shù)據(jù): " + encryptedData);

            // 解密
            String decryptedData = decrypt(encryptedData, key);
            System.out.println("解密數(shù)據(jù): " + decryptedData);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • 生成密鑰:使用 KeyGenerator 生成一個AES密鑰。
  • 加密:使用 Cipher 類進行加密,將數(shù)據(jù)轉(zhuǎn)換成字節(jié)后加密,并使用 Base64 編碼轉(zhuǎn)換為字符串,以便于存儲或傳輸。
  • 解密:將加密的字符串解碼回字節(jié),然后使用相同的密鑰進行解密。

以上代碼大家需要注意的是:

  • 密鑰長度(如 128 位)應根據(jù)安全需求選擇。
  • 確保密鑰安全存儲,不要在代碼中硬編碼密鑰。
  • 對于生產(chǎn)環(huán)境,應考慮使用更安全的密鑰管理策略。

出于安全考慮,我們一般使用上面的方案生成密鑰。這種方案生成的密鑰有一個特點就是系統(tǒng)每次重啟就會變。如果你希望能夠自己控制密鑰的生成,那么可以通過如下方式生成密鑰:

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

這樣就可以通過自己傳入的參數(shù)去控制密鑰。

1.4 前后端搭配

在跨語言(如 JavaScript 和 Java)使用 AES 算法進行加密和解密時,關(guān)鍵是確保兩端使用相同的密鑰、算法模式(如 CBC, ECB 等)、填充模式(如 PKCS5Padding, PKCS7Padding 等)和初始化向量(IV,如果使用了需要 IV的 模式如 CBC)。

之前有小伙伴說自己前端加密之后后端總是無法解密,松哥這里也給一個前后端搭配的例子。

前端加密后端解密

前端代碼:

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

后端代碼:

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

注意這里前端加密時會產(chǎn)生一個 iv 參數(shù),要隨著前端加密結(jié)果一起傳遞給后端。

后端加密前端解密

在 Java 進行 AES 加密并在 JavaScript 中解密時,同樣需要確保兩端使用相同的密鑰、算法模式(如 CBC、ECB 等)、填充模式(如 PKCS5Padding、PKCS7Padding 等)以及(如果適用)相同的初始化向量(IV)。

后端代碼:

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發(fā)送給解密方

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

前端代碼:

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

以上前后端交互加解密代碼松哥親測都是沒問題的,大家有這方面的需求記得及時收藏本文,可以作為參考。

二 非對稱加密

2.1 什么是非對稱加密

非對稱加密,也稱為公鑰加密,是一種使用兩個不同密鑰(公鑰和私鑰)的加密方式。

一般來說,非對稱加密有如下幾種不同的特點:

  1. 公鑰與私鑰

公鑰與私鑰的生成:非對稱加密使用一對密鑰,公鑰是公開的,任何人都可以訪問,而私鑰是私有的,只有密鑰的持有者可以訪問。這兩個密鑰是由數(shù)學算法生成的,且相互關(guān)聯(lián)但不可從一方推導出另一方。

  1. 安全性高

難以破解:由于公鑰和私鑰的復雜數(shù)學關(guān)系,非對稱加密的安全性較高。攻擊者很難從公鑰中推斷出私鑰,從而保證了加密數(shù)據(jù)的安全性。

抗量子計算攻擊:一些區(qū)塊鏈項目開始采用抗量子計算攻擊的加密算法,如橢圓曲線數(shù)字簽名算法(ECDSA)的量子安全變體,以應對未來量子計算的威脅。

  1. 公開密鑰分發(fā)方便

公鑰的公開性:公鑰可以公開給任何人,因此分發(fā)起來非常方便。任何人都可以使用公鑰來加密數(shù)據(jù),但只有私鑰持有者才能解密。

  1. 身份驗證與數(shù)字簽名

身份驗證:公鑰可以用作用戶的身份標識,其他人可以驗證用戶的身份而無需了解其私鑰。這有助于在區(qū)塊鏈等去中心化網(wǎng)絡(luò)中建立可信身份。

數(shù)字簽名:私鑰持有者可以使用私鑰對消息進行簽名,其他人則可以使用公鑰來驗證簽名的真實性。這確保了消息的完整性和來源的可靠性。

  1. 安全通信

加密通信:公鑰可以用于加密消息,只有持有相應私鑰的人才能解密。這確保了通信過程中的數(shù)據(jù)安全,防止了信息被未經(jīng)授權(quán)的人員訪問。

  1. 數(shù)字資產(chǎn)控制

區(qū)塊鏈地址與私鑰:區(qū)塊鏈地址通常由公鑰派生而來,用戶通過私鑰來控制與該地址相關(guān)聯(lián)的數(shù)字資產(chǎn)。私鑰的安全性對數(shù)字資產(chǎn)的安全至關(guān)重要。

  1. 加密解密速度

相對較慢:非對稱加密的加密和解密速度相對于對稱加密要慢得多,因為它需要進行更加復雜的數(shù)學計算。然而,這并不影響其在安全通信、身份驗證等領(lǐng)域的應用。

2.2 非對稱加密的用途

非對稱加密有兩個經(jīng)典使用場景。

  • 加密:這是我們最為熟知的用法,就是公鑰加密,私鑰解密。
  • 簽名:考慮到網(wǎng)絡(luò)不可信,數(shù)據(jù)在傳輸過程中可能被篡改,這個時候公私鑰可以反過來用,用私鑰對數(shù)據(jù)進行簽名,公鑰進行驗簽,確保數(shù)據(jù)安全完整。

針對第二點用途,有的小伙伴會將之表述為用私鑰進行加密,公鑰進行解密,反正大伙知道說的是同一回事。

非對稱加密算法,盡管在理論上能夠用于數(shù)據(jù)加密和數(shù)字簽名,但在實踐中,其高計算復雜度和低效率成為了主要障礙。

相比對稱加密算法,非對稱加密的運算速度要慢上幾個數(shù)量級,這極大地影響了其處理大數(shù)據(jù)量的能力。此外,由于非對稱加密算法的加密和解密過程與密鑰長度緊密相關(guān),且不支持分組加密模式,導致它只能處理不超過密鑰長度的少量數(shù)據(jù),無法進行大量數(shù)據(jù)的加密。

為了克服非對稱加密在性能上的不足,現(xiàn)代加密系統(tǒng)通常采用混合加密策略,即結(jié)合對稱加密和非對稱加密的優(yōu)點。在這種策略中,非對稱加密主要用于安全地傳輸一個對稱加密的密鑰(即“密鑰協(xié)商”)給另一方。一旦雙方安全地共享了這個對稱密鑰,就可以使用高效的對稱加密算法來加密和解密大量數(shù)據(jù)。這種結(jié)合使用的方法不僅提高了加密效率,還增強了通信的安全性,被廣泛應用于各種安全通信協(xié)議中,如SSL/TLS。

在數(shù)字簽名領(lǐng)域,為了提升非對稱加密的效率也做了一些適配。具體做法是,首先對原始數(shù)據(jù)進行摘要處理(可以利用 MD5、SHA 等),得到一個固定長度的摘要值。然后,使用非對稱加密算法對這個摘要值進行加密,生成數(shù)字簽名。由于摘要算法能夠高效地將任意長度的數(shù)據(jù)壓縮為固定長度的摘要,因此不管原始數(shù)據(jù)多大,摘要數(shù)據(jù)長度都一樣,簽名過程也能保持高效。當驗證簽名時,只需重新計算原始數(shù)據(jù)的摘要,并與解密后的簽名進行比較,即可快速判斷數(shù)據(jù)是否被篡改。

2.3 加密案例

Java 代碼使用 RSA 加解密案例:

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("加密后的數(shù)據(jù): " + encryptedData);
            System.out.println("解密后的數(shù)據(jù): " + 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 前后端搭配

前端加密后端解密

后端代碼和上面案例中一致,不同的是,我們在拿到公鑰之后,可以將公鑰打印出來,這個公鑰將來要傳遞給前端:

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("加密后的數(shù)據(jù): " + encryptedData);
        System.out.println("解密后的數(shù)據(jù): " + decryptedData);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

前端代碼如下:

<!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/jsencrypt@3.0.0-beta.1/bin/jsencrypt.min.js"></script>
</head>
<body>
<script>
    // 重寫前端加密方法
    function encryptData(publicKey, data) {
        // 創(chuàng)建一個新的JSEncrypt對象
        var encryptor = new JSEncrypt();
        // 設(shè)置公鑰
        encryptor.setPublicKey(publicKey);
        // 加密數(shù)據(jù)
        var encrypted = encryptor.encrypt(data);
        return encrypted;
    }

    // 示例公鑰(實際使用時應該替換為服務器提供的公鑰)
    var publicKey = `MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnP5FOXIghidLDE2FgFwzi4Y+qTdYjnc0eMoiL5i4gdOeTE+7Uu9YvDB51GcBWKD9lvmZr5rX8z0OBpwe526qsTekNpXoIVk9DB34US0HOrAXEEwpUTVuSR656cJGAwmWBkVaalQynEz4Dlzrm53zExVYueruYUuzFyuZDaQcFnl3rWUH/XDkCIe23z0R1TfT3Q2OYNKft0u56r0S/ko99utXuYJK9yowe7QGT6q4cSJwsITQTomCARAq9q+bSNuGEYa4FlYCKIKWIhKbMhz0FYIMB2fJN10GyZbbvKASqeMkuCoD2Efgd8/6uMwOaMcx9LgEkcFaQ3qgDutsPXNUswIDAQAB`;

    // 要加密的數(shù)據(jù)
    var data = 'Hello, javaboy!';

    // 調(diào)用加密方法
    var encryptedData = encryptData(publicKey, data);

    // 輸出加密后的數(shù)據(jù)
    console.log('Encrypted Data:', encryptedData);

</script>
</body>
</html>

后端加密前端解密

在 Java 中使用 RSA 算法加密數(shù)據(jù),并在 JavaScript 中解密這些數(shù)據(jù),意味著服務端用前端的公鑰加密,前端用自己的私鑰解密,這種場景前端私鑰很容易被盜取,因此不推薦這種用法。我也就不舉例了。

好啦,又和小伙伴們聊了一遍對稱加密和非對稱加密,上面的案例代碼松哥都是測試通過的,小伙伴們可以作為參考。

責任編輯:武曉燕 來源: 江南一點雨
相關(guān)推薦

2024-08-26 08:34:47

AES加密算法

2023-04-26 07:30:00

promptUI非結(jié)構(gòu)化

2021-08-27 07:06:10

IOJava抽象

2024-02-20 21:34:16

循環(huán)GolangGo

2022-10-08 00:00:05

SQL機制結(jié)構(gòu)

2023-08-04 08:20:56

DockerfileDocker工具

2022-05-24 08:21:16

數(shù)據(jù)安全API

2023-08-10 08:28:46

網(wǎng)絡(luò)編程通信

2023-09-10 21:42:31

2023-06-30 08:18:51

敏捷開發(fā)模式

2023-03-07 07:05:29

生產(chǎn)數(shù)據(jù)庫運維

2021-07-31 11:40:55

Openresty開源

2022-02-14 07:03:31

網(wǎng)站安全MFA

2022-06-26 09:40:55

Django框架服務

2022-10-28 07:27:17

Netty異步Future

2022-04-06 08:23:57

指針函數(shù)代碼

2023-12-28 09:55:08

隊列數(shù)據(jù)結(jié)構(gòu)存儲

2022-11-12 12:33:38

CSS預處理器Sass

2024-02-26 00:00:00

Go性能工具

2023-07-27 07:46:51

SAFe團隊測試
點贊
收藏

51CTO技術(shù)棧公眾號