用 Java 解密 C# 加密的数据(DES)【转】

[原文地址:http://yidinghe.cnblogs.com/articles/449212.html]
今天碰上一件令我头大的事情。我们的系统要和一个外部系统进行通讯,传输方式是采用 DES 算法对消息进行加密,再用 BASE64 编码。不过对方系统是用 C# 写的。平台不一样,于是我和对面的老兄先测试一下加密解密。这一测试问题就来了。两边采用同样的密钥,对同一个字符串加密出来的结果不一样。怎么办呢?我先把他那边的代码要过来了,他是这样写的(C#):

    public static string Encrypt(string message, string key)     {         DES des = new DESCryptoServiceProvider();         des.Key = Encoding.UTF8.GetBytes(key);         des.IV = Encoding.UTF8.GetBytes(key);
        
byte[] bytes = Encoding.UTF8.GetBytes(message);         byte[] resultBytes = des.CreateEncryptor().TransformFinalBlock(bytes, 0, bytes.Length);
        
return Convert.ToBase64String(resultBytes);     }  

而我这边呢,是用 Java 这样写的(仅仅加密到字节串,BASE64 编码是在另一个类里):

    public static byte[] desEncrypt(String message, String keythrows Exception {         Cipher cipher = Cipher.getInstance("DES");
        DESKeySpec desKeySpec 
= new DESKeySpec(key.getBytes("UTF-8"));         SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");         SecretKey secretKey = keyFactory.generateSecret(desKeySpec);         cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        
return cipher.doFinal(message.getBytes("UTF-8"));     }

两边一对比我就奇怪了,他那边怎么有个 IV (initialization vector)?我打开JDK文档一看Cipher类,竟然也有一个getIV()方法。看来这 IV 大有来头。于是在JDK文档里面到处找IV,找到了一个叫“IvParameterSpec“的类,莫非就是它?它实现的是 AlgorithmParameterSpec 接口,而Cipher.init()中就有一个 AlgorithmParameterSpec 类型的参数。那么看来就是它了。于是参考了IvParameterSpec的构造函数后,我这样把它加进去:

    public static byte[] desEncrypt(String message, String key) throws Exception {         Cipher cipher = Cipher.getInstance("DES");
        DESKeySpec desKeySpec 
= new DESKeySpec(key.getBytes("UTF-8"));
        SecretKeyFactory keyFactory 
= SecretKeyFactory.getInstance("DES");         SecretKey secretKey = keyFactory.generateSecret(desKeySpec);         IvParameterSpec iv = new IvParameterSpec(key.getBytes("UTF-8"));         cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
        
return cipher.doFinal(message.getBytes("UTF-8"));     }

这下行了吧,结果一运行报异常:
java.security.InvalidAlgorithmParameterException: ECB mode cannot use IV
这又是什么?    “ECB”?  还是找吧,在JDK文档中找到了这么几句鬼东西:

the SunJCE provider uses ECB as the default mode, and PKCS5Padding as the default padding scheme for DES, DES-EDE and Blowfish ciphers. This means that in the case of the SunJCE provider,     Cipher c1 = Cipher.getInstance("DES/ECB/PKCS5Padding"); and     Cipher c1 = Cipher.getInstance("DES"); are equivalent statements.

哦,问题就出在方法里的第一行。那么不准用 ECB,该用什么呢?在该文往下找,找到了这么几项:

The following names can be specified as the mode component in a transformation when requesting an instance of Cipher:

  • NONE: No mode.    
  • CBC: Cipher Block Chaining Mode, as defined in FIPS PUB 81.    
  • CFB: Cipher Feedback Mode, as defined in FIPS PUB 81.    
  • ECB:    Electronic Codebook Mode, as defined in: The National Institute of Standards and    Technology (NIST) Federal Information Processing Standard (FIPS) PUB 81, "DES    Modes of Operation," U.S. Department of Commerce, Dec 1980.    
  • OFB: Output Feedback Mode, as defined in FIPS PUB 81.    
  • PCBC: Propagating Cipher Block Chaining, as defined by Kerberos V4.    

我这样都换着试一下就行了。哎,运气不错,拿着第一个 CBC 试一下就成功了,加密后的字符串也“正确”了。 我那个方法就变成这样:

    public static byte[] desEncrypt(String message, String key) throws Exception {         Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
        DESKeySpec desKeySpec 
= new DESKeySpec(key.getBytes("UTF-8"));
        SecretKeyFactory keyFactory 
= SecretKeyFactory.getInstance("DES");         SecretKey secretKey = keyFactory.generateSecret(desKeySpec);         IvParameterSpec iv = new IvParameterSpec(key.getBytes("UTF-8"));         cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
        
return cipher.doFinal(message.getBytes("UTF-8"));     }

这样,解决了这个 ECB 之后,Java 和 .net 平台的两个应用终于能够相互加密和解密数据了。什么?文不对题?加密都写出来了,解密还不简单?自己看着办吧。
[原文地址:http://yidinghe.cnblogs.com/articles/449212.html]

posted @ 2016-02-02 14:02  J.Y  阅读(182)  评论(0编辑  收藏  举报