JAVA 3DES加解密與C對接

[color=red]C端同事給的密文(十六進制)是32位,但是我這邊生成的是48位
剛開始工具類中使用的
Cipher cipher=Cipher.getInstance("DESede");
後來把這個改爲
Cipher cipher=Cipher.getInstance("DESede/ECB/NoPadding");
生成的32位 與C端相同
Java 解密、工具類 見下文
[/color]

Java 官方附錄:[url]http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#AlgorithmParameterGenerator[/url]

Cipher cipher=Cipher.getInstance("DESede/CBC/PKCS5Padding");

以前寫的代碼,給的參數都是DES或DESede。實際上DESede是簡寫,它與DESede/ECB/PKCS5Padding等價。這個參數分爲三段。

- 第一段是加密算法的名稱,如DESede實際上是3-DES。這一段還可以放其它的對稱加密算法,如Blowfish等。

- 第二段是分組加密的模式,除了CBC和ECB之外,還可以是NONE/CFB/QFB等。最常用的就是CBC和ECB了。DES採用分組加密的方式,將明文按8字節(64位)分組分別加密。如果每個組獨立處理,則是ECB。CBC的處理方式是先用初始向量IV對第一組加密,再用第一組的密文作爲密鑰對第二組加密,然後依次完成整個加密操作。如果明文中有兩個分組的內容相同,ECB會得到完全一樣的密文,但CBC則不會。

- 第三段是指最後一個分組的填充方式。大部分情況下,明文並非剛好64位的倍數。對於最後一個分組,如果長度小於64位,則需要用數據填充至64位。PKCS5Padding是常用的填充方式,如果沒有指定,默認的方式就是它。

補充一點,雖然DES的有效密鑰長度是56位,但要求密鑰長度是64位(8字節)。3DES則要求24字節。


出處:[url]http://www.cnblogs.com/qkhh/p/4683626.html[/url]

MODE指的是加密模式。加密算法是按塊進行加密的,DES是64bit(8 bytes)一個塊進行加密,每次輸入8個字節的明文,輸出8個字節密文,如果是明文一共16個字節長,則分成兩組依次進行加密。

(1) 例如明文是 1234567812345678,那麼加密的結果類似C21431262C779CC21431262C779C,看出了什麼沒,是的,重複了兩遍,這鐘MODE就是ECB。分組之間沒有聯繫,組和組明文相同,那麼密文也是相同的,容易被發現規律。

(2)爲了解決這個問題,出現了新的加密模式,分組連接模式(CBC),密碼反饋模式(CFB),輸出反饋模式(OFB)。

CBC 是需要給一個初始化的向量,然後將每個輸出與該向量作運算,並將運算的結果作爲下一個加密塊的初始化向量,CFB 和 OFB 則不需要提供初始化向量,直接將密碼或者輸出作爲初始化向量進行計算;避免了明文的規律性出現反應在密文中。當然帶來的問題是,解密的時候必須接收完整才能開始解密。如果其中部分信息網絡接收出錯,會導致整個解密失敗,而ECB僅影響網絡傳輸出錯的那個塊的解密。



其次、解釋上段落第3點鐘提到的PADDING。padding在這裏指填充方式,例如明文是10位,按照8bytes分組,正好1組多2個byte,這2個byte怎麼加密?這時候必須對明文進行Padding操作。padding的方式很多,在《跨語言的加解密兼容問題討論》 一文中有比較想起的對比。

(1)一種PKCS#7,該填充方法是將每一個補充的字節內容填充爲填充的字節個數;在這裏我們需要填充的6個'0x6';

(2)另一種PKCS#5,和PKCS#7的區別就 是,分組的大小爲8個字節。

(3)另外,就是如果明文剛剛好進行分組,那麼需要補充一個獨立的分組。如 DES明文:12345678,爲 8 個字節,則必須補充8個0×08至16個字節,然後進行加密;解密後的字符串爲12345678\x08\x08\x08\x08\x08\x08\x08\x08,需要去掉多餘的, 0×08的到原文。

出處:[url]http://www.mythroad.net/2012/11/01/3des%E7%AE%97%E6%B3%95java%E4%B8%8Ec%E7%9A%84%E5%85%BC%E5%AE%B9%E9%97%AE%E9%A2%98%E8%AF%A6%E7%BB%86%E5%88%86%E6%9E%90%E4%B8%8E%E5%AE%9E%E7%8E%B0/[/url]

[color=red]DESUtil[/color]


import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;

import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;

public class DESUtil {

private static final String Algorithm = "DESede"; //定義 加密算法,可用 DES,DESede,Blowfish

private static final String AlgorithmP = "DESede/ECB/NoPadding";

public static String Decrypt3DES(String value, String key) throws Exception {

// byte[] b = decryptMode(GetKeyBytes(key), Base64.decode(value));
byte[] b = decryptMode(GetKeyBytes(key), HexUtil.decodeHex(value.toCharArray()));

return new String(b);

}


public static String Encrypt3DES(String value, String key) throws Exception {
// String str = byte2Base64(encryptMode(GetKeyBytes(key), value.getBytes()));
String str = HexUtil.encodeHexStr(encryptMode(GetKeyBytes(key), value.getBytes()));
return str;
}


//計算24位長的密碼byte值,首先對原始密鑰做MD5算hash值,再用前8位數據對應補全後8位
public static byte[] GetKeyBytes(String strKey) throws Exception {
if (null == strKey || strKey.length() < 1)
throw new Exception("key is null or empty!");
byte[] bkey = strKey.getBytes();
int start = bkey.length;
byte[] bkey24 = new byte[24];
for (int i = 0; i < start; i++) {
bkey24[i] = bkey[i];
}
for (int i = start; i < 24; i++) {//爲了與.net16位key兼容
bkey24[i] = bkey[i - start];
}
return bkey24;
}


//keybyte爲加密密鑰,長度爲24字節

//src爲被加密的數據緩衝區(源)

public static byte[] encryptMode(byte[] keybyte, byte[] src) {

try {
//生成密鑰

SecretKey deskey = new SecretKeySpec(keybyte, Algorithm); //加密

Cipher c1 = Cipher.getInstance(AlgorithmP);

c1.init(Cipher.ENCRYPT_MODE, deskey);

return c1.doFinal(src);

} catch (java.security.NoSuchAlgorithmException e1) {

e1.printStackTrace();

} catch (javax.crypto.NoSuchPaddingException e2) {

e2.printStackTrace();

} catch (java.lang.Exception e3) {

e3.printStackTrace();

}

return null;

}



//keybyte爲加密密鑰,長度爲24字節

//src爲加密後的緩衝區

public static byte[] decryptMode(byte[] keybyte, byte[] src) {

try { //生成密鑰

SecretKey deskey = new SecretKeySpec(keybyte, Algorithm);

//解密

Cipher c1 = Cipher.getInstance(AlgorithmP);

c1.init(Cipher.DECRYPT_MODE, deskey);

return c1.doFinal(src);

} catch (java.security.NoSuchAlgorithmException e1) {

e1.printStackTrace();

} catch (javax.crypto.NoSuchPaddingException e2) {

e2.printStackTrace();

} catch (java.lang.Exception e3) {

e3.printStackTrace();

}

return null;

}



//轉換成base64編碼

public static String byte2Base64(byte[] b) {

return Base64.encode(b);

}

//轉換成十六進制字符串

public static String byte2hex(byte[] b) {

String hs = "";

String stmp = "";

for (int n = 0; n < b.length; n++) {

stmp = (java.lang.Integer.toHexString(b[n] & 0XFF));

if (stmp.length() == 1)

hs = hs + "0" + stmp;

else

hs = hs + stmp;

}

return hs.toUpperCase();

}


/**

* 去掉java加密後會在後面自動填充的8位

* @param src

* @return

*/

public static byte[] withoutAutofill(byte[] src){

byte[] newbyte = new byte[src.length-8];

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

newbyte[i] = src[i];

}

return newbyte;

}


public static void main(String[] args) throws Exception {
String x = Encrypt3DES("12341234","000000000222225678");
System.out.println(x);
String y = Decrypt3DES(x, "000000000222225678");
System.out.println(y);
}


}



[color=red]HexUtil[/color]


public class HexUtil {
/**
* 用於建立十六進制字符的輸出的小寫字符數組
*/
private static final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
/**
* 用於建立十六進制字符的輸出的大寫字符數組
*/
private static final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
/**
* 將字節數組轉換爲十六進制字符數組
*
* @param data
* byte[]
* @return 十六進制char[]
*/
public static char[] encodeHex(byte[] data) {
return encodeHex(data, true);
}
/**
* 將字節數組轉換爲十六進制字符數組
*
* @param data
* byte[]
* @param toLowerCase
* <code>true</code> 傳換成小寫格式 , <code>false</code> 傳換成大寫格式
* @return 十六進制char[]
*/
public static char[] encodeHex(byte[] data, boolean toLowerCase) {
return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
}
/**
* 將字節數組轉換爲十六進制字符數組
*
* @param data
* byte[]
* @param toDigits
* 用於控制輸出的char[]
* @return 十六進制char[]
*/
protected static char[] encodeHex(byte[] data, char[] toDigits) {
int l = data.length;
char[] out = new char[l << 1];
// two characters form the hex value.
for (int i = 0, j = 0; i < l; i++) {
out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
out[j++] = toDigits[0x0F & data[i]];
}
return out;
}
/**
* 將字節數組轉換爲十六進制字符串
*
* @param data
* byte[]
* @return 十六進制String
*/
public static String encodeHexStr(byte[] data) {
return encodeHexStr(data, false);
}
/**
* 將字節數組轉換爲十六進制字符串
*
* @param data
* byte[]
* @param toLowerCase
* <code>true</code> 傳換成小寫格式 , <code>false</code> 傳換成大寫格式
* @return 十六進制String
*/
public static String encodeHexStr(byte[] data, boolean toLowerCase) {
return encodeHexStr(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
}
/**
* 將字節數組轉換爲十六進制字符串
*
* @param data
* byte[]
* @param toDigits
* 用於控制輸出的char[]
* @return 十六進制String
*/
protected static String encodeHexStr(byte[] data, char[] toDigits) {
return new String(encodeHex(data, toDigits));
}
/**
* 將十六進制字符數組轉換爲字節數組
*
* @param data
* 十六進制char[]
* @return byte[]
* @throws RuntimeException
* 如果源十六進制字符數組是一個奇怪的長度,將拋出運行時異常
*/
public static byte[] decodeHex(char[] data) {
int len = data.length;
if ((len & 0x01) != 0) {
throw new RuntimeException("Odd number of characters.");
}
byte[] out = new byte[len >> 1];
// two characters form the hex value.
for (int i = 0, j = 0; j < len; i++) {
int f = toDigit(data[j], j) << 4;
j++;
f = f | toDigit(data[j], j);
j++;
out[i] = (byte) (f & 0xFF);
}
return out;
}
/**
* 將十六進制字符轉換成一個整數
*
* @param ch
* 十六進制char
* @param index
* 十六進制字符在字符數組中的位置
* @return 一個整數
* @throws RuntimeException
* 當ch不是一個合法的十六進制字符時,拋出運行時異常
*/
protected static int toDigit(char ch, int index) {
int digit = Character.digit(ch, 16);
if (digit == -1) {
throw new RuntimeException("Illegal hexadecimal character " + ch
+ " at index " + index);
}
return digit;
}
public static void main(String[] args) {
String srcStr = "待轉換字符串";
String encodeStr = encodeHexStr(srcStr.getBytes());
String decodeStr = new String(decodeHex(encodeStr.toCharArray()));
System.out.println("轉換前:" + srcStr);
System.out.println("轉換後:" + encodeStr);
System.out.println("還原後:" + decodeStr);
}
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章