DES對稱加密算法簡析
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對稱加密算法簡析