4-對稱加密

對稱加密與非對稱加密

按照密鑰的特征不同, 密碼體制分為對稱密碼體制和非對稱密碼體制。對稱加密和非對稱加密都是可逆加密,用于安全的傳送數(shù)據(jù)。對稱加密是指數(shù)據(jù)加密和解密采用同一個密鑰,而非對稱加密則使用不同的密鑰進行加解密。對稱加密的優(yōu)勢是加密和解密速度快,能夠處理大數(shù)據(jù)量的加解密,但其安全性依賴于密鑰的安全性,所以必須通過可信任的通道交換密鑰

分組密碼與流密碼

按照對明文消息加密方式的不同, 密碼體制分為分組密碼(Block Cipher)和流密碼(Stream Cipher)。分組密碼也稱為塊密碼,對稱密碼體制均為分組密碼,分組密碼加密時首先將明文分割為若干固定長度的組,如果數(shù)據(jù)不夠一個分組,則通過填充額外的數(shù)據(jù)來補全成一個分組,然后逐個對每個分組進行加密。流密碼也稱為序列密碼,是使用密鑰流對明文中的每個二進制位或者字節(jié)進行加密。

分組密碼的工作模式

分組密碼的工作模式是指每個加密分組之間的關聯(lián)方式,主要有ECB模式(Electronic Code Book,電子密碼本)、CBC模式(Cipher Block Chain,密文分組鏈接)、CFB模式(Cipher Feed Back,密文反饋)、OFB模式(Output Feed Back,輸出反饋)、CTR模式(Counter,計數(shù)器)。

不同的工作模式有不同的優(yōu)缺點,比如ECB模式是最基本的工作模式,每個密文分組不影響其他分組,相同的明文加密后對應相同的密文,無初始化向量。而CBC模式是目前使用最廣泛的工作模式,明文加密前需要與前面的密文進行異或操作,第一個明文與初始向量進行異或操作,因此只要選擇不同的初始向量,加密后的密文就不同。關于模式的詳細內容可參考其他的資料

填充模式

如果明文的長度不是分組的整數(shù)倍,那么最后的那部分消息就不夠一個分組,這時就要對最后一個消息塊進行填充。目前有多種可行的填充方式,如果加密時以某種方式填充,解密時必須能夠識別這種填充方式并去除填充內容。常見填充方式有Zeros填充(全部填充為0)、X923 填充(最后一個字節(jié)為填充字節(jié)數(shù),其他填0)、PKCS7填充(每個填充字節(jié)都為填充了的字節(jié)數(shù))、ISO10126填充(最后一個字節(jié)為填充字節(jié)數(shù),其他填隨機數(shù))等。

加密解密過程(以AES為例)

  1. 生成密鑰
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(new SecureRandom());
SecretKey secretKey = keyGenerator.generateKey();
byte[] bytesKey = secretKey.getEncoded();
  1. 存儲密鑰

生成的密鑰bytesKey為毫無意義的字節(jié)數(shù)組,不能通過UTF-8等字符編碼方式解析成無亂碼的字符串(new String(bytesKey, "UTF-8")為亂碼)),所以存儲密鑰有兩種方式

hex編碼方式

hex編碼方式,我們可以通過16進制與2進制轉換方式,清晰的看到密鑰的二進制數(shù)據(jù),但是需要占用雙倍的存儲空間

二進制碼:   0100_0001
重新分組:   0000_0100 0000_0001
十六進制:   4                 1
Hex編碼:   41

base64編碼方式

Base64編碼就是把3個8位的二進制數(shù)據(jù)用4個ASCII可見字符展示出來。編碼時,將3個8位二進制碼重新分組成4個6位的二進制碼,不足6位的,右側補零,然后這4個6位的二進制碼高位補兩個0,形成4個8位的字節(jié)數(shù)據(jù),然后取每個字節(jié)的十進制值在編碼表中對應的字符作為最終的編碼數(shù)據(jù)。Base64編碼后的數(shù)據(jù)長度是源數(shù)據(jù)長度的4/3。標準的Base64編碼要求最終的數(shù)據(jù)長度是4字節(jié)的整數(shù)倍,不足4字節(jié)的倍數(shù)時要用填充字符補齊,填充字符為等號“=”。編碼表如下

 0 A     1 B     2 C     3 D     4 E     5 F     6 G     7 H    
 8 I     9 J    10 K    11 L    12 M    13 N    14 O    15 P    
16 Q    17 R    18 S    19 T    20 U    21 V    22 W    23 X    
24 Y    25 Z    26 a    27 b    28 c    29 d    30 e    31 f    
32 g    33 h    34 i    35 j    36 k    37 l    38 m    39 n    
40 o    41 p    42 q    43 r    44 s    45 t    46 u    47 v    
48 w    49 x    50 y    51 z    52 0    53 1    54 2    55 3    
56 4    57 5    58 6    59 7    60 8    61 9    62 +    63 /

例如ASCII碼A的Base64編碼過程為

二進制:     0100 0001
重新分組:   010000 01
低位補零:   010000 010000
高位補零:   00010000 00010000 
轉十進制:   16       16
對應字符:   Q        Q

為了節(jié)省存儲空間,密鑰一般選擇Base64進行編碼存儲為字符串A(保證無亂碼),加密和解密的雙方都得知A。

  1. 加密

