java 与 c# 3des 加解密

 

主要差异如下:

1、  对于待加密解密的数据,各自的填充模式不一样

      C#的模式有:ANSIX923、ISO10126、None、PKCS7、Zero,而Java有:NoPadding、PKCS5Padding、SSL3Padding

2、  各自默认的3DES实现,模式和填充方式不一样

C#的默认模式为CBC,默认填充方式为PKCS7; java的默认模式为ECB,默认填充方式为PKCS5Padding

3、  各自的key的size不一样

C#中key的size为16和24均可;java中要求key的size必须为24;对于CBC模式下的向量iv的size两者均要求必须为8

 

翻看了3DES的原理:

DES主要采用替换和移位的方法,用56位密钥对64位二进制数据块进行加密,每次加密可对64位的输入数据进行16轮编码,

经一系列替换和移位后,输入的64位转换成安全不同的64的输出数据

.   
3DES:是在DES的基础上采用三重DES,即用两个56位的密钥K1,K2,发送方用K1加密,K2解密,再使用K1加密.接收方使用K1解密,K2加密,再使用K1解密,

其效果相当于密钥长度加倍.

 

于是尝试在java中,对key进行补位,即用前8个字节作为byte[24] 中的byte[16]~byte[23];发现与c#中加密的结果相同!于是大胆假设C#中可能是检查key的size为16的时候

自动将前8个字节作为k3进行了补位,而java没有实现这一点(因为java的3DES算法中强制要求key的size必须为24)。这样的情况下,可能就是发送方用k1加密、k2解密、k3再加密;接受方k3解密、k2加密、再k1解密来实现。

 

最终经过编码验证,确认key大小为24时,java和c#的加密解密结果相一致。

Java中实现时,只要注意对大小不足24的key进行补位,和采用CBC模式,填充模式为PKCS5Padding即可。

