最近因?yàn)闃I(yè)務(wù)需求,需要把一段C#編寫(xiě)的加密代碼采用Java進(jìn)行實(shí)現(xiàn),在此記下我的實(shí)施過(guò)程和過(guò)程中碰到的一些問(wèn)題以備后用。為了便于觀察,只貼上C#源碼中關(guān)鍵的部分:
1 ///
2 ///<summary>
3 /// 加密方法
4 /// </summary>
5 /// <param name=\"Source\">待加密的串</param>
6 /// <returns>經(jīng)過(guò)加密的串</returns>
7 public static string Encrypto(string Source)
8 {
9 byte[] bytIn = UTF8Encoding.UTF8.GetBytes(Source);
10 MemoryStream ms = new MemoryStream();
11 mobjCryptoService.Key = GetLegalKey();
12 mobjCryptoService.IV = GetLegalIV();
13 //創(chuàng)建對(duì)稱(chēng)加密器對(duì)象
14 //下面的控制臺(tái)打印是我自己添加方便觀察的
15 Console.WriteLine("KeySize:" + mobjCryptoService.KeySize);
16 Console.WriteLine("LegalKeySizes:" + mobjCryptoService.LegalKeySizes.Length);
17 Console.WriteLine("Mode:"+ mobjCryptoService.Mode);
18 Console.WriteLine("Padding:"+mobjCryptoService.Padding);
19 ICryptoTransform encrypto = mobjCryptoService.CreateEncryptor();
20 //定義將數(shù)據(jù)流鏈接到加密轉(zhuǎn)換的流
21 CryptoStream cs = new CryptoStream(ms, encrypto, CryptoStreamMode.Write);
22 cs.Write(bytIn, 0, bytIn.Length);
23 cs.FlushFinalBlock();
24 ms.Close();
25 byte[] bytOut = ms.ToArray();
26
27 return Convert.ToBase64String(bytOut);
28 }
29
30 ///
31 ///<summary>
32 /// 解密方法
33 /// </summary>
34 /// <param name=\"Source\">待解密的串</param>
35 /// <returns>經(jīng)過(guò)解密的串</returns>
36 public static string Decrypto(string Source)
37 {
38 try
39 {
40 byte[] bytIn = Convert.FromBase64String(Source);
41 MemoryStream ms = new MemoryStream(bytIn, 0, bytIn.Length);
42 mobjCryptoService.Key = GetLegalKey();
43 mobjCryptoService.IV = GetLegalIV();
44 //創(chuàng)建對(duì)稱(chēng)解密器對(duì)象
45 ICryptoTransform encrypto = mobjCryptoService.CreateDecryptor();
46 //定義將數(shù)據(jù)流鏈接到加密轉(zhuǎn)換的流
47 CryptoStream cs = new CryptoStream(ms, encrypto, CryptoStreamMode.Read);
48 StreamReader sr = new StreamReader(cs);
49 return sr.ReadToEnd();
50 }
51 catch (Exception)
52 {
53 return string.Empty;
54 }
55 }
其中有一個(gè)問(wèn)題:
1 //生成加密器的對(duì)象由RijndaelManaged得來(lái),那么這個(gè)RijndaelManaged對(duì)象是干嘛的?第三方的?原生的?
2 private static SymmetricAlgorithm mobjCryptoService = new RijndaelManaged();
調(diào)查之后,好吧,它是一種對(duì)稱(chēng)加密算法(可以理解為在雙方之間可以實(shí)現(xiàn)加解密),以下來(lái)自百科:
高級(jí)加密標(biāo)準(zhǔn)(英語(yǔ):Advanced Encryption Standard,縮寫(xiě):AES),在密碼學(xué)中又稱(chēng)Rijndael加密法,是美國(guó)聯(lián)邦政府采用的一種區(qū)塊加密標(biāo)準(zhǔn)。這個(gè)標(biāo)準(zhǔn)用來(lái)替代原先的DES,已經(jīng)被多方分析且廣為全世界所使用。經(jīng)過(guò)五年的甄選流程,高級(jí)加密標(biāo)準(zhǔn)由美國(guó)國(guó)家標(biāo)準(zhǔn)與技術(shù)研究院(NIST)于2001年11月26日發(fā)布于FIPS PUB 197,并在2002年5月26日成為有效的標(biāo)準(zhǔn)。2006年,高級(jí)加密標(biāo)準(zhǔn)已然成為對(duì)稱(chēng)密鑰加密中最流行的算法之一。
…
之前對(duì)加密標(biāo)準(zhǔn)認(rèn)識(shí)的并不多,但是通過(guò)代碼來(lái)看,似乎實(shí)現(xiàn)對(duì)稱(chēng)加密還需要?jiǎng)e的材料,看到下面這兩個(gè)方法
1 /// <summary>
2 /// 獲得密鑰
3 /// </summary>
4 /// <returns>密鑰</returns>
5 private static byte[] GetLegalKey()
6 {
7 string _TempKey = Key;//Key已經(jīng)預(yù)先寫(xiě)死在代碼里了
8 mobjCryptoService.GenerateKey();
9 byte[] bytTemp = mobjCryptoService.Key;
10 int KeyLength = bytTemp.Length;
11 if (_TempKey.Length > KeyLength)
12 _TempKey = _TempKey.Substring(0, KeyLength);
13 else if (_TempKey.Length < KeyLength)
14 _TempKey = _TempKey.PadRight(KeyLength, ' ');
15 return ASCIIEncoding.ASCII.GetBytes(_TempKey);
16 }
17 //GetLegalKey方法每次都會(huì)返回一個(gè)長(zhǎng)度為32的byte數(shù)組
18 //mobjCryptoService.Key = GetLegalKey();
19
20 /// <summary>
21 /// 獲得初始向量IV
22 /// </summary>
23 /// <returns>初試向量IV</returns>
24 private static byte[] GetLegalIV()
25 {
26 string _TempIV = "$%^&*()_osudghwe7%%2kdijskldglk2397^&*wigwuqwelkn";//這是虛構(gòu)的
27 mobjCryptoService.GenerateIV();
28 byte[] bytTemp = mobjCryptoService.IV;
29 int IVLength = bytTemp.Length;
30 if (_TempIV.Length > IVLength)
31 _TempIV = _TempIV.Substring(0, IVLength);
32 else if (_TempIV.Length < IVLength)
33 _TempIV = _TempIV.PadRight(IVLength, ' ');
34 return ASCIIEncoding.ASCII.GetBytes(_TempIV);
35 }
36 //GetLegalIV方法每次都會(huì)返回一個(gè)長(zhǎng)度為16的byte數(shù)組
37 //mobjCryptoService.IV = GetLegalIV();
雖然是通過(guò)兩個(gè)方法來(lái)取得的KEY和IV(二者都是byte數(shù)組),但是KEY和IV都是預(yù)先寫(xiě)死在代碼里的,只不過(guò)是返回了特定長(zhǎng)度的byte數(shù)組。
1 //RijndaelManaged,這是對(duì)稱(chēng)算法合法的key和IV的長(zhǎng)度(分別以位來(lái)計(jì)算)
2 // Legal min key size = 128
3 // Legal max key size = 256
4 // Legal min block size = 128
5 // Legal max block size = 256
經(jīng)過(guò)調(diào)查,還需要配置算法的mode和padding,此處并沒(méi)有進(jìn)行設(shè)置,所以默認(rèn)分別為CipherMode.CBC和PaddingMode.PKCS7。詳見(jiàn)
mode和
padding
目前來(lái)看,需要調(diào)查的可以告一段落了,那么接下來(lái),就需要調(diào)查如何Java實(shí)現(xiàn)了。在Java中,實(shí)現(xiàn)加密需要用到JCE提供的Cipher對(duì)象,類(lèi)比C#的實(shí)現(xiàn),借鑒廣大程序員的智慧,我首先嘗試寫(xiě)了一下(只貼出關(guān)鍵部分)
1 public void init(byte[] keyBytes) {
2
3 // 如果密鑰不足16位,那么就補(bǔ)足. 這個(gè)if 中的內(nèi)容很重要
4 int base = 16;
5 if (keyBytes.length % base != 0) {
6 int groups = keyBytes.length / base
7 + (keyBytes.length % base != 0 ? 1 : 0);
8 byte[] temp = new byte[groups * base];
9 Arrays.fill(temp, (byte) 0);
10 System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length);
11 keyBytes = temp;
12 }
13 // 轉(zhuǎn)化成JAVA的密鑰格式
14 key = new SecretKeySpec(keyBytes, "AES");
15 try {
16 // 初始化cipher
17 cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
18 } catch (NoSuchAlgorithmException e) {
19 e.printStackTrace();
20 } catch (NoSuchPaddingException e) {
21 e.printStackTrace();
22 }
23 }
24
25 /**
26 * 加密方法
27 * @param content 待加密內(nèi)容字符串
28 * @param keySrc 加解密密秘鑰字符串
29 * @param iv 算法所需向量
30 * @return
31 * @throws UnsupportedEncodingException
32 */
33 public String encrypt(String content, String keySrc,String iv) throws UnsupportedEncodingException {
34 byte[] encryptedText = null;
35 init(keySrc.getBytes("utf-8"));
36 try {
37 cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv.getBytes()));
38 encryptedText = cipher.doFinal(content.getBytes("utf-8"));
39 } catch (Exception e) {
40 e.printStackTrace();
41 }
42 return Base64.encodeBase64String(encryptedText);
43 }
44
45 /**
46 * 解密方法
47 * @param encryptedData 待解密數(shù)據(jù)
48 * @param keySrc 加解密密秘鑰字符串
49 * @param iv 算法所需向量
50 * @return
51 * @throws UnsupportedEncodingException
52 */
53 public String decrypt(String encryptedData, String keySrc,String iv) throws UnsupportedEncodingException {
54 byte[] decryptedText = null;
55 init(keySrc.getBytes("utf-8"));
56 try {
57 cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv.getBytes()));
58 decryptedText = cipher.doFinal(Base64.decodeBase64(encryptedData));
59 } catch (Exception e) {
60 e.printStackTrace();
61 }
62 return new String(decryptedText);
63 }
64
65 public static void main(String[] args) throws UnsupportedEncodingException {
66 EncryptingUtil eu = new EncryptingUtil();
67
68 String content = "abc";
69 // 加密字符串
70 System.out.println("加密前的:" + content);
71 // 加密方法
72 String enc = eu.encrypt(content,"秘鑰值","向量值");
73 System.out.println(enc);
74 // 解密方法
75 String dec = eu.decrypt(enc,"秘鑰值", "向量值");
76 System.out.println("解密后的內(nèi)容:" + dec);
77 }
Java版本的實(shí)現(xiàn),目前有幾個(gè)問(wèn)題,一個(gè)就是補(bǔ)齊方式的問(wèn)題,在C#中默認(rèn)的補(bǔ)齊方式PKCS7,但是Java默認(rèn)并不支持該補(bǔ)齊方式,只有PKCS5;其次就是秘鑰的長(zhǎng)度問(wèn)題,C#代碼中的秘鑰長(zhǎng)度是256位的,但是Java默認(rèn)最大支持128位的,執(zhí)行時(shí)會(huì)報(bào)出”Illegal key size”的問(wèn)題。這位大牛的東東幫到我了,感謝。
查看
當(dāng)需要使用256位長(zhǎng)度的秘鑰時(shí),需要下載對(duì)應(yīng)Java版本的JCE拓展包,圖中的問(wèn)題就是因?yàn)镴CE拓展包和Java版本不匹配引起的