解决java和C语言之间DES加解密不一致的问题。
C实现des加解密方式用的是openssl的。
下面的类,直接copy下来,直接运行DESUtil类就行。
base64编码
DESUtil类(des工具类)
C实现des加解密方式用的是openssl的。
下面的类,直接copy下来,直接运行DESUtil类就行。
base64编码
package com.hui.security;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class CustBase64 {
private static final char[] legalChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_"
.toCharArray();
public static String encode(byte[] data) {
int start = 0;
int len = data.length;
StringBuffer buf = new StringBuffer(data.length * 3 / 2);
int end = len - 3;
int i = start;
int n = 0;
while (i <= end) {
int d = ((((int) data[i]) & 0x0ff) << 16)
| ((((int) data[i + 1]) & 0x0ff) << 8)
| (((int) data[i + 2]) & 0x0ff);
buf.append(legalChars[(d >> 18) & 63]);
buf.append(legalChars[(d >> 12) & 63]);
buf.append(legalChars[(d >> 6) & 63]);
buf.append(legalChars[d & 63]);
i += 3;
if (n++ >= 14) {
n = 0;
buf.append(" ");
}
}
if (i == start + len - 2) {
int d = ((((int) data[i]) & 0x0ff) << 16)
| ((((int) data[i + 1]) & 255) << 8);
buf.append(legalChars[(d >> 18) & 63]);
buf.append(legalChars[(d >> 12) & 63]);
buf.append(legalChars[(d >> 6) & 63]);
buf.append("=");
} else if (i == start + len - 1) {
int d = (((int) data[i]) & 0x0ff) << 16;
buf.append(legalChars[(d >> 18) & 63]);
buf.append(legalChars[(d >> 12) & 63]);
buf.append("==");
}
return buf.toString();
}
private static int decode(char c) {
if (c >= 'A' && c <= 'Z')
return ((int) c) - 65;
else if (c >= 'a' && c <= 'z')
return ((int) c) - 97 + 26;
else if (c >= '0' && c <= '9')
return ((int) c) - 48 + 26 + 26;
else
switch (c) {
case '+':
return 62;
case '_':
return 63;
case '=':
return 0;
default:
throw new RuntimeException("unexpected code: " + c);
}
}
public static byte[] decode(String s) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
decode(s, bos);
} catch (IOException e) {
throw new RuntimeException();
}
byte[] decodedBytes = bos.toByteArray();
try {
bos.close();
bos = null;
} catch (IOException ex) {
System.err.println("Error while decoding BASE64: " + ex.toString());
}
return decodedBytes;
}
private static void decode(String s, OutputStream os) throws IOException {
int i = 0;
int len = s.length();
while (true) {
while (i < len && s.charAt(i) <= ' ')
i++;
if (i == len)
break;
int tri = (decode(s.charAt(i)) << 18)
+ (decode(s.charAt(i + 1)) << 12)
+ (decode(s.charAt(i + 2)) << 6)
+ (decode(s.charAt(i + 3)));
os.write((tri >> 16) & 255);
if (s.charAt(i + 2) == '=')
break;
os.write((tri >> 8) & 255);
if (s.charAt(i + 3) == '=')
break;
os.write(tri & 255);
i += 4;
}
}
}
LibDes(模拟openssl库的方法)
package com.hui.security;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class LibDes {
public LibDes() throws Exception {
cipher_ = Cipher.getInstance("DES/CBC/NoPadding");
}
public byte[] des_string_to_2keys(String keyString) throws Exception {
final byte[] str = keyString.getBytes();
byte[] key1 = new byte[8];
byte[] key2 = new byte[8];
for (int i = 0; i < str.length; i++) {
int j = str[i] & 0xff;
if ((i % 32) < 16) {
if ((i % 16) < 8)
key1[i % 8] ^= (j << 1);
else
key2[i % 8] ^= (j << 1);
} else {
j = ((j << 4) & 0xf0) | ((j >>> 4) & 0x0f);
j = ((j << 2) & 0xcc) | ((j >>> 2) & 0x33);
j = ((j << 1) & 0xaa) | ((j >>> 1) & 0x55);
if ((i % 16) < 8)
key1[7 - (i % 8)] ^= j;
else
key2[7 - (i % 8)] ^= j;
}
}
if (str.length <= 8) {
for (int index = 0; index < 8; index++)
key2[index] = key1[index];
}
des_set_odd_parity(key1);
key1 = des_cbc_cksum(str, key1, key1);
des_set_odd_parity(key1);
des_set_odd_parity(key2);
key2 = des_cbc_cksum(str, key2, key2);
des_set_odd_parity(key2);
return concat(key1, key2, key1);
}
private static byte[] concat(byte[] a, byte[] b, byte[] c) {
final byte[] result = new byte[a.length + b.length + c.length];
int k = 0;
for (int index = 0; index < a.length; index++)
result[k++] = a[index];
for (int index = 0; index < b.length; index++)
result[k++] = b[index];
for (int index = 0; index < c.length; index++)
result[k++] = c[index];
return result;
}
/**
* http://web.mit.edu/macdev/Development/MITKerberos/MITKerberosLib/DESLib/
* Documentation/api.html
*/
public byte[] des_string_to_key(String keyString) throws Exception {
final byte[] str = keyString.getBytes();
// printHex("Raw", str);
byte[] key = new byte[8];
for (int index = 0; index < str.length; index++) {
int j = str[index] & 0xff;
if ((index % 16) < 8)
key[index % 8] ^= (j << 1);
else {
j = ((j << 4) & 0xf0) | ((j >>> 4) & 0x0f);
j = ((j << 2) & 0xcc) | ((j >>> 2) & 0x33);
j = ((j << 1) & 0xaa) | ((j >>> 1) & 0x55);
key[7 - (index % 8)] ^= j;
}
}
// printHex("Basic", key);
des_set_odd_parity(key);
// printHex("Parity", key);
// Uses the key as a key in the cbc checksum and
// as the IV, that is why it MUST MUST MUST have
// odd parity.
key = des_cbc_cksum(str, key, key);
// printHex("Checksum", checksum);
// Not really needed by JCE since it ignore the
// parity bit BUT for completeness.
des_set_odd_parity(key);
// printHex("Parity 1", checksum);
return key;
}
public byte[] des_cbc_cksum(byte[] data, byte[] keyBytes, byte[] ivec) throws Exception {
// Pad the data with zeros until a multiple of 8 bytes
final byte[] paddedData = new byte[((data.length + 7) / 8) * 8];
for (int index = 0; index < data.length; index++) {
paddedData[index] = data[index];
}
// CBC encrypt the key
final SecretKey skey = new SecretKeySpec(keyBytes, "DES");
final IvParameterSpec iv = new IvParameterSpec((byte[]) ivec.clone());
cipher_.init(Cipher.ENCRYPT_MODE, skey, iv);
final byte[] rkey = cipher_.doFinal(paddedData);
// The checksum is the last 8 bytes
final byte[] checksum = new byte[8];
for (int index = 0, j = rkey.length - 8; index < 8; index++, j++) {
checksum[index] = rkey[j];
}
return checksum;
}
public static void des_set_odd_parity(byte[] data) {
for (int i = 0; i < 8; i++)
data[i] = (byte) odd_parity[data[i] & 0xff];
}
final Cipher cipher_;
private static final int[] odd_parity =
{
1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14,
16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31,
32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47,
49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62,
64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79,
81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94,
97, 97, 98, 98, 100, 100, 103, 103, 104, 104, 107, 107, 109, 109, 110, 110,
112, 112, 115, 115, 117, 117, 118, 118, 121, 121, 122, 122, 124, 124, 127, 127,
128, 128, 131, 131, 133, 133, 134, 134, 137, 137, 138, 138, 140, 140, 143, 143,
145, 145, 146, 146, 148, 148, 151, 151, 152, 152, 155, 155, 157, 157, 158, 158,
161, 161, 162, 162, 164, 164, 167, 167, 168, 168, 171, 171, 173, 173, 174, 174,
176, 176, 179, 179, 181, 181, 182, 182, 185, 185, 186, 186, 188, 188, 191, 191,
193, 193, 194, 194, 196, 196, 199, 199, 200, 200, 203, 203, 205, 205, 206, 206,
208, 208, 211, 211, 213, 213, 214, 214, 217, 217, 218, 218, 220, 220, 223, 223,
224, 224, 227, 227, 229, 229, 230, 230, 233, 233, 234, 234, 236, 236, 239, 239,
241, 241, 242, 242, 244, 244, 247, 247, 248, 248, 251, 251, 253, 253, 254, 254
};
}
PaddingProcessor(对加密的数据不满足8字节倍数的话,进行加,减padding,这里补的是0)
package com.hui.security;
public class PaddingProcessor {
private final static int BLOCK_SIZE = 8;
public static byte[] addPadding(byte[] in, int blockSize) {
int length = in.length;
if (length % blockSize == 0) {
return in;
} else {
byte[] out;
int blockCount = length / blockSize;
out = new byte[(blockCount+1) * blockSize];
System.arraycopy(in, 0, out, 0, length);
return out;
}
}
public static byte[] removePadding(byte[] in, int blockSize) {
int outLen = in.length;
for (int i = BLOCK_SIZE + 1; i > 0&&outLen>0; i--) {
if (in[--outLen] == (byte) 0x00) {
break;
}
}
byte[] out = new byte[outLen];
System.arraycopy(in, 0, out, 0, outLen);
return out;
}
}
DESUtil类(des工具类)
package com.hui.security;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
public class DESUtil {
// 算法名称
public static final String KEY_ALGORITHM = "DES";
// 算法名称/加密模式/填充方式
// DES共有四种工作模式-->>ECB:电子密码本模式、CBC:加密分组链接模式、CFB:加密反馈模式、OFB:输出反馈模式
public static final String CIPHER_ALGORITHM = "DES/CBC/NoPadding";
private static final int DES_PADDING_SIZE = 8;
/**
*
* 生成密钥key对象
*
* @param KeyStr
* 密钥字符串
* @return 密钥对象
* @throws InvalidKeyException
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
* @throws Exception
*/
private static SecretKey keyGenerator(String keyStr) throws Exception {
LibDes libDes = new LibDes();
byte input[] = libDes.des_string_to_key(keyStr);
System.out.println("密钥key的字节长度:"+input.length);
DESKeySpec desKey = new DESKeySpec(input);
// 创建一个密匙工厂,然后用它把DESKeySpec转换成
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey securekey = keyFactory.generateSecret(desKey);
return securekey;
}
private static int parse(char c) {
if (c >= 'a')
return (c - 'a' + 10) & 0x0f;
if (c >= 'A')
return (c - 'A' + 10) & 0x0f;
return (c - '0') & 0x0f;
}
// 从十六进制字符串到字节数组转换
public static byte[] HexString2Bytes(String hexstr) {
byte[] b = new byte[hexstr.length() / 2];
int j = 0;
for (int i = 0; i < b.length; i++) {
char c0 = hexstr.charAt(j++);
char c1 = hexstr.charAt(j++);
b[i] = (byte) ((parse(c0) << 4) | parse(c1));
}
return b;
}
/**
* 加密数据
*
* @param data
* 待加密数据
* @param key
* 密钥
* @return 加密后的数据
*/
public static String encrypt(String data, String key) throws Exception {
Key deskey = keyGenerator(key);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);// 实例化Cipher对象,它用于完成实际的加密操作
cipher.init(Cipher.ENCRYPT_MODE, deskey, new IvParameterSpec(new byte[8])); // 初始化Cipher对象,设置为加密模式
byte[] in = PaddingProcessor.addPadding(data.getBytes(), DES_PADDING_SIZE);
System.out.println("加密数据的字节长度:"+in.length);
byte[] results = cipher.doFinal(in); // 执行加密操作。
return CustBase64.encode(results); //加密后的结果通常都会用Base64编码进行传输
}
/**
* 解密数据
*
* @param data
* 待解密数据
* @param key
* 密钥
* @return 解密后的数据
*/
public static String decrypt(String data, String key) throws Exception {
Key deskey = keyGenerator(key);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
// 初始化Cipher对象,设置为解密模式
cipher.init(Cipher.DECRYPT_MODE, deskey, new IvParameterSpec(new byte[8]));
System.out.println("解密数据的字节长度:"+CustBase64.decode(data).length);
byte[] plain = cipher.doFinal(CustBase64.decode(data));
plain = PaddingProcessor.removePadding(plain, DES_PADDING_SIZE);//这句要不要都行,因为string遇到0就标示中断,所以去不去padding都无所谓。
// 执行解密操作
return new String(plain);
}
public static void main(String[] args) throws Exception {
String source = "aaaaaaaaa";
System.out.println("原文: " + source);
String keyString = "bbbbbbbbb";
System.out.println(keyString.getBytes().length);
String encryptData = encrypt(source, keyString);
System.out.println("加密后: " + encryptData);
String decryptData = decrypt(encryptData, keyString);
System.out.println("解密后: " + decryptData);
}
}
输出结果
原文: aaaaaaaaa
9
密钥key的字节长度:8
加密数据的字节长度:16
加密后: B2d5O2gMRtE24KeDCUzGtA==
密钥key的字节长度:8
解密数据的字节长度:16
解密后: aaaaaaaaa