發(fā)送方: 在發(fā)送數(shù)據(jù)B前,對A進行base64解碼得到密鑰字節(jié)數(shù)組C,使用C對B進行加密得到密文字節(jié)數(shù)組D

  1. 編碼與解碼

發(fā)送方:由于D也是沒有意義的字節(jié)數(shù)組,在網(wǎng)絡上傳輸時,需要將D進行base64編碼成無亂碼的字符串E,網(wǎng)絡傳輸E(例如http://xxxx?text=E
接收方: 接收方收到的為密文的base64編碼,所以需要進行base64解碼得到密文的字節(jié)數(shù)組D

  1. 解密

接收方:對A進行base64解碼得到密鑰字節(jié)數(shù)組C,使用C對D進行解密得到原始信息B

AES示例

AES(Advanced Encryption Standard,高級數(shù)據(jù)加密標準)是DES的繼任者,支持128、192、256位密鑰長度。下面的代碼是AES加密的示例,使用CBC工作模式和PKCS5填充模式,CBC工作模式需要16個字節(jié)的初始向量。

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.junit.Test;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
/**
 * Created by tingkl on 2017/9/7.
 */
public class AES {
    private static String src = "tingkl security aes";
    static String hexKey;
    static String base64Key;
    static {
        // 生成KEY
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
            keyGenerator.init(new SecureRandom());
            SecretKey secretKey = keyGenerator.generateKey();
            byte[] bytesKey = secretKey.getEncoded();
            // 密鑰是沒有意義的56個字節(jié),如果使用utf-8編碼方式顯示成字符串(new String(result))會是亂碼
            // 所以一般直接顯示密文的16進制字符串
            hexKey = Hex.encodeHexString(bytesKey);
            System.out.println("hexKey:" + hexKey);
            base64Key = Base64.encodeBase64String(bytesKey);
            System.out.println("base64Key:" + base64Key);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }

    public static Key getSecretKey(byte[] bytesKey) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException {
        // KEY轉換
        Key key = new SecretKeySpec(bytesKey, "AES");
        return key;
    }

    public byte[] encrypt(String src, Key secretKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        // 加密
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        byte[] result = cipher.doFinal(src.getBytes());
        return result;
    }

    private String decrypt(byte[] encryptBytesReceived, Key secretKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        // 解密
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        byte[] result = cipher.doFinal(encryptBytesReceived);
        return new String(result);
    }

    @Test
    public void jdkAESHex() throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, DecoderException, InvalidKeySpecException {
        Key secretKeyHex = getSecretKey(Hex.decodeHex(hexKey.toCharArray()));
        // 加密
        byte[] encryptBytesToSend = encrypt(src, secretKeyHex);
        // 加密以后,不能用new String(result)來顯示,因為加密完后是沒有意義的一堆字節(jié),
        // 如果使用魔種編碼方式顯示成字符串,會是亂碼,所以一般直接顯示密文的16進制字符串
        // 編碼
        String hexEncryptBytes = Hex.encodeHexString(encryptBytesToSend);
        System.out.println("aes encrypt:" + hexEncryptBytes);

        // 網(wǎng)絡傳輸時用hex,比如http傳參

        // 解碼
        byte[] encryptBytesReceived = Hex.decodeHex(hexEncryptBytes.toCharArray());
        // 解密
        String result = decrypt(encryptBytesReceived, secretKeyHex);
        System.out.println("aes decrypt:" + result);
    }

    @Test
    public void jdkAESBase64() throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException {
        Key secretKeyBase64 = getSecretKey(Base64.decodeBase64(base64Key));
        byte[] encryptBytesToSend = encrypt(src, secretKeyBase64);
        // 加密以后,不能用new String(result)來顯示,因為加密完后是沒有意義的一堆字節(jié),
        // 如果使用魔種編碼方式顯示成字符串,會是亂碼,所以一般直接顯示密文的16進制字符串
        // 編碼
        String hexEncryptBytes = Base64.encodeBase64String(encryptBytesToSend);
        System.out.println("aes encrypt:" + hexEncryptBytes);

        // 網(wǎng)絡傳輸時,一般用Base64(因為base64編碼是數(shù)據(jù)的4/3,而hex編碼數(shù)據(jù)增長一倍),比如http傳參

        // 解碼
        byte[] encryptBytesReceived = Base64.decodeBase64(hexEncryptBytes);
        // 解密
        // 接受到網(wǎng)絡傳過來的參數(shù),先轉成密文字節(jié)數(shù)組
        String result = decrypt(encryptBytesReceived, secretKeyBase64);
        System.out.println("aes decrypt:" + result);
    }


    @Test
    public void compare() throws BadPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, DecoderException, NoSuchPaddingException, InvalidKeyException, InvalidKeySpecException {
        jdkAESHex();
        jdkAESBase64();
        /*
        網(wǎng)絡傳輸時,一般用Base64(因為base64編碼是數(shù)據(jù)的4/3,而hex編碼數(shù)據(jù)增長一倍),比如http傳參
        hexKey:52a3968b25ba3695657d2930f0b2ee48
        base64Key:UqOWiyW6NpVlfSkw8LLuSA==
        aes encrypt:c26dc0a7eb32b956c8f8631f66dca5c079c22ad11f157cd40ba0abba5b37c860
        aes decrypt:tingkl security aes
        aes encrypt:wm3Ap+syuVbI+GMfZtylwHnCKtEfFXzUC6Cruls3yGA=
        aes decrypt:tingkl security aes
        * */
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容