iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 20
1
Security

看完眼眶濕濕的App開發者慘烈對抗險惡資安環境血與淚的控訴!系列 第 20

Day 20. 對稱式加密演算法 - 大家都愛用的 AES

進階加密標準(英語:Advanced Encryption Standard,縮寫:AES),在密碼學中又稱Rijndael加密法,是美國聯邦政府採用的一種區塊加密標準。

資料來源:Wiki

AES簡介

AES是美國政府公開徵選用來取代 DES 加密法的一種新型加密法,目前已經廣泛用於很多加密標準當中。AES出現原因主要是DES加密法已經被證實為不安全的加密方式,原因有只能一次加密64bit (耗時)、Key僅56bit (不安全)等等。

AES, Advanced Encryption Standard,其實是一套標準:FIPS 197,而我們所說的AES演算法 就是Rijndael演算法

NIST (National INstitute of Standards and Technology) 在1997年9月12日公開徵集更高效更安全的替代DES加密演算法,第一輪共有15種演算法入選,其中5種演算法入圍了決賽,分別是MARS,RC6,Rijndael,Serpent和Twofish。又經過3年的驗證、評測及公眾討論之後Rijndael演算法最終入選。

Rijndael演算法

Rijndael演算法是由比利時學者Joan Daemen和Vincent Rijmen所提出的,演算法的名字就由兩位作者的名字組合而成。Rijndael的優勢在於集安全性、效能、效率、可實現性及靈活性與一體。

加密演算法設計思想

設計準則

  • 混淆 (Confusion) 最大限度地複雜化密文、明文與金鑰之間的關係,通常用非線性變換演算法達到最大化的混淆。
  • 擴散 (Diffusion) 明文或金鑰每變動一位將最大化地影響密文中的位數,通常採用線性變換演算法達到最大化的擴散。

AES評判要求

NIST在徵集演算法的時候就提出了幾項硬性要求:

  • 分組加密演算法:支援128位分組大小,128/192/256位金鑰
  • 安全性不低於3DES,但實施與執行要比3DES的更高效
  • 優化過的ANSI C的實現程式碼
  • KAT(Known-Answer tests)及MCT(Monte Carlo Tests)測試及驗證
  • 軟體及硬體實現的便捷
  • 可抵禦已知攻擊

Rijndael 設計思想

  1. 安全性(Security) 演算法足夠強,抗攻擊
  2. 經濟性(Efficiency) 演算法運算效率高
  3. 金鑰捷變(Key Agility) 更改金鑰所引入的損失儘量小,即最小消耗的金鑰擴充套件演算法
  4. 適應性 (Versatility) 適用於不同的CPU架構,軟體或硬體平臺的實現
  5. 設計簡單(Simplicity) 輪函式的設計精簡,只是多輪迭代

AES 加密流程

將明文 (plan text)二進位資料,分為一塊一塊(block) ,每塊長度跟加密金鑰長度相同(128 / 192 / 256 bit .. )稱為 明文區塊

並透過 AES 演算法,將每一塊明文區塊加密成密文區塊

image-20201005131843648

當然實際上的運作,複雜很多,為了方便理解,簡化成上圖,讓大家理解

金鑰長度

  • AES-128 = 16 bytes
    • 字串 長度16
    • HEX 長度 32 (0-9 A-F)
  • AES-192 = 24 bytes
    • 字串 長度24
    • HEX 長度 48 ( 0-9 A-F)
  • AES-256 = 32 bytes
    • 字串 長度32
    • HEX 長度 64 (0-9 A-F)

使用 AES

Key 長度 128 , ECB 加密模式

iOS Swift

import CryptoSwift

let aes_message = "咩"
let aes_key_HexString = "00000000000000000000000000000000" // aes128
let keyArray = Array<UInt8>.init(hex: aes_key_HexString)
if
    let aes = try? AES(key: keyArray, blockMode: ECB()) , 
    let encrypted = try? aes.encrypt(aes_message.bytes) ,
    let EncData = encrypted.toBase64()
{
    print(EncData)
}

NodeJS / ES6

import CryptoJS from 'crypto-js';

const message = "咩"
var key =  CryptoJS.enc.Hex.parse("00000000000000000000000000000000");
var option={
    mode:CryptoJS.mode.ECB,
};
          
var encrypted = CryptoJS.AES.encrypt(message, key, option);
    
console.log(`key : ${encrypted.key}`)
console.log(`enc : ${encrypted.toString()}`)

Android / JAVA

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
/**
 *
 */
public class AES {

    //Encrypt
    public static String Encrypt(String sSrc, String sKey) throws Exception {
        if (sKey == null) {
            System.out.print("Key is null");
            return null;
        }
        byte[] raw = sKey.getBytes("utf-8");
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(sSrc.getBytes("utf-8"));

        return new Base64().encodeToString(encrypted);
    }

    //Decrypt
    public static String Decrypt(String sSrc, String sKey) throws Exception {
        try {
            if (sKey == null) {
                System.out.print("Key is null");
                return null;
            }
            byte[] raw = sKey.getBytes("utf-8");
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec);
            byte[] encrypted1 = new Base64().decode(sSrc);
            try {
                byte[] original = cipher.doFinal(encrypted1);
                String originalString = new String(original,"utf-8");
                return originalString;
            } catch (Exception e) {
                System.out.println(e.toString());
                return null;
            }
        } catch (Exception ex) {
            System.out.println(ex.toString());
            return null;
        }
    }

    public static void main(String[] args) throws Exception {
        /*
         * 使用AES-128-ECB加密模式,key需要16位
         */
        String cKey = "0000000000000000";
        String cSrc = "咩";
        System.out.println(cSrc);
        //Encrypt
        String enString = AES.Encrypt(cSrc, cKey);
        System.out.println("Encrypt:" + enString);

        //Decrypt
        String DeString = AES.Decrypt(enString, cKey);
        System.out.println("Decrypt:" + DeString);
    }
}

什麼時候採用對稱加密

前面章節有提到 加密有分對稱加密非對稱加密,而非對稱加密還分公鑰和私鑰,那是否後者更加安全一點呢?

沒錯似乎是比較安全,但對非稱式加密也有天生的弱點,極端情況下運算時間比對稱式金鑰慢1000 倍,所以一般不會用來加密大量資料

因此兩種加密方式都有存在的必要,非對稱加密一般不會單獨拿來使用,經常會將對稱加密和非對稱加密兩種技術聯合起來使用

稱為 混合密碼系統(hybrid cryptosystem)

小結

介紹了AES ,歷史緣由,和簡易到不能再簡化的流程圖 是不是更了解了AES 為何物

AES(進階加密標準) 已成為對稱金鑰加密中最流行的演算法之一,所以沒有特別需求考量

對稱式加密,通常都是直接選擇 AES,是不二人選

可能有讀者會說,AES 加密流程才沒這麼單純,沒錯,實際上的流程比我圖片複雜大約有一百倍吧XD

有興趣的可以看AES加密過程

螢幕錄製-2020-10-05-下午7.11.25

看完這個動畫流程圖,似乎好像很厲害,但也有可能幾乎看不懂,但是你又多懂了一點了 XD

話說回來,重點及目的是讓大家容易了解演算法,如果有興趣可以在深入了解運作原理,可以深入研究那是最棒的
最重要的是讓大家怎麼好好認識並如何利用這個工具(演算法),才是最重要的

參考資料

https://zh.wikipedia.org/wiki/%E9%AB%98%E7%BA%A7%E5%8A%A0%E5%AF%86%E6%A0%87%E5%87%86

https://www.itread01.com/content/1541892089.html

https://www.cnblogs.com/arix04/archive/2009/06/26/1511839.html


上一篇
Day 19. 對稱式加密演算法 - DES & 3DES
下一篇
Day 21. 加密演算法要注意的那些毛 (一) - 加密模式
系列文
看完眼眶濕濕的App開發者慘烈對抗險惡資安環境血與淚的控訴!31

尚未有邦友留言

立即登入留言