〇、前言
最近在项目中,涉及到与第三方厂家系统进行对接时,在参数传递过程中考虑到了数据的安全性,故双方采用3DES进行对传递参数的加解密,因此,进一步了解了下3DES的加解密算法,再次进行梳理。
一、DES算法
DES,Data Encryption Standard,即:数据加密标准,是一种使用密钥加密的块算法。
DES算法在POS、ATM、磁卡及IC卡、加油站、高速公路收费站等领域被广泛应用,以此来实现关键数据的保密,如信用卡持卡人的PIN的加密传输,IC卡与POS间的双向认证、金融交易数据包的MAC校验等,均用到DES算法。
1、DES算法的入口参数有三个: Key、Data、Mode。
其中Key为8个字节共64位,是DES算法的工作密钥;
Data也为8个字节64位,是要被加密或被解密的数据;
Mode为DES的工作方式,有两种:加密或解密。
2、DES算法过程:
如果mode为加密,则用Key 去把数据Data进行加密, 生成Data的密码形式(64位)作为DES的输出结果;
如果mode为解密,则用Key去把密码形式的数据Data解密,还原为Data的明码形式(64位)作为DES的输出结果。
在通信网络的两端,双方约定一致的Key,在通信的源点用Key对核心数据进行DES加密,然后以密码形式在公共通信网(如电话网)中传输到通信网络的终点,数据到达目的地后,用同样的Key对密码数据进行解密,便再现了明码形式的核心数据。这样,便保证了核心数据(如PIN、MAC等)在公共通信网中传输的安全性和可靠性。
通过定期在通信网络的源端和目的端同时改用新的Key,便能更进一步提高数据的保密性,这正是现在金融交易网络的流行做法。
(PS:不过在当今为了数据的更安全,一般采用加密平台进行数据的硬加密,实现数据明文不落地,尤其是在金融行业尤为运用广泛。)
3、DES算法步骤
DES算法把64位的明文输入块变为64位的密文输出块,它所使用的密钥也是64位(实际用到了56位,第8、16、24、32、40、48、56、64位是校验位,使得每个密钥都有奇数个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
②逆置换
经过16次迭代运算后,得到L16、R16,将此作为输入,进行逆置换,逆置换正好是初始置换的逆运算,由此即得到密文输出。
此算法是对称加密算法体系中的代表,在计算机网络系统中广泛使用。
4、DES算法实现
见 "三、算法实现"
二、3DES算法
3DES(又称Triple DES),是进行了三重数据加密,即:每个数据块进行了三次DES加密算法,使用3条64位的密钥对数据进行三次加密,故比DES加密更为安全,更难破解。
1、加密算法,其具体实现如下:
设Ek()和Dk()代表DES算法的加密和解密过程,K代表DES算法使用的密钥,M代表明文,C代表密文:
3DES加密过程为:C=Ek3(Dk2(Ek1(M)))
3DES解密过程为:M=Dk1(EK2(Dk3(C)))
注:K1、K2、K3决定了算法的安全性,若三个密钥互不相同,本质上就相当于用一个长为168位的密钥进行加密。多年来,它在对付强力攻击时是比较安全的。若数据对安全性要求不那么高,K1可以等于K3。在这种情况下,密钥的有效长度为112位。
2、3DES算法实现
(见 "三、算法实现")
三、算法实现
1、Java实现
<span style="font-size:12px;">package com.xcbeyond.security;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 加解密工具类
* @author xcbeyond
* 2016-8-6下午11:16:46
*/
public class ScrtUtil {
private static final Log log = LogFactory.getLog(ScrtUtil.class);
private static final String DES3 = "DESede";
private static final String DES = "DES";
/**
* 测试方法
* @param args
*/
public static void main(String[] args) {
//密钥
String key = "0123456789asdfghjklzxcvbnmqwertyuiop";
//待加密数据
String data = "1000000";
System.out.println("加密前的数据为:" + data);
System.out.println();
//DES加解密
String encoded = encryptDES(getDesKey(key), data.getBytes());
System.out.println("DES加密后的数据为:" + encoded);
String decoded = decryptDES(getDesKey(key), hexStringToBytes(encoded));
System.out.println("DES解密后的数据为:" + new String(decoded));
System.out.println();
encoded = encrypt3DES(get3DesKey(key), data.getBytes());
System.out.println("3DES加密后的数据为:" + encoded);
decoded = decrypt3DES(get3DesKey(key), hexStringToBytes(encoded));
System.out.println("3DES解密后的数据为:" + new String(decoded));
}
/**
* 生成24字节的3DES密钥。
* (不够24字节,则补0;超过24字节,则取前24字节。)
* @param key 密钥字符串
* @return
*/
public static byte[] get3DesKey(String key) {
byte[] keyBytes = new byte[24];
if(key.getBytes().length > 24) {
for(int i = 0;i<24;i++) {
keyBytes[i] = key.getBytes()[i];
}
} else {
for(int i = 0;i<24;i++) {
if(i < key.getBytes().length) {
keyBytes[i] = key.getBytes()[i];
} else {
keyBytes[i] = 0x00;
}
}
}
return keyBytes;
}
/**
* 生成8字节的DES密钥。
* (不够8字节,则补0;超过8字节,则取前8字节。)
* @param key 密钥字符串
* @return
*/
public static byte[] getDesKey(String key) {
byte[] keyBytes = new byte[8];
if(key.getBytes().length > 8) {
for(int i = 0;i<8;i++) {
keyBytes[i] = key.getBytes()[i];
}
} else {
for(int i = 0;i<8;i++) {
if(i < key.getBytes().length) {
keyBytes[i] = key.getBytes()[i];
} else {
keyBytes[i] = 0x00;
}
}
}
return keyBytes;
}
/**
* 3DES加密
* @param
* key :加密密钥
* value :被加密的数据
* @return
* 加密后的数据
*/
public static String encrypt3DES(byte[] key, byte[] value) {
byte[] retValue = null;
try {
SecretKey deskey = new SecretKeySpec(key, DES3);
Cipher c1 = Cipher.getInstance(DES3);
c1.init(1, deskey);
retValue = c1.doFinal(value);
} catch (Exception ex) {
if(log.isErrorEnabled()) {
log.error("3DES加密错误" + ex);
}
}
//转换为16进制数返回
return getHexString(retValue);
}
/**
* 3DES解密
* @param
* key :解密密钥
* value :被解密的数据
* @return
* 解密后的明文数据
*/
public static String decrypt3DES(byte[] key, byte[] value) {
byte[] retValue = null;
try {
SecretKey deskey = new SecretKeySpec(key, DES3);
Cipher c1 = Cipher.getInstance(DES3);
c1.init(2, deskey);
retValue = c1.doFinal(value);
} catch (Exception ex) {
if(log.isErrorEnabled()) {
log.error("3DES解密错误" + ex);
}
}
return new String(retValue);
}
/**
* DES加密
* @param
* key :加密密钥
* value :被加密的数据
* @return
* 加密后的数据
*/
public static String encryptDES(byte[] key, byte[] value) {
byte[] retValue = null;
try {
SecretKey deskey = new SecretKeySpec(key, DES);
Cipher c1 = Cipher.getInstance(DES);
c1.init(1, deskey);
retValue = c1.doFinal(value);
} catch (Exception ex) {
if(log.isErrorEnabled()) {
log.error("DES加密错误" + ex);
}
}
//转换为16进制数返回
return getHexString(retValue);
}
/**
* DES解密
* @param
* key :解密密钥
* value :被加密的数据
* @return
* 加解密后的明文数据
*/
public static String decryptDES(byte[] key, byte[] value) {
byte[] retValue = null;
try {
SecretKey deskey = new SecretKeySpec(key, DES);
Cipher c1 = Cipher.getInstance(DES);
c1.init(2, deskey);
retValue = c1.doFinal(value);
} catch (Exception ex) {
if(log.isErrorEnabled()) {
log.error("DES解密错误" + ex);
}
}
return new String(retValue);
}
/*
* 转换为16进制数
* @param data
* @return
* 16进制数
*/
public static String getHexString(byte[] data) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < data.length; ++i) {
String ch = Integer.toHexString(data[i] & 0xFF).toUpperCase();
if (ch.length() == 2)
sb.append(ch);
else
sb.append("0").append(ch);
}
return sb.toString();
}
/**
* 将16进制的字符串转换为byte数组
* @param hexString
* @return
*/
public static byte[] hexStringToBytes(String hexString) {
if (hexString == null || hexString.equals("")) {
return null;
}
hexString = hexString.toUpperCase();
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] d = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return d;
}
public static byte charToByte(char c) {
return (byte) "0123456789ABCDEF".indexOf(c);
}
}</span>
温馨注意:
Java与其它语言进行相互加密、解密时,应在java中需要待加密或解密数据后追加字符串结束符“\0”,否则造成相互加解密存在数据差异。
2、Delphi实现
<span style="font-size:12px;">//---------------------------------------------------------------------------
//包含DES加/解密和3DES加/解密
//---------------------------------------------------------------------------
unit DesCipher;
//---------------------------------------------------------------------------
interface
//---------------------------------------------------------------------------
uses
SysUtils, StrUtils;
//---------------------------------------------------------------------------
type
TKeyByte=array[0..5] of Byte;
TDesMode=(dmEncry, dmDecry);
function DesEncryStrHex(Str, Key: string): string;