View Code
  1 public class CDES {
  2 
  3     public static byte[] encrypt(String sKey, byte[] bIV, byte[] bPlainText, int nOffset, int nSize)
  4             throws Exception {
  5         byte[] bKey = buildKey(sKey);
  6         byte[] bInput = buildInput(nSize, bPlainText, nOffset);
  7         byte[] bResult = encrypt(bIV, bKey, bInput);
  8         return bResult;
  9     }
 10 
 11     public static byte[] decrypt(String sKey, byte[] bIV, byte[] bCipherText)
 12             throws Exception {
 13         byte[] bKey = buildKey(sKey);
 14         SecretKey securekey = buildSecretKey(bKey);
 15         IvParameterSpec iv = new IvParameterSpec(bIV);
 16         Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
 17         SecureRandom sr = new SecureRandom();
 18         cipher.init(Cipher.DECRYPT_MODE, securekey, iv, sr);
 19         byte[] bOutput = cipher.doFinal(bCipherText);
 20         int nLen = 0;
 21         nLen = nLen | (int) bOutput[0];
 22         nLen = nLen | ((int) bOutput[1] << 8);
 23         if (nLen > bOutput.length - 4) {
 24             throw new Exception("非法的密文");
 25         }        
 26         byte[] bResult = new byte[bOutput.length  - 4];
 27         bResult = Arrays.copyOfRange(bOutput, 2, bOutput.length - 2);
 28         return bResult;
 29     }
 30 
 31     private static byte[] md5Digest(String sData) throws NoSuchAlgorithmException {
 32         MessageDigest md5 = MessageDigest.getInstance("MD5");
 33         byte[] bData = sData.getBytes();
 34         md5.update(bData, 0, bData.length);
 35         byte[] hash = md5.digest();
 36         return hash;
 37     }
 38 
 39     private static byte[] buildInput(int nSize, byte[] bPlainText, int nOffset) {
 40         byte[] bInput = new byte[nSize + 4];
 41         for (int i = 0; i < bPlainText.length; i++) {
 42             Arrays.fill(bInput, 2 + i, 2 + i + 1, bPlainText[i]);
 43         }
 44         int usCRC = CCRC16.CalcCrcCheckSum(bPlainText, nOffset, nSize);
 45         bInput[0] = (byte) (nSize & 0xFF);
 46         bInput[1] = (byte) (nSize >> 8 & 0xFF);
 47         bInput[nSize + 2] = (byte) (usCRC & 0xFF);
 48         bInput[nSize + 3] = (byte) (usCRC >> 8 & 0xFF);
 49         return bInput;
 50     }
 51 
 52     private static byte[] buildKey(String sKey) throws NoSuchAlgorithmException {
 53         byte[] bTmp = md5Digest(sKey);
 54         byte[] bKey = new byte[24];
 55         System.arraycopy(bTmp,0,bKey,0,bTmp.length);
 56         System.arraycopy(bTmp,0,bKey,16,8);
 57         return bKey;
 58     }
 59 
 60     private static SecretKey buildSecretKey(byte[] bkey) throws InvalidKeyException,
 61             InvalidKeySpecException, NoSuchAlgorithmException {
 62         DESedeKeySpec dks = new DESedeKeySpec(bkey);
 63         SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
 64         SecretKey securekey = keyFactory.generateSecret(dks);
 65         return securekey;
 66     }
 67 
 68     private static byte[] encrypt(byte[] biv, byte[] bkey, byte[] input) throws Exception {
 69         SecretKey securekey = buildSecretKey(bkey);
 70         IvParameterSpec iv = new IvParameterSpec(biv);
 71         Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
 72         SecureRandom sr = new SecureRandom();
 73         cipher.init(Cipher.ENCRYPT_MODE, securekey, iv, sr);
 74         return cipher.doFinal(input);
 75     }
 76 }
 77 
 78 class CCRC16 {
 79     public static int CalcCrcCheckSum(byte[] pBuffer, int nOffset, int nSize) {
 80 
 81         int[] table = {
 82             0x00000xC0C10xC1810x01400xC3010x03C00x02800xC241,
 83             0xC6010x06C00x07800xC7410x05000xC5C10xC4810x0440,
 84             0xCC010x0CC00x0D800xCD410x0F000xCFC10xCE810x0E40,
 85             0x0A000xCAC10xCB810x0B400xC9010x09C00x08800xC841,
 86             0xD8010x18C00x19800xD9410x1B000xDBC10xDA810x1A40,
 87             0x1E000xDEC10xDF810x1F400xDD010x1DC00x1C800xDC41,
 88             0x14000xD4C10xD5810x15400xD7010x17C00x16800xD641,
 89             0xD2010x12C00x13800xD3410x11000xD1C10xD0810x1040,
 90             0xF0010x30C00x31800xF1410x33000xF3C10xF2810x3240,
 91             0x36000xF6C10xF7810x37400xF5010x35C00x34800xF441,
 92             0x3C000xFCC10xFD810x3D400xFF010x3FC00x3E800xFE41,
 93             0xFA010x3AC00x3B800xFB410x39000xF9C10xF8810x3840,
 94             0x28000xE8C10xE9810x29400xEB010x2BC00x2A800xEA41,
 95             0xEE010x2EC00x2F800xEF410x2D000xEDC10xEC810x2C40,
 96             0xE4010x24C00x25800xE5410x27000xE7C10xE6810x2640,
 97             0x22000xE2C10xE3810x23400xE1010x21C00x20800xE041,
 98             0xA0010x60C00x61800xA1410x63000xA3C10xA2810x6240,
 99             0x66000xA6C10xA7810x67400xA5010x65C00x64800xA441,
100             0x6C000xACC10xAD810x6D400xAF010x6FC00x6E800xAE41,
101             0xAA010x6AC00x6B800xAB410x69000xA9C10xA8810x6840,
102             0x78000xB8C10xB9810x79400xBB010x7BC00x7A800xBA41,
103             0xBE010x7EC00x7F800xBF410x7D000xBDC10xBC810x7C40,
104             0xB4010x74C00x75800xB5410x77000xB7C10xB6810x7640,
105             0x72000xB2C10xB3810x73400xB1010x71C00x70800xB041,
106             0x50000x90C10x91810x51400x93010x53C00x52800x9241,
107             0x96010x56C00x57800x97410x55000x95C10x94810x5440,
108             0x9C010x5CC00x5D800x9D410x5F000x9FC10x9E810x5E40,
109             0x5A000x9AC10x9B810x5B400x99010x59C00x58800x9841,
110             0x88010x48C00x49800x89410x4B000x8BC10x8A810x4A40,
111             0x4E000x8EC10x8F810x4F400x8D010x4DC00x4C800x8C41,
112             0x44000x84C10x85810x45400x87010x47C00x46800x8641,
113             0x82010x42C00x43800x83410x41000x81C10x80810x4040,};
114 
115         byte[] bytes = pBuffer;
116         int crc = 0x0000;
117         for (byte b : bytes) {
118             crc = (crc >>> 8) ^ table[(crc ^ b) & 0xff];
119         }
120 
121         return crc;
122     }

  

还有一种让Java和.Net兼容的方式,在.Net中指定模式为ECB,填充为PKCS7,然后在Java中采用其默认的模式(DESede/ECB/PKCS5Padding)即可,注意双方约定key的size为24个字节。建议双方对key以base64编码字符串进行告知,因为java和.net中byte字节的范围不相同(前者-128~127,后者0~255),避免不必要的处理。

原文地址:https://www.cnblogs.com/tearer/p/2284432.html