天天看點

AES算法,DES算法,RSA算法JAVA實作

1     AES算法

1.1    算法描述

1.1.1      設計思想

Rijndael密碼的設計力求滿足以下3條标準:

① 抵抗所有已知的攻擊。

② 在多個平台上速度快,編碼緊湊。

③ 設計簡單。

目前的大多數分組密碼,其輪函數是Feistel結構。

Rijndael沒有這種結構。

Rijndael輪函數是由3個不同的可逆均勻變換

1.1.2      密碼說明

嚴格地說,AES和Rijndael加密法并不完全一樣(雖然在實際應用中二者可以互換),因為Rijndael加密法可以支援更大範圍的區塊和密鑰長度:AES的區塊長度固定為128 比特,密鑰長度則可以是128,192或256比特;而Rijndael使用的密鑰和區塊長度可以是32位的整數倍,以128位為下限,256比特為上限。加密過程中使用的密鑰是由Rijndael密鑰生成方案産生。

大多數AES計算是在一個特别的有限域完成的。

AES加密過程是在一個4×4的位元組矩陣上運作,這個矩陣又稱為“狀态(state)”,其初值就是一個明文區塊(矩陣中一個元素大小就是明文區塊中的一個Byte)。(Rijndael加密法因支援更大的區塊,其矩陣行數可視情況增加)加密時,各輪AES加密循環(除最後一輪外)均包含4個步驟:

  1. AddRoundKey — 矩陣中的每一個位元組都與該次輪秘鑰(round key)做XOR運算;每個子密鑰由密鑰生成方案産生。
  2. SubBytes — 通過非線性的替換函數,用查找表的方式把每個位元組替換成對應的位元組。
  3. ShiftRows — 将矩陣中的每個橫列進行循環式移位。
  4. MixColumns — 為了充分混合矩陣中各個直行的操作。這個步驟使用線性轉換來混合每列的四個位元組。
  5. 最後一個加密循環中省略MixColumns步驟,而以另一個AddRoundKey取代。

1.1.3      加密标準

對稱密碼體制的發展趨勢将以分組密碼為重點。分組密碼算法通常由密鑰擴充算法和加密(解密)算法兩部分組成。密鑰擴充算法将b位元組使用者主密鑰擴充成r個子密鑰。加密算法由一個密碼學上的弱函數f與r個子密鑰疊代r次組成。混亂和密鑰擴散是分組密碼算法設計的基本原則。抵禦已知明文的差分和線性攻擊,可變長密鑰和分組是該體制的設計要點。

AES是美國國家标準技術研究所NIST旨在取代DES的21世紀的加密标準。

AES的基本要求是,采用對稱分組密碼體制,密鑰的長度最少支援為128、192、256,分組長度128位,算法應易于各種硬體和軟體實作。1998年NIST開始AES第一輪分析、測試和征集,共産生了15個候選算法。1999年3月完成了第二輪AES2的分析、測試。2000年10月2日美國政府正式宣布選中比利時密碼學家Joan Daemen 和 Vincent Rijmen 提出的一種密碼算法RIJNDAEL 作為 AES.

在應用方面,盡管DES在安全上是脆弱的,但由于快速DES晶片的大量生産,使得DES仍能暫時繼續使用,為提高安全強度,通常使用獨立密鑰的三級DES。但是DES遲早要被AES代替。流密碼體制較之分組密碼在理論上成熟且安全,但未被列入下一代加密标準。

AES加密資料塊分組長度必須為128比特,密鑰長度可以是128比特、192比特、256比特中的任意一個(如果資料塊及密鑰長度不足時,會補齊)。AES加密有很多輪的重複和變換。大緻步驟如下:1、密鑰擴充(KeyExpansion),2、初始輪(Initial Round),3、重複輪(Rounds),每一輪又包括:SubBytes、ShiftRows、MixColumns、AddRoundKey,4、最終輪(Final Round),最終輪沒有MixColumns。

1.2    算法實作

1.2.1      算法實作代碼(java)

import java.io.*;

import java.security.*;

import java.util.Scanner;

import javax.crypto.*;

import javax.crypto.spec.SecretKeySpec;

public class aes {

         //加密

         public static byte[] encrypt(String content, String password) { 

        try {            

                KeyGenerator kgen = KeyGenerator.getInstance("AES"); 

                kgen.init(128, new SecureRandom(password.getBytes())); 

                SecretKey secretKey = kgen.generateKey(); 

                byte[] enCodeFormat = secretKey.getEncoded(); 

                SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES"); 

                Cipher cipher = Cipher.getInstance("AES");// 建立密碼器 

                byte[] byteContent = content.getBytes("utf-8"); 

                cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化 

                byte[] result = cipher.doFinal(byteContent); 

                return result; // 加密 

        } catch (NoSuchAlgorithmException e) { 

                e.printStackTrace(); 

        } catch (NoSuchPaddingException e) { 

        } catch (InvalidKeyException e) { 

        } catch (UnsupportedEncodingException e) { 

        } catch (IllegalBlockSizeException e) { 

        } catch (BadPaddingException e) { 

        } 

        return null; 

         //解密

         public static byte[] decrypt(byte[] content, String password) { 

        try { 

                 KeyGenerator kgen = KeyGenerator.getInstance("AES"); 

                 kgen.init(128, new SecureRandom(password.getBytes())); 

                 SecretKey secretKey = kgen.generateKey(); 

                 byte[] enCodeFormat = secretKey.getEncoded(); 

                 SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");             

                 Cipher cipher = Cipher.getInstance("AES");// 建立密碼器 

                cipher.init(Cipher.DECRYPT_MODE, key);// 初始化 

                byte[] result = cipher.doFinal(content); 

//主函數

         public static void main(String[] args) {

                   Scanner scan = new Scanner(System.in); 

        System.out.println("請輸入一個字元串:"); 

        String s=scan.next();

        System.out.println("你輸入的是:"+s);

                   String password = "12345678"; 

                   //加密 

                   System.out.println("加密前:" + s); 

                   byte[] encryptResult = encrypt(s, password); 

                   //解密 

                   byte[] decryptResult = decrypt(encryptResult,password); 

                   System.out.println("解密後:" + new String(decryptResult));

         }

}

2     DES算法

2.1    算法描述

2.1.1      基本原則

DES設計中使用了分組密碼設計的兩個原則:混淆(confusion)和擴散(diffusion),其目的是抗擊敵手對密碼系統的統計分析。混淆是使密文的統計特性與密鑰的取值之間的關系盡可能複雜化,以使密鑰和明文以及密文之間的依賴性對密碼分析者來說是無法利用的。擴散的作用就是将每一位明文的影響盡可能迅速地作用到較多的輸出密文位中,以便在大量的密文中消除明文的統計結構,并且使每一位密鑰的影響盡可能迅速地擴充到較多的密文位中,以防對密鑰進行逐段破譯。

2.1.2      算法步驟

DES算法把64位的明文輸入塊變為64位的密文輸出塊,它所使用的密鑰也是64位(實際用到了56位,第8、

DES算法流程

16、24、32、40、48、56、64位是校驗位, 使得每個密鑰都有奇數個1),其算法主要分為兩步:

1)初始置換

其功能是把輸入的64位資料塊按位重新組合,并把輸出分為L0、R0兩部分,每部分各長32位,其置換規則為将輸入的第58位換到第一位,第50位換到第2位……依此類推,最後一位是原來的第7位。L0、R0則是換位輸出後的兩部分,L0是輸出的左32位,R0是右32位,例:設定換前的輸入值為D1D2D3……D64,則經過初始置換後的結果為:L0=D58D50……D8;R0=D57D49……D7。

其置換規則見下表:

58,50,42,34,26,18,10,2,

60,52,44,36,28,20,12,4,

62,54,46,38,30,22,14,6,

64,56,48,40,32,24,16,8,

57,49,41,33,25,17,9,1,

59,51,43,35,27,19,11,3,

61,53,45,37,29,21,13,5,

63,55,47,39,31,23,15,7,

2)逆置換

經過16次疊代運算後,得到L16、R16,将此作為輸入,進行逆置換,逆置換正好是初始置換的逆運算,由此即得到密文輸出。

此算法是對稱加密算法體系中的代表,在計算機網絡系統中廣泛使用。

2.2    算法實作

2.2.1      算法實作代碼(java)

import javax.crypto.spec.DESKeySpec;

public class des {

         public des() {

         //測試主函數

         public static void main(String args[]) {

    Scanner scan = new Scanner(System.in); 

    System.out.println("請輸入一個字元串:"); 

    String s=scan.next();

    System.out.println("你輸入的是:"+s);

         String password = "9588028820109132";

         byte[] result = des.encrypt(s.getBytes(),password);

         System.out.println("加密後:"+new String(result));

         try {

         byte[] decryResult = des.decrypt(result, password);

         System.out.println("解密後:"+new String(decryResult));

         } catch (Exception e1) {

         e1.printStackTrace();

         public static byte[] encrypt(byte[] datasource, String password) {

         try{

         SecureRandom random = new SecureRandom();

         DESKeySpec desKey = new DESKeySpec(password.getBytes());

         //建立一個密匙工廠,然後用它把DESKeySpec轉換成

         SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");

         SecretKey securekey = keyFactory.generateSecret(desKey);

         //Cipher對象實際完成加密操作

         Cipher cipher = Cipher.getInstance("DES");

         //用密匙初始化Cipher對象

         cipher.init(Cipher.ENCRYPT_MODE, securekey, random);

         //現在,擷取資料并加密

         //正式執行加密操作

         return cipher.doFinal(datasource);

         }catch(Throwable e){

         e.printStackTrace();

         return null;

  //解密

   public static byte[] decrypt(byte[] src, String password) throws Exception {

         // DES算法要求有一個可信任的随機數源

         // 建立一個DESKeySpec對象

         // 建立一個密匙工廠

         // 将DESKeySpec對象轉換成SecretKey對象

         // Cipher對象實際完成解密操作

         // 用密匙初始化Cipher對象

         cipher.init(Cipher.DECRYPT_MODE, securekey, random);

         // 真正開始解密操作

         return cipher.doFinal(src);

3   RSA算法

3.1    算法描述

3.1.1      實作細節

密鑰生成

首先要使用機率算法來驗證随機産生的大的整數是否質數,這樣的算法比較快而且可以消除掉大多數非質數。假如有一個數通過了這個測試的話,那麼要使用一個精确的測試來保證它的确是一個質數。

除此之外這樣找到的p和q還要滿足一定的要求,首先它們不能太靠近,此外p-1或q-1的因子不能太小,否則的話N也可以被很快地分解。

此外尋找質數的算法不能給攻擊者任何資訊,這些質數是怎樣找到的,尤其産生随機數的軟體必須非常好。要求是随機和不可預測。這兩個要求并不相同。一個随機過程可能可以産生一個不相關的數的系列,但假如有人能夠預測出(或部分地預測出)這個系列的話,那麼它就已經不可靠了。比如有一些非常好的随機數算法,但它們都已經被發表,是以它們不能被使用,因為假如一個攻擊者可以猜出p和q一半的位的話,那麼他們就已經可以輕而易舉地推算出另一半。

此外密鑰d必須足夠大,1990年有人證明假如p大于q而小于2q(這是一個很經常的情況)而,那麼從N和e可以很有效地推算出d。此外e = 2永遠不應該被使用。

運算速度

由于進行的都是大數計算,使得RSA最快的情況也比DES慢上好幾倍,無論是軟體還是硬體實作。速度一直是RSA的缺陷。一般來說隻用于少量資料加密。RSA的速度比對應同樣安全級别的對稱密碼算法要慢1000倍左右。

比起DES和其它對稱算法來說,RSA要慢得多。實際上Bob一般使用一種對稱算法來加密他的資訊,然後用RSA來加密他的比較短的對稱密碼,然後将用RSA加密的對稱密碼和用對稱算法加密的消息送給Alice。

這樣一來對随機數的要求就更高了,尤其對産生對稱密碼的要求非常高,因為否則的話可以越過RSA來直接攻擊對稱密碼。

密鑰配置設定

和其它加密過程一樣,對RSA來說配置設定公鑰的過程是非常重要的。配置設定公鑰的過程必須能夠抵擋一個從中取代的攻擊。假設Eve交給Bob一個公鑰,并使Bob相信這是Alice的公鑰,并且她可以截下Alice和Bob之間的資訊傳遞,那麼她可以将她自己的公鑰傳給Bob,Bob以為這是Alice的公鑰。Eve可以将所有Bob傳遞給Alice的消息截下來,将這個消息用她自己的密鑰解密,讀這個消息,然後将這個消息再用Alice的公鑰加密後傳給Alice。理論上Alice和Bob都不會發現Eve在偷聽他們的消息。今天人們一般用數字認證來防止這樣的攻擊。

時間攻擊

1995年有人提出了一種非常意想不到的攻擊方式:假如Eve對Alice的硬體有充分的了解,而且知道它對一些特定的消息加密時所需要的時間的話,那麼她可以很快地推導出d。這種攻擊方式之是以會成立,主要是因為在進行加密時所進行的模指數運算是一個位元一個位元進行的而位元為1所花的運算比位元為0的運算要多很多,是以若能得到多組訊息與其加密時間,就會有機會可以反推出私鑰的内容。

3.1.2      安全性

RSA的安全性依賴于大數分解,但是否等同于大數分解一直未能得到理論上的證明,因為沒有證明破解RSA就一定需要作大數分解。假設存在一種無須分解大數的算法,那它肯定可以修改成為大數分解算法。 RSA 的一些變種算法已被證明等價于大數分解。不管怎樣,分解n是最顯然的攻擊方法。人們已能分解多個十進制位的大素數。是以,模數n必須選大一些,因具體适用情況而定。

3.2    算法實作

3.2.1      算法實作代碼(java)

import java.security.*; 

import java.security.interfaces.*; 

import java.io.*; 

import java.math.*;

public class rsa { 

        public rsa() { 

        public static void generateKey() { 

            try { 

                KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); 

                kpg.initialize(1024); 

                KeyPair kp = kpg.genKeyPair(); 

                PublicKey pbkey = kp.getPublic(); 

                PrivateKey prkey = kp.getPrivate(); 

                // 儲存公鑰 

                FileOutputStream f1 = new FileOutputStream("pubkey.dat"); 

                ObjectOutputStream b1 = new ObjectOutputStream(f1); 

                b1.writeObject(pbkey); 

                // 儲存私鑰 

                FileOutputStream f2 = new FileOutputStream("privatekey.dat"); 

                ObjectOutputStream b2 = new ObjectOutputStream(f2); 

                b2.writeObject(prkey); 

            } catch (Exception e) { 

            } 

        public static void encrypt() throws Exception { 

            Scanner scan = new Scanner(System.in); 

            System.out.println("請輸入一個字元串:"); 

            //System.out.println("您輸入的字元串是:" + scan.next());

            String s=scan.next();

            System.out.println("你輸入的是:"+s);

            // 擷取公鑰及參數e,n 

            FileInputStream f = new FileInputStream("pubkey.dat"); 

            ObjectInputStream b = new ObjectInputStream(f); 

            RSAPublicKey pbk = (RSAPublicKey) b.readObject(); 

            BigInteger e = pbk.getPublicExponent(); 

            BigInteger n = pbk.getModulus(); 

            System.out.println("e= " + e); 

            System.out.println("n= " + n); 

            // 擷取明文m 

            byte ptext[] = s.getBytes("UTF-8"); 

            BigInteger m = new BigInteger(ptext); 

            // 計算密文c 

            BigInteger c = m.modPow(e, n); 

            System.out.println("c= " + c); 

            // 儲存密文 

            String cs = c.toString(); 

            BufferedWriter out = 

                new BufferedWriter( 

                    new OutputStreamWriter(new FileOutputStream("encrypt.dat"))); 

            out.write(cs, 0, cs.length()); 

            out.close(); 

        public static void decrypt() throws Exception { 

            // 讀取密文 

            BufferedReader in = 

                new BufferedReader( 

                    new InputStreamReader(new FileInputStream("encrypt.dat"))); 

            String ctext = in.readLine(); 

            BigInteger c = new BigInteger(ctext); 

            // 讀取私鑰 

            FileInputStream f = new FileInputStream("privatekey.dat"); 

            RSAPrivateKey prk = (RSAPrivateKey) b.readObject(); 

            BigInteger d = prk.getPrivateExponent(); 

            // 擷取私鑰參數及解密 

            BigInteger n = prk.getModulus(); 

            System.out.println("d= " + d); 

            BigInteger m = c.modPow(d, n); 

            // 顯示解密結果 

            System.out.println("m= " + m); 

            byte[] mt = m.toByteArray(); 

            System.out.println("解密出來的文本是:"); 

            for (int i = 0; i < mt.length; i++) { 

                System.out.print((char) mt[i]); 

        public static void main(String args[]) { 

                generateKey(); 

                encrypt(); 

                decrypt(); 

                System.out.println(e.toString());