Bootstrap

跨Java和.Net的DES加解密

Java的DES加解密和.Net的DES加解密两者相互转换或通用的问题

近期遇到了一个DES加解密的问题,由于项目上的原因那边的DES加密是通过.Net开发,但是我这边的项目又是用Java写的,所以拿到那边传过来的密文数据后我无法对其进行解密,同时我这边加密的数据到了他们.Net那边后,他们那边也无法正常使用,所以着手做一个两边通用的DES加解密。

.Net的源码:

提示:这里是.Net的DES加解密源码
我这里把秘钥去掉了,但是不影响整体逻辑,秘钥你自己加一个就行了,如果要通用的话必须保证C# 的秘钥和Java的秘钥一样。

        //加解密的密钥
        public static string strKey ="A&FX****";
        // 向量
         public static byte[] strIV = { 0x30, 0x76, 0x18, 0x50, 0x44, 0xAC, 0xBD, 0x55 };

        //加密
        public static string Encrypt(string value)
        {
            
            using (DESCryptoServiceProvider des = new DESCryptoServiceProvider())
            {
                byte[] inputByteArray = Encoding.UTF8.GetBytes(value);
                des.Key = ASCIIEncoding.ASCII.GetBytes(strKey);
                des.IV = strIV;
                des.Mode = CipherMode.CBC;
                des.Padding = PaddingMode.Zeros;
                System.IO.MemoryStream ms = new System.IO.MemoryStream();
                using (CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(inputByteArray, 0, inputByteArray.Length);
                    cs.FlushFinalBlock();
                    cs.Close();
                }
                String s = Convert.ToBase64String(ms.ToArray()).Replace("+", "-");
                s = s.Replace("/","_");
                ms.Close();
                return s;
            }
        }

        //解密
        public static string Decrypt(string value)
        {
            try{
            value = value.Replace("-","+");
            value = value.Replace("_", "/");        
            byte[] inputByteArray = Convert.FromBase64String(value);
            using (DESCryptoServiceProvider des = new DESCryptoServiceProvider())
            {
                des.Key = ASCIIEncoding.ASCII.GetBytes(strKey);
                des.IV = strIV;
                des.Mode = CipherMode.CBC;
                des.Padding = PaddingMode.Zeros;
                System.IO.MemoryStream ms = new System.IO.MemoryStream();
                using (CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(inputByteArray, 0, inputByteArray.Length);
                    cs.FlushFinalBlock();
                    cs.Close();
                }
                String s = Encoding.UTF8.GetString(ms.ToArray()).Replace("\0","");
                ms.Close();
                return s;
                }
              }catch (Exception e)
                {
                      String rs = e.ToString();
                      return "Excepion";
                }
        }

Java的源码:

提示:这里是java的DES加解密源码:
同样的我这里把秘钥去掉了,但实际开发中也一定保证秘钥和C#端的一致。

    // 加解密的秘钥
    public static String strKey = "A&FX****";
    // 向量
    public static byte[] strIV = {0x30, 0x76, 0x18, 0x50, 0x44, (byte) 0xAC, (byte) 0xBD, 0x55};
    
    /**
     * 加密
     *
     * @param data 待加密的数据
     * @return 加密后的数据
     * @throws Exception
     */
    public static String encode(String data) {
        try {
            KeySpec ks = new DESKeySpec(strKey.getBytes("UTF-8"));
            SecretKey key = SecretKeyFactory.getInstance("DES").generateSecret(ks);
            IvParameterSpec iv = new IvParameterSpec(strIV);
            Cipher enCipher = Cipher.getInstance("DES/CBC/NoPadding");// 设置填充方式
            enCipher.init(Cipher.ENCRYPT_MODE, key, iv);// 设置工作模式为加密模式,给出密钥和向量
            byte[] b = data.getBytes("UTF-8");// 当这里的字节数不是8的倍数的时候
            int remainder = b.length % 8;
            if (0 != remainder) {
                int oldLength = b.length;
                // 1.扩展自身长度
                b = Arrays.copyOf(b, b.length + 8 - remainder);
                // 2.填充扩展内容为0
                Arrays.fill(b, oldLength, b.length, (byte) 0);
            }
            byte[] pasByte = enCipher.doFinal(b); // 这边就必须要补字节来达到离当前字节最近的8的倍数,也就是遵循了DES加密的原则
            String code = Base64.getEncoder().encodeToString(pasByte);// 所以,后面就会补上几个字节
            String Encode;
            Encode = code.replace("+", "-");
            Encode = Encode.replace("/", "_");
            return Encode;
        } catch (Exception ex) {
            System.out.println(ex);
            return "exception";
        }
    }

    /**
     * 解密
     *
     * @param data 解密前的数据
     * @return 解密后的数据
     * @throws Exception
     */
    public static String decode(String data) {
        try {
            data = data.replace("-", "+");
            data = data.replace("_", "/");
            KeySpec ks = new DESKeySpec(strKey.getBytes("UTF-8"));
            SecretKey key = SecretKeyFactory.getInstance("DES").generateSecret(ks);

            IvParameterSpec iv = new IvParameterSpec(strIV);
            Cipher deCipher = Cipher.getInstance("DES/CBC/NoPadding");
            deCipher.init(Cipher.DECRYPT_MODE, key, iv);
            byte[] b = data.getBytes("ASCII");
            byte[] pasByte = deCipher.doFinal(Base64.getDecoder().decode(b));

            return new String(pasByte, "UTF-8").replace("\0", "");
        } catch (Exception ex) {
            System.out.println(ex);
            return "exception";
        }
    }
    
     public static void main(String[] args) {
        String Encode = encode("865771051726461_110*Cmd_100");
        System.out.println("加密后:" + Encode);
        System.out.println("直接解密C#那边:" + decode("FcztQMpmwkudoN1lVJMWYrkJG7dzhAaL4OdMz6M_NDBNJMWczkpcgkUArm3t4rJ8"));
        System.out.println("经Java加密后再解密:" + decode(Encode));
    }
}

注意点:

我主要做的是Java这边,关键就在于两个点:
一是,如果DES加密格式统一都是CBC格式,那么秘钥和向量两边必须一致;
二是,填充方式的问题,比如我上面的例子,注意看C#的代码有这么一段“des.Padding = PaddingMode.Zeros”,这段代码则表示C#那边不进行填充,所以Java这边也要使用这种方式,所以我Java这边的加解密方法都有这么一段代码“DES/CBC/NoPadding”,这个“NoPadding”就等同于C#那边的“Zeros”,代表不填充。其实主要就在于格式上面,一定要两边的格式都统一,这是基础。

;