加密有很多種方式(如: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,搞定收工,拿碗排隊打飯!
如果有幫助到你,可以的話請幫我點個贊吧,謝謝~