1. 程式人生 > >DES對稱加密算法簡析

DES對稱加密算法簡析

nts 奇偶校驗位 throw parse https stl public aes ast

1 對稱加密算法

  在了解DES算法前,先加單介紹一下對稱加密算法,因為DES屬於對稱加密算法的一種。

  對稱加密算法是應用較早的加密算法,技術成熟。在對稱加密算法中,數據發信方將明文(原始數據)和加密密鑰(mi yao)一起經過特殊加密算法處理後,使其變成復雜的加密密文發送出去。收信方收到密文後,若想解讀原文,則需要使用加密用過的密鑰及相同算法的逆算法對密文進行解密,才能使其恢復成可讀明文。在對稱加密算法中,使用的密鑰只有一個,發收信雙方都使用這個密鑰對數據進行加密和解密,這就要求解密方事先必須知道加密密鑰。

  原理如下圖:

  技術分享圖片

2 DES算法簡介

2.1 概述

  DES全稱為Data Encryption Standard,即數據加密標準,是一種使用密鑰加密的塊算法,1977年被美國聯邦政府的國家標準局確定為聯邦資料處理標準(FIPS),並授權在非密級政府通信中使用,隨後該算法在國際上廣泛流傳開來。需要註意的是,在某些文獻中,作為算法的DES稱為數據加密算法(Data Encryption Algorithm,DEA),已與作為標準的DES區分開來。

2.2 發展歷史

  美國國家標準局1973年開始研究除國防部外的其它部門的計算機系統的數據加密標準,於1973年5月15日和1974年8月27日先後兩次向公眾發出了征求加密算法的公告。加密算法要達到的目的(通常稱為DES 密碼算法要求)主要為以下四點:

  ☆提供高質量的數據保護,防止數據未經授權的泄露和未被察覺的修改;

  ☆具有相當高的復雜性,使得破譯的開銷超過可能獲得的利益,同時又要便於理解和掌握;

  ☆DES密碼體制的安全性應該不依賴於算法的保密,其安全性僅以加密密鑰的保密為基礎;

  ☆實現經濟,運行有效,並且適用於多種完全不同的應用。

  1977年1月,美國政府頒布:采納IBM公司設計的方案作為非機密數據的正式數據加密標準(DES Data Encryption Standard)。

2.3 算法原理

  DES 使用一個 56 位的密鑰以及附加的 8 位奇偶校驗位,產生最大 64 位的分組大小。這是一個叠代的分組密碼,使用稱為 Feistel 的技術,其中將加密的文本塊分成兩半。使用子密鑰對其中一半應用循環功能,然後將輸出與另一半進行“異或”運算;接著交換這兩半,這一過程會繼續下去,但最後一個循環不交換。DES 使用 16 個循環,使用異或,置換,代換,移位操作四種基本運算。

  DES算法的入口參數有三個:Key、Data、Mode。

  Key為8個字節共64位,是DES算法的工作密鑰;

  Data也為8個字節64位,是要被加密或被解密的數據;

  Mode為DES的工作方式,有兩種:加密或解密。

  DES算法是這樣工作的:

  如Mode為加密,則用Key 去把數據Data進行加密, 生成Data的密碼形式(64位)作為DES的輸出結果;

  如Mode為解密,則用Key去把密碼形式的數據Data解密,還原為Data的明碼形式(64位)作為DES的輸出結果。

  在通信網絡的兩端,雙方約定一致的Key,在通信的源點用Key對核心數據進行DES加密,然後以密碼形式在公共通信網(如電話網)中傳輸到通信網絡的終點,數據到達目的地後,用同樣的Key對密碼數據進行解密,便再現了明碼形式的核心數據。這樣,便保證了核心數據(如PIN、MAC等)在公共通信網中傳輸的安全性和可靠性。

  通過定期在通信網絡的源端和目的端同時改用新的Key,便能更進一步提高數據的保密性,這正是現在金融交易網絡的流行做法。

2.4 應用

  目前在國內,隨著三金工程尤其是金卡工程的啟動,DES算法在POS、ATM、磁卡及智能卡(IC卡)、加油站、高速公路收費站等領域被廣泛應用,以此來實現關鍵數據的保密,如信用卡持卡人的PIN的加密傳輸,IC卡與POS間的雙向認證、金融交易數據包的MAC校驗等,均用到DES算法。

2.5 java代碼實現

  1 package xin.dreaming.des;
  2 
  3 import java.security.SecureRandom;
  4 import java.util.Arrays;
  5 
  6 import javax.crypto.Cipher;
  7 import javax.crypto.SecretKey;
  8 import javax.crypto.SecretKeyFactory;
  9 import javax.crypto.spec.DESKeySpec;
 10 import javax.crypto.spec.IvParameterSpec;
 11 
 12 import org.junit.Test;
 13 /**
 14  * 
 15  * @author DREAMING.XIN
 16  *
 17  */
 18 public class DESUtils {
 19     
 20     /**
 21      * 生成隨機密鑰
 22      * 
 23      * @param size
 24      *            位數
 25      * @return
 26      */
 27     public static String generateRandomKey(int size) {
 28         StringBuilder key = new StringBuilder();
 29         String chars = "0123456789ABCDEF";
 30         for (int i = 0; i < size; i++) {
 31             int index = (int) (Math.random() * (chars.length() - 1));
 32             key.append(chars.charAt(index));
 33         }
 34         return key.toString();
 35     }
 36 
 37     /**
 38      * DES加密
 39      * 
 40      * @param key
 41      *            密鑰信息
 42      * @param content
 43      *            待加密信息
 44      * @return
 45      * @throws Exception
 46      */
 47     public static byte[] encodeDES(byte[] key, byte[] content) throws Exception {
 48         // 不是8的倍數的,補足
 49         if (key.length % 8 != 0) {
 50             int groups = key.length / 8 + (key.length % 8 != 0 ? 1 : 0);
 51             byte[] temp = new byte[groups * 8];
 52             Arrays.fill(temp, (byte) 0);
 53             System.arraycopy(key, 0, temp, 0, key.length);
 54             key = temp;
 55         }
 56 
 57         // 不是8的倍數的,補足
 58         byte[] srcBytes = content;
 59         if (srcBytes.length % 8 != 0) {
 60             int groups = content.length / 8 + (content.length % 8 != 0 ? 1 : 0);
 61             srcBytes = new byte[groups * 8];
 62             Arrays.fill(srcBytes, (byte) 0);
 63             System.arraycopy(content, 0, srcBytes, 0, content.length);
 64         }
 65 
 66         IvParameterSpec iv = new IvParameterSpec(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 });
 67         SecureRandom sr = new SecureRandom();
 68         DESKeySpec dks = new DESKeySpec(key);
 69         SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
 70         SecretKey secretKey = keyFactory.generateSecret(dks);
 71         Cipher cipher = Cipher.getInstance("DES/CBC/NoPadding");
 72         cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv, sr);
 73         byte[] tgtBytes = cipher.doFinal(srcBytes);
 74         return tgtBytes;
 75     }
 76 
 77     /**
 78      * DES解密
 79      * 
 80      * @param key
 81      *            密鑰信息
 82      * @param content
 83      *            待加密信息
 84      * @return
 85      * @throws Exception
 86      */
 87     public static byte[] decodeDES(byte[] key, byte[] content) throws Exception {
 88         // 不是8的倍數的,補足
 89         if (key.length % 8 != 0) {
 90             int groups = key.length / 8 + (key.length % 8 != 0 ? 1 : 0);
 91             byte[] temp = new byte[groups * 8];
 92             Arrays.fill(temp, (byte) 0);
 93             System.arraycopy(key, 0, temp, 0, key.length);
 94             key = temp;
 95         }
 96         // 不是8的倍數的,補足
 97         byte[] srcBytes = content;
 98         if (srcBytes.length % 8 != 0) {
 99             int groups = content.length / 8 + (content.length % 8 != 0 ? 1 : 0);
100             srcBytes = new byte[groups * 8];
101             Arrays.fill(srcBytes, (byte) 0);
102             System.arraycopy(content, 0, srcBytes, 0, content.length);
103         }
104 
105         IvParameterSpec iv = new IvParameterSpec(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 });
106         SecureRandom sr = new SecureRandom();
107         DESKeySpec dks = new DESKeySpec(key);
108         SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
109         SecretKey secretKey = keyFactory.generateSecret(dks);
110         Cipher cipher = Cipher.getInstance("DES/CBC/NoPadding");
111         cipher.init(Cipher.DECRYPT_MODE, secretKey, iv, sr);
112         byte[] tgtBytes = cipher.doFinal(content);
113         return tgtBytes;
114     }
115     
116     @Test
117     public void desTest() throws Exception{
118         //獲取隨機密鑰
119         String key = generateRandomKey(16);
120         System.out.println("隨機密鑰:"+key);
121         String str = "DREAMING.XIN";
122         //des加密
123         byte[] encodeDES = encodeDES(key.getBytes(), str.getBytes());
124         System.out.println("加密結果:"+encodeDES);
125         
126         //des解密
127         byte[] decodeDES = decodeDES(key.getBytes(), encodeDES);
128         System.out.println("減密結果: "+new String(decodeDES));
129     }
130 
131 }

執行結果:

  隨機密鑰:AB09C55631425D67
  加密結果:[B@19fc4e
  減密結果: DREAMING.XIN

3 3DES加密算法

3.1 概述

  3DES又稱Triple DES,是DES加密算法的一種模式,它使用3條56位的密鑰對數據進行三次加密。數據加密標準(DES)是美國的一種由來已久的加密標準,它使用對稱密鑰加密法,並於1981年被ANSI組織規範為ANSI X.3.92。DES使用56位密鑰和密碼塊的方法,而在密碼塊的方法中,文本被分成64位大小的文本塊然後再進行加密。比起最初的DES,3DES更為安全。   3DES(即Triple DES)是DES向AES過渡的加密算法(1999年,NIST將3-DES指定為過渡的加密標準),加密算法,其具體實現如下:設Ek()和Dk()代表DES算法的加密和解密過程,K代表DES算法使用的密鑰,M代表明文,C代表密文,這樣:   3DES加密過程為:C=Ek3(Dk2(Ek1(M)))   3DES解密過程為:M=Dk1(EK2(Dk3(C)))

3.2 java代碼實現

  1 package xin.dreaming.des;
  2 
  3 import java.security.Security;
  4 import java.util.Arrays;
  5 
  6 import javax.crypto.Cipher;
  7 import javax.crypto.SecretKey;
  8 import javax.crypto.spec.SecretKeySpec;
  9 
 10 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 11 import org.junit.Test;
 12 
 13 public class TripleDESUtils {
 14 
 15     /**
 16      * 生成隨機密鑰
 17      * 
 18      * @param size
 19      *            位數
 20      * @return
 21      */
 22     public static String generateRandomKey(int size) {
 23         StringBuilder key = new StringBuilder();
 24         String chars = "0123456789ABCDEF";
 25         for (int i = 0; i < size; i++) {
 26             int index = (int) (Math.random() * (chars.length() - 1));
 27             key.append(chars.charAt(index));
 28         }
 29         return key.toString();
 30     }
 31 
 32     /**
 33      * 3DES加密
 34      * 
 35      * @param key
 36      *            密鑰信息
 37      * @param content
 38      *            待加密信息
 39      * @return
 40      * @throws Exception
 41      */
 42     public static byte[] encode3DES(byte[] key, byte[] content) throws Exception {
 43         Security.addProvider(new BouncyCastleProvider());
 44         // 不是8的倍數的,補足
 45         if (key.length % 8 != 0) {
 46             int groups = key.length / 8 + (key.length % 8 != 0 ? 1 : 0);
 47             byte[] temp = new byte[groups * 8];
 48             Arrays.fill(temp, (byte) 0);
 49             System.arraycopy(key, 0, temp, 0, key.length);
 50             key = temp;
 51         }
 52         // 長度為16位,轉換成24位的密鑰
 53         if (key.length == 16) {
 54             byte[] temp = new byte[24];
 55             System.arraycopy(key, 0, temp, 0, key.length);
 56             System.arraycopy(key, 0, temp, key.length, temp.length - key.length);
 57             key = temp;
 58         }
 59 
 60         // 不是8的倍數的,補足
 61         byte[] srcBytes = content;
 62         if (srcBytes.length % 8 != 0) {
 63             int groups = content.length / 8 + (content.length % 8 != 0 ? 1 : 0);
 64             srcBytes = new byte[groups * 8];
 65             Arrays.fill(srcBytes, (byte) 0);
 66             System.arraycopy(content, 0, srcBytes, 0, content.length);
 67         }
 68 
 69         SecretKey deskey = new SecretKeySpec(key, "DESede");
 70         Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding");
 71         cipher.init(Cipher.ENCRYPT_MODE, deskey);
 72         byte[] temp = cipher.doFinal(srcBytes);
 73         byte[] tgtBytes = new byte[content.length];
 74         System.arraycopy(temp, 0, tgtBytes, 0, tgtBytes.length);
 75         return tgtBytes;
 76     }
 77 
 78     /**
 79      * 3DES解密
 80      * 
 81      * @param key
 82      *            密鑰
 83      * @param content
 84      *            待解密信息
 85      * @return
 86      * @throws Exception
 87      */
 88     public static byte[] decode3DES(byte[] key, byte[] content) throws Exception {
 89         // 不是8的倍數的,補足
 90         if (key.length % 8 != 0) {
 91             int groups = key.length / 8 + (key.length % 8 != 0 ? 1 : 0);
 92             byte[] temp = new byte[groups * 8];
 93             Arrays.fill(temp, (byte) 0);
 94             System.arraycopy(key, 0, temp, 0, key.length);
 95             key = temp;
 96         }
 97         // 長度為16位,轉換成24位的密鑰
 98         if (key.length == 16) {
 99             byte[] temp = new byte[24];
100             System.arraycopy(key, 0, temp, 0, key.length);
101             System.arraycopy(key, 0, temp, key.length, temp.length - key.length);
102             key = temp;
103         }
104 
105         // 不是8的倍數的,補足
106         byte[] srcBytes = content;
107         if (srcBytes.length % 8 != 0) {
108             int groups = content.length / 8 + (content.length % 8 != 0 ? 1 : 0);
109             srcBytes = new byte[groups * 8];
110             Arrays.fill(srcBytes, (byte) 0);
111             System.arraycopy(content, 0, srcBytes, 0, content.length);
112         }
113 
114         SecretKey deskey = new SecretKeySpec(key, "DESede");
115         Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding");
116         cipher.init(Cipher.DECRYPT_MODE, deskey);
117         byte[] tgtBytes = cipher.doFinal(srcBytes);
118         return tgtBytes;
119     }
120 
121     /**
122      * 二進制轉十六進制字符串。每一個字節轉為兩位十六進制字符串。
123      */
124     public static String byte2hex(byte[] b) {
125         String hs = "";
126         String stmp = "";
127         for (int i = 0; i < b.length; i++) {
128             stmp = Integer.toHexString(b[i] & 0XFF);
129             if (stmp.length() == 1) {
130                 hs = hs + "0" + stmp;
131             } else {
132                 hs = hs + stmp;
133             }
134         }
135         return hs.toUpperCase();
136     }
137     
138     /**
139      * <b>概要:</b>
140      * 十六進制轉二進制
141      * @param hex
142      * @return
143      * @throws IllegalArgumentException
144      */
145     public static byte[] hex2byte(String hex) throws IllegalArgumentException {
146         if (hex.length() % 2 != 0) {
147             throw new IllegalArgumentException();
148         }
149         if (hex.startsWith("0x")) {
150             hex = hex.substring(2);
151         }
152         char[] arr = hex.toCharArray();
153         byte[] b = new byte[hex.length() / 2];
154         for (int i = 0, j = 0, l = hex.length(); i < l; i++, j++) {
155             String swap = "" + arr[i++] + arr[i];
156             int byteint = Integer.parseInt(swap, 16) & 0xFF;
157             b[j] = new Integer(byteint).byteValue();
158         }
159         return b;
160     }
161     
162     /**
163      * 3DES加密模式
164      */
165      public static String encrypt(String value,String key) {
166         try {
167             SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "DESede");
168             Cipher cipher = Cipher.getInstance("DESede");
169             cipher.init(Cipher.ENCRYPT_MODE, keySpec);
170             byte[] encryptedByte = cipher.doFinal(value.getBytes());
171             String encodedByte = byte2hex(encryptedByte);
172             return encodedByte;
173         } catch(Exception e) {
174             e.printStackTrace();
175             return null;
176         }
177      }
178      
179      /**
180       * <b>概要:</b>
181       * 3DES解密
182       * @param value
183       * @param key
184       * @return
185       */
186     public static String decrypt(String value,String key) {
187         try {
188             
189             SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "DESede");
190             Cipher cipher = Cipher.getInstance("DESede");
191             cipher.init(Cipher.DECRYPT_MODE, keySpec);
192             byte[] decryptedByte = cipher.doFinal(hex2byte(value));            
193             return new String(decryptedByte);
194         } catch(Exception e) {
195             e.printStackTrace();
196             return null;
197         }
198         }
199     
200     @Test
201     public void TripleDESTest() throws Exception {
202 
203         // 獲取隨機密鑰
204         String key = generateRandomKey(24);
205         System.out.println("隨機密鑰:" + key);
206         String str = "DREAMING.XIN";
207         // des加密
208         String encodeDES = encrypt( str,key);
209         System.out.println("3DES加密結果:" + encodeDES);
210         
211         // des解密
212         String decodeDES = decrypt(encodeDES, key);
213         System.out.println("減密結果: " + decodeDES);
214     }
215 }

運行結果:

  技術分享圖片

補充說明:

 1、3DES的密鑰必須是24位的byte數組

  否則會報錯:如圖,我生成23位密鑰進行測試,報錯如下:

  技術分享圖片

  2、加密結果的編碼方式要一致

  從byte數組轉成字符串,一般有兩種方式,base64處理和十六進制處理。

  

參考:

  1、https://www.zhihu.com/question/36767829

  2、https://baike.baidu.com/item/DES/210508?fr=aladdin

  3、https://baike.baidu.com/item/對稱加密算法/211953?fr=aladdin

DES對稱加密算法簡析