加密有很多种方式(如:AES、DES等),在不同语言间进行加密对接时,由于各语言间类库的不同,而导致翻译有困难,从而对接失败。因此我们要做的,是先了解对方的加密特点,然后在这边用自己的语言翻译出来。
帅帅最近就遇到了这样一个问题,场景是:
我们做了一个系统A,现在有另一个系统B,要和我们进行单点登录的整合,由于A系统和B系统,有个居村的code,是一样的,因此他们只传居村code(加密后)过来,从而实现登录。
拿到这个问题,我就首先分析了B系统的Java加密类代码,他们是这样写的:
1 import java.math.BigInteger; 2 3 import javax.crypto.Cipher; 4 import javax.crypto.KeyGenerator; 5 import javax.crypto.spec.SecretKeySpec; 6 7 import org.apache.commons.codec.binary.Base64; 8 import org.apache.commons.lang3.StringUtils; 9 10 import sun.misc.BASE64Decoder; 11 /** 12 * AES的加密和解密 13 * @author libo 14 */ 15 public class Aes { 16 //密钥 (需要前端和后端保持一致) 17 public static final String KEY = "key"; 18 //算法 19 private static final String ALGORITHMSTR = "AES/ECB/PKCS5Padding"; 20 21 22 /** 23 * base 64 encode 24 * @param bytes 待编码的byte[] 25 * @return 编码后的base 64 code 26 */ 27 public static String base64Encode(byte[] bytes){ 28 return Base64.encodeBase64String(bytes); 29 } 30 31 /** 32 * AES加密为base 64 code 33 * @param content 待加密的内容 34 * @param encryptKey 加密密钥 35 * @return 加密后的base 64 code 36 * @throws Exception 37 */ 38 public static String aesEncrypt(String content, String encryptKey) throws Exception { 39 return base64Encode(aesEncryptToBytes(content, encryptKey)); 40 } 41 42 /** 43 * AES加密 44 * @param content 待加密的内容 45 * @param encryptKey 加密密钥 46 * @return 加密后的byte[] 47 * @throws Exception 48 */ 49 public static byte[] aesEncryptToBytes(String content, String encryptKey) throws Exception { 50 KeyGenerator kgen = KeyGenerator.getInstance("AES"); 51 kgen.init(128); 52 Cipher cipher = Cipher.getInstance(ALGORITHMSTR); 53 cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), "AES")); 54 55 return cipher.doFinal(content.getBytes("utf-8")); 56 } 57 }
百度了半天,找到一段我觉得比较简洁易懂的代码,试试效果:
/// <summary> /// AES解密 /// </summary> /// <param name="encryptedContent">加密的内容</param> /// <param name="key">密钥</param> /// <returns>解密后的内容</returns> public string AESDecrypt(string encryptedContent, string key) { using (AesCryptoServiceProvider aesProvider = new AesCryptoServiceProvider()) { aesProvider.Key = Convert.FromBase64String(key); aesProvider.Mode = CipherMode.ECB; aesProvider.Padding = PaddingMode.PKCS7; using (ICryptoTransform cryptoTransform = aesProvider.CreateDecryptor()) { byte[] inputBuffers = Convert.FromBase64String(encryptedContent); byte[] results = cryptoTransform.TransformFinalBlock(inputBuffers, 0, inputBuffers.Length); aesProvider.Clear(); return Encoding.UTF8.GetString(results); } } }
但是,运行起来报错:
System.Security.Cryptography.CryptographicException:“指定的密钥大小对于此算法无效。”
欲哭无泪~
后来,我通过下面这篇文章,找到了启示:https://www.cnblogs.com/yetiea/articles/3858669.html
修改了下我的C#解密代码,运行一看,您猜怎么着,解决啦!
正确代码如下:
1 /// <summary> 2 /// AES解密 3 /// </summary> 4 /// <param name="encryptedContent">加密的内容</param> 5 /// <param name="key">密钥</param> 6 /// <returns>解密后的内容</returns> 7 public string AESDecrypt(string encryptedContent, string key) 8 { 9 using (AesCryptoServiceProvider aesProvider = new AesCryptoServiceProvider()) 10 { 11 aesProvider.BlockSize = 128; //这句可以不写,因为默认128;写出来只是为了和上面Java的kgen.init(128)做个比对 12 aesProvider.Key = Encoding.UTF8.GetBytes(key); 13 aesProvider.Mode = CipherMode.ECB; 14 aesProvider.Padding = PaddingMode.PKCS7; 15 16 using (ICryptoTransform cryptoTransform = aesProvider.CreateDecryptor()) 17 { 18 byte[] inputBuffers = Convert.FromBase64String(encryptedContent); 19 byte[] results = cryptoTransform.TransformFinalBlock(inputBuffers, 0, inputBuffers.Length); 20 aesProvider.Clear(); 21 return Encoding.UTF8.GetString(results); 22 } 23 } 24 }
总结:
1.Java的AES,对应C#的AesCryptoServiceProvider
这里可以举一反三,如:Java的DES,对应C#的DESCryptoServiceProvider 等等。
2.注意Java加密里这段代码:
String ALGORITHMSTR = "AES/ECB/PKCS5Padding"; //算法
Cipher.getInstance(ALGORITHMSTR);
算法里的DES、ECB、PKCS5Padding,分别解释如下:
AES,我们就不说了,上面已经阐述过了;
ECB,对应C#写法的CipherMode.ECB(参考上方C#代码第13行);
PKCS5Padding,对应C#写法的PaddingMode.PKCS7(参考上方C#代码第14行);
3.注意key是不是base64加密了
如果没加密,用Encoding.UTF8.GetBytes(key) 转为byte[](当然,要注意java那边的编码形式),
如果加密了,用Convert.FromBase64String(key) 转为byte[]
OK,搞定收工,拿碗排队打饭!
如果有帮助到你,可以的话请帮我点个赞吧,谢谢~