對稱加密和非對稱加密


一  非對稱加密

  非對稱加密和解密花費的時間長

  非對稱加密算法需要兩個密鑰:公開密鑰(publickey)和私有密鑰(privatekey),一般使用公鑰進行加密,使用私鑰進行解密。

  常見的非對稱加密算法有:RSA、ECC(移動設備用)、Diffie-Hellman、El Gamal、DSA(數字簽名用)

  非對稱加密相比對稱加密的顯著優點在於,對稱加密需要協商密鑰,而非對稱加密可以安全地公開各自的公鑰,在N個人之間通信的時候:使用非對稱加密只需要N個密鑰對,每個人只管理自己的密鑰對。而使用對稱加密需要則需要N*(N-1)/2個密鑰,因此每個人需要         管理N-1個密鑰,密鑰管理難度大,而且非常容易泄漏。

Java標准庫提供了RSA算法的實現,示例代碼如下:

public class TestDemo {
     public static void main(String[] args) throws Exception {
           // 明文:
            byte[] plain = "Hello, encrypt use RSA".getBytes("UTF-8");
            // 創建公鑰/私鑰對:
            Secretkey mctao = new Secretkey("MCTao");
            // 用MCTao的公鑰加密:
            byte[] pk = mctao.getPublicKey();
            System.out.println(String.format("public key: %x", new BigInteger(1, pk)));
            
            byte[] encrypted = mctao.encrypt(plain);
            System.out.println(String.format("encrypted: %x", new BigInteger(1, encrypted)));
            // 用MCTao的私鑰解密:
            byte[] sk = mctao.getPrivateKey();
            System.out.println(String.format("private key: %x", new BigInteger(1, sk)));
            byte[] decode = mctao.decrypt(encrypted);
            System.out.println(new String(decode, "UTF-8"));
        }
         
     
          
}
class Secretkey {
    String name;
    // 私鑰:
    PrivateKey sk;
    // 公鑰:
    PublicKey pk;
    public Secretkey(String name) throws GeneralSecurityException {
        super();
        this.name = name;
        // 生成公鑰/私鑰對:
        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA");
        kpGen.initialize(1024);
        KeyPair kp = kpGen.generateKeyPair();
        this.sk = kp.getPrivate();
        this.pk = kp.getPublic();
    }
      // 把私鑰導出為字節
    public byte[] getPrivateKey() {
        return this.sk.getEncoded();
    }
      // 把公鑰導出為字節
    public byte[] getPublicKey() {
        return this.pk.getEncoded();
    }
    // 用公鑰加密:
    public byte[] encrypt(byte[] message) throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, this.pk);
        return cipher.doFinal(message);
    }

    // 用私鑰解密:
    public byte[] decrypt(byte[] input) throws GeneralSecurityException {
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.DECRYPT_MODE, this.sk);
    return cipher.doFinal(input);
}
}

 RSA的公鑰和私鑰都可以通過getEncoded()方法獲得以byte[]表示的二進制數據,並根據需要保存到文件中。要從byte[]數組恢復公鑰或私鑰,可以這么寫:

byte[] pkData = ...
byte[] skData = ...
KeyFactory kf = KeyFactory.getInstance("RSA");
// 恢復公鑰:
X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(pkData);
PublicKey pk = kf.generatePublic(pkSpec);
// 恢復私鑰:
PKCS8EncodedKeySpec skSpec = new PKCS8EncodedKeySpec(skData);
PrivateKey sk = kf.generatePrivate(skSpec);

以RSA算法為例,它的密鑰有256/512/1024/2048/4096等不同的長度。長度越長,密碼強度越大,當然計算速度也越慢。

如果修改待加密的byte[]數據的大小,可以發現,使用512bit的RSA加密時,明文長度不能超過53字節,使用1024bit的RSA加密時,明文長度不能超過117字節,這也是為什么使用RSA的時候,總是配合AES一起使用,即用AES加密任意長度的明文,用RSA加密AES口令。

此外,只使用非對稱加密算法不能防止中間人攻擊。

二  對稱加密

  算法公開、計算量小、加密速度快、加密效率高,加密和解密使用相同的密鑰加密

    對稱密鑰的管理和分發工作是一件具有潛在危險的和煩瑣的過程。

      常見的對稱加密算法有DES、3DES、Blowfish、IDEA、RC4、RC5、RC6和AES

 

 

使用AES加密

 

AES算法是目前應用最廣泛的加密算法。我們先用ECB模式加密並解密:

public class TestDemo {
    public static void main(String[] args) throws Exception {
        // 原文:
        String message = "Hello, world!";
        System.out.println("Message: " + message);
        // 128位密鑰 = 16 bytes Key:
        byte[] key = "1234567890abcdef".getBytes("UTF-8");
        // 加密:
        byte[] data = message.getBytes("UTF-8");
        byte[] encrypted = encrypt(key, data);
        System.out.println("Encrypted: " + Base64.getEncoder().encodeToString(encrypted));
        // 解密:
        byte[] decrypted = decrypt(key, encrypted);
        System.out.println("Decrypted: " + new String(decrypted, "UTF-8"));
    }

    // 加密:
    public static byte[] encrypt(byte[] key, byte[] input) throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        SecretKey keySpec = new SecretKeySpec(key, "AES");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);
        return cipher.doFinal(input);
    }

    // 解密:
    public static byte[] decrypt(byte[] key, byte[] input) throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        SecretKey keySpec = new SecretKeySpec(key, "AES");
        cipher.init(Cipher.DECRYPT_MODE, keySpec);
        return cipher.doFinal(input);
    }
}

Java標准庫提供的對稱加密接口非常簡單,使用時按以下步驟編寫代碼:

  1. 根據算法名稱/工作模式/填充模式獲取Cipher實例;
  2. 根據算法名稱初始化一個SecretKey實例,密鑰必須是指定長度;
  3. 使用SerectKey初始化Cipher實例,並設置加密或解密模式;
  4. 傳入明文或密文,獲得密文或明文。

ECB模式是最簡單的AES加密模式,它只需要一個固定長度的密鑰,固定的明文會生成固定的密文,這種一對一的加密方式會導致安全性降低,更好的方式是通過CBC模式,它需要一個隨機數作為IV參數,這樣對於同一份明文,每次生成的密文都不同:

public class TestDemo {
    public static void main(String[] args) throws Exception {
        // 原文:
        String message = "Hello, world!";
        System.out.println("Message: " + message);
        // 256位密鑰 = 32 bytes Key:
        byte[] key = "1234567890abcdef1234567890abcdef".getBytes("UTF-8");
        // 加密:
        byte[] data = message.getBytes("UTF-8");
        byte[] encrypted = encrypt(key, data);
        System.out.println("Encrypted: " + Base64.getEncoder().encodeToString(encrypted));
        // 解密:
        byte[] decrypted = decrypt(key, encrypted);
        System.out.println("Decrypted: " + new String(decrypted, "UTF-8"));
    }

    // 加密:
    public static byte[] encrypt(byte[] key, byte[] input) throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        // CBC模式需要生成一個16 bytes的initialization vector:
        SecureRandom sr = SecureRandom.getInstanceStrong();
        byte[] iv = sr.generateSeed(16);
        IvParameterSpec ivps = new IvParameterSpec(iv);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivps);
        byte[] data = cipher.doFinal(input);
        // IV不需要保密,把IV和密文一起返回:
        return join(iv, data);
    }

    // 解密:
    public static byte[] decrypt(byte[] key, byte[] input) throws GeneralSecurityException {
        // 把input分割成IV和密文:
        byte[] iv = new byte[16];
        byte[] data = new byte[input.length - 16];
        System.arraycopy(input, 0, iv, 0, 16);
        System.arraycopy(input, 16, data, 0, data.length);
        // 解密:
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        IvParameterSpec ivps = new IvParameterSpec(iv);
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivps);
        return cipher.doFinal(data);
    }

    public static byte[] join(byte[] bs1, byte[] bs2) {
        byte[] r = new byte[bs1.length + bs2.length];
        System.arraycopy(bs1, 0, r, 0, bs1.length);
        System.arraycopy(bs2, 0, r, bs1.length, bs2.length);
        return r;
    }
}

 

 

附: 標准國密算法

國密即國家密碼局認定的國產密碼算法,即商用密碼。

1 SM1對稱密碼

SM1 算法是分組密碼算法,分組長度為128位,密鑰長度都為 128 比特,算法安全保密強度及相關軟硬件實現性能與 AES 相當,算法不公開,僅以IP核的形式存在於芯片中。
采用該算法已經研制了系列芯片、智能IC卡、智能密碼鑰匙、加密卡、加密機等安全產品,廣泛應用於電子政務、電子商務及國民經濟的各個應用領域(包括國家政務通、警務通等重要領域)。

2 SM2橢圓曲線公鑰密碼算法

SM2算法就是ECC橢圓曲線密碼機制,但在簽名、密鑰交換方面不同於ECDSA、ECDH等國際標准,而是采取了更為安全的機制。另外,SM2推薦了一條256位的曲線作為標准曲線。
SM2標准包括總則,數字簽名算法,密鑰交換協議,公鑰加密算法四個部分,並在每個部分的附錄詳細說明了實現的相關細節及示例。
SM2算法主要考慮素域Fp和F2m上的橢圓曲線,分別介紹了這兩類域的表示,運算,以及域上的橢圓曲線的點的表示,運算和多倍點計算算法。然后介紹了編程語言中的數據轉換,包括整數和字節串,字節串和比特串,域元素和比特串,域元素和整數,點和字節串之間的數據轉換規則。詳細說明了有限域上橢圓曲線的參數生成以及驗證,橢圓曲線的參數包括有限域的選取、橢圓曲線方程參數、橢圓曲線群基點的選取等,並給出了選取的標准以便於驗證。最后給橢圓曲線上密鑰對的生成以及公鑰的驗證,用戶的密鑰對為(s,sP),其中s為用戶的私鑰,sP為用戶的公鑰,由於離散對數問題從sP難以得到s,並針對素域和二元擴域給出了密鑰對生成細節和驗證方式。總則中的知識也適用於SM9算法。
在總則的基礎上給出了數字簽名算法(包括數字簽名生成算法和驗證算法),密鑰交換協議以及公鑰加密算法(包括加密算法和解密算法),並在每個部分給出了算法描述,算法流程和相關示例。
數字簽名算法、密鑰交換協議以及公鑰加密算法都使用了國家密管理局批准的SM3密碼雜湊算法和隨機數發生器。數字簽名算法、密鑰交換協議以及公鑰加密算法根據總則來選取有限域和橢圓曲線,並生成密鑰對。

SM2算法在很多方面都優於RSA算法。

3 SM3雜湊算法

SM3密碼雜湊(哈希、散列)算法給出了雜湊函數算法的計算方法和計算步驟,並給出了運算示例。此算法適用於商用密碼應用中的數字簽名和驗證,消息認證碼的生成與驗證以及隨機數的生成,可滿足多種密碼應用的安全需求。在SM2,SM9標准中使用。
此算法對輸入長度小於2的64次方的比特消息,經過填充和迭代壓縮,生成長度為256比特的雜湊值,其中使用了異或,模,模加,移位,與,或,非運算,由填充,迭代過程,消息擴展和壓縮函數所構成。具體算法及運算示例見SM3標准。

4 SM4對稱算法

此算法是一個分組算法,用於無線局域網產品。該算法的分組長度為128比特,密鑰長度為128比特。加密算法與密鑰擴展算法都采用32輪非線性迭代結構。解密算法與加密算法的結構相同,只是輪密鑰的使用順序相反,解密輪密鑰是加密輪密鑰的逆序。
此算法采用非線性迭代結構,每次迭代由一個輪函數給出,其中輪函數由一個非線性變換和線性變換復合而成,非線性變換由S盒所給出。其中rki為輪密鑰,合成置換T組成輪函數。輪密鑰的產生與上圖流程類似,由加密密鑰作為輸入生成,輪函數中的線性變換不同,還有些參數的區別。SM4算法的具體描述和示例見SM4標准。

SM7對稱密碼

SM7算法,是一種分組密碼算法,分組長度為128比特,密鑰長度為128比特。SM7適用於非接觸式IC卡,應用包括身份識別類應用(門禁卡、工作證、參賽證),票務類應用(大型賽事門票、展會門票),支付與通卡類應用(積分消費卡、校園一卡通、企業一卡通等)。

SM9標識密碼算法

為了降低公開密鑰系統中密鑰和證書管理的復雜性,以色列科學家、RSA算法發明人之一Adi Shamir在1984年提出了標識密碼(Identity-Based Cryptography)的理念。標識密碼將用戶的標識(如郵件地址、手機號碼、QQ號碼等)作為公鑰,省略了交換數字證書和公鑰過程,使得安全系統變得易於部署和管理,非常適合端對端離線安全通訊、雲端數據加密、基於屬性加密、基於策略加密的各種場合。2008年標識密碼算法正式獲得國家密碼管理局頒發的商密算法型號:SM9(商密九號算法),為我國標識密碼技術的應用奠定了堅實的基礎。
SM9算法不需要申請數字證書,適用於互聯網應用的各種新興應用的安全保障。如基於雲技術的密碼服務、電子郵件安全、智能終端保護、物聯網安全、雲存儲安全等等。這些安全應用可采用手機號碼或郵件地址作為公鑰,實現數據加密、身份認證、通話加密、通道加密等安全應用,並具有使用方便,易於部署的特點,從而開啟了普及密碼算法的大門。

ZUC祖沖之算法

祖沖之序列密碼算法是中國自主研究的流密碼算法,是運用於移動通信4G網絡中的國際標准密碼算法,該算法包括祖沖之算法(ZUC)、加密算法(128-EEA3)和完整性算法(128-EIA3)三個部分。目前已有對ZUC算法的優化實現,有專門針對128-EEA3和128-EIA3的硬件實現與優化。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM