解决java和C语言之间DES加解密不一致的问题。

解决java和C语言之间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



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值