附 :近日的项目是与其他公司进行合作开发的,在传输密码时对方提出用3DES加密方式进行加密。对方提供了BASE64编码的key和向量IV,其使用的是C#语言,在进行3DES加密时使用的是CBC模式,Zeros填充方式。而我方使用的是java语言。
在Google一番后发现,两种语言之间有两种兼容方式:
1.C#采用CBC Mode,PKCS7 Padding,Java采用CBC Mode,PKCS5Padding Padding
2.C#采用ECB Mode,PKCS7 Padding,Java采用ECB Mode,PKCS5Padding Padding
但是在此次合作中,C#的填充方式为Zeros、CBC模式,使我很苦恼。在大量的Google之后,找到了解决方案,因此进行记录。
在java中用CBC模式,NoPadding填充方式。在进行加密时自己完成对密码的填充(用字节零填充)可以保证与C#中CBC模式Zeros填充方式加密结果相同。
代码:
import java.io.UnsupportedEncodingException; import java.security.SecureRandom; import java.security.Security; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESedeKeySpec; import javax.crypto.spec.IvParameterSpec; import org.apache.commons.codec.binary.Base64; /** * 加密方法DESede表示是3des加密方式<p> * 运算模式CBC,ECB。在CBC模式下使用key,向量iv;在ECB模式下仅使用key。<p> * 填充模式NoPadding、PKCS5Padding、SSL3Padding。<p> * 语言之间的兼容:<br> * 一个是C#采用CBC Mode,PKCS7 Padding,Java采用CBC Mode,PKCS5Padding Padding,<br> * 另一个是C#采用ECB Mode,PKCS7 Padding,Java采用ECB Mode,PKCS5Padding Padding, * <p> * 此段代码使用的CBC模式NoPadding填充方式、用字节零填充,目的是匹配C#语言中CBC模式,zeros填充方式。 */ public class ThreeDES { /** * 加密BASE64编码的KEY */ private static final String BASE64_key = "12345678901234567890123456789012"; /** * 向量IV */ private static final String _IV = "12345678"; private final static String Algorithm = "DESede/CBC/NoPadding";//加密方法/运算模式/填充模式 /* 加密方法DESede表示是3des加密方式 * 运算模式CBC,ECB。在CBC模式下使用key,向量iv;在ECB模式下仅使用key。 * 填充模式NoPadding、PKCS5Padding、SSL3Padding。 * 语言之间的兼容: * 一个是C#采用CBC Mode,PKCS7 Padding,Java采用CBC Mode,PKCS5Padding Padding, * 另一个是C#采用ECB Mode,PKCS7 Padding,Java采用ECB Mode,PKCS5Padding Padding, */ private static SecureRandom sr = new SecureRandom(); private static SecretKeyFactory keyFactory; private static DESedeKeySpec dks; private static SecretKey securekey; private static IvParameterSpec ips; static { //添加jce支持(sun有其默认实现) Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); try { dks = new DESedeKeySpec(Base64.decodeBase64(BASE64_key.getBytes("UTF-8"))); keyFactory = SecretKeyFactory.getInstance("DESede"); securekey = keyFactory.generateSecret(dks); ips = new IvParameterSpec(_IV.getBytes("UTF-8")); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 3des加密 * @param password 密码 * @return 加密后密文 */ public static String encrypt(String password){ Cipher cipher; String result = null; try { cipher = Cipher.getInstance(Algorithm); cipher.init(Cipher.ENCRYPT_MODE, securekey, ips, sr); byte [] arry = cipher.doFinal(FormateData(password)); result = new String(Base64.encodeBase64(arry),"UTF-8"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return result; } /** * 3des解密 * @param password 密文 * @return 解密后密码 */ public static String decrypt(String password){ Cipher cipher; String result = null; try { cipher = Cipher.getInstance(Algorithm); cipher.init(Cipher.DECRYPT_MODE, securekey, ips, sr); byte [] arry = cipher.doFinal(Base64.decodeBase64(password.getBytes("UTF-8"))); result = new String(FormateByte(arry)); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return result; } /** * 密码加密时,填充字符串为8的倍数。<p> * (此方法在模式是CBC模式,填充方式为NoPadding方式的情况下,用字节零填充.) * * @param str * 密码 * @return 加密后的密文 */ public static byte [] FormateData(String str) throws UnsupportedEncodingException{ int yu = str.length() % 8; if(yu != 0){ int size = 8 - yu; byte [] arr = new byte [str.length() + size]; byte [] data = str.getBytes("UTF-8"); int i = 0; for (; i < data.length; i++) { arr[i] = data[i]; } for (int j = 0; j < size; j++,i++) { arr[i] = new byte [] {0}[0]; } return arr; } return str.getBytes("UTF-8"); } /** * 密码解密时,将填充的字节零去掉!<p> * (此方法只在模式是CBC模式,填充方式为NoPadding方式,用字节零填充 的情况下使用。) * @param arr * 密文字节组 * * @return 密码字节组 */ public static byte [] FormateByte(byte [] arr){ int i = 0; for (; i < arr.length; i++) { if(arr[i] == new Byte("0")){ break; } } byte [] result = new byte [i]; for (int j = 0; j < i; j++) { result[j] = arr[j]; } return result; } public static void main(String[] args) { String a = encrypt("12345612345612345"); System.out.println(a); System.out.println(decrypt(a)); } }