標籤:split text intern span lan tsp 參數 create turn
引用:http://blog.csdn.net/dslinmy/article/details/37362661
這裡,講一下RSA演算法加解密在C#和Java之間互動的問題,這兩天糾結了很久,也看了很多其他人寫的文章,頗受裨益,但沒能解決我的實際問題,終於,還是被我搗鼓出來了。
首先,介紹一下寫這代碼的目的:完成webService驗證問題,伺服器端採用C#開發,用戶端採用Java開發。伺服器端給用戶端提供公開金鑰,已進行資料加密,用戶端加密後提資料提交給伺服器,伺服器用私密金鑰對資料解密,進行驗證。
這裡遇到的主要問題是C# RSACryptoServiceProvider類產生的公開金鑰、私密金鑰都是xml字串資料,而java RSA演算法要求的 Modulus、Exponent都是BigInteger類型,兩者間的轉換才是問題所在。
關於Java 和 C#各自獨立的進行RSA加密解密,大家可以看整兩篇文章,java RSA加密解密實現() 和 C#中RSA加密解密和簽名與驗證的實現。
接下來講一下實現步驟:
首先由C# RSACryptoServiceProvider類產生公開金鑰、私密金鑰
/// <summary> /// 產生公開金鑰、私密金鑰 /// </summary> /// <returns>公開金鑰、私密金鑰,公開金鑰鍵"PUBLIC",私密金鑰鍵"PRIVATE"</returns> public Dictionary<string, string> createKeyPair() { Dictionary<string, string> keyPair = new Dictionary<string, string>(); RSACryptoServiceProvider provider = new RSACryptoServiceProvider(1024); keyPair.Add("PUBLIC", provider.ToXmlString(false)); keyPair.Add("PRIVATE", provider.ToXmlString(true)); return keyPair; }
如此處產生的公開金鑰為
<RSAKeyValue> <Modulus>t+56m5jXXonAJAKC7mgkhAZX5gWJTZojbSloLpLBGEWiebFaM+aUUKALfRx83/HaUV79ZiR3zuLJOLBdALx1cmcPk/b9fdNblLmzqi4cfSnfmMLWh05xf+ZS1pKHSKQtui3dfuu+3XH6Ak+S38dpIZUj/hihQQuKysN6GJ9h+c8= </Modulus> <Exponent>AQAB</Exponent> </RSAKeyValue>
在用戶端(Java)對C#提供的公開金鑰提取Modulus和Exponent
/** * 返回包含模數modulus和指數exponent的haspMap * @return * @throws MalformedURLException * @throws DocumentException */ public static HashMap<String,String> rsaParameters(String xmlPublicKey) throws MalformedURLException, DocumentException{ HashMap<String ,String> map = new HashMap<String, String>(); Document doc = DocumentHelper.parseText(xmlPublicKey); String mudulus = (String) doc.getRootElement().element("Modulus").getData(); String exponent = (String) doc.getRootElement().element("Exponent").getData(); map.put("mudulus", mudulus); map.put("exponent", exponent); return map; }
用Modulus和Exponent產生公開金鑰RSAPublicKey(java)
這裡有個關鍵步驟先對Mudolus和Exponent進行Base64解碼,這個是由於C#產生的金鑰組,其參數已經過Base64編碼成String類型,而java RSA參數是未經base64編碼的byte[]類型。
至於Base64編碼、解碼方法,參考這篇文章,java 編碼和解碼,想詳細。
public static byte[] decodeBase64(String input) throws Exception{ Class clazz=Class.forName("com.sun.org.apache.xerces.internal.impl.dv.util.Base64"); Method mainMethod= clazz.getMethod("decode", String.class); mainMethod.setAccessible(true); Object retObj=mainMethod.invoke(null, input); return (byte[])retObj; } /** * 返回RSA公開金鑰 * @param modules * @param exponent * @return */ public static PublicKey getPublicKey(String modulus, String exponent){ try { byte[] m = decodeBase64(modulus); byte[] e = decodeBase64(exponent); BigInteger b1 = new BigInteger(1,m); BigInteger b2 = new BigInteger(1,e); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); RSAPublicKeySpec keySpec = new RSAPublicKeySpec(b1, b2); return (RSAPublicKey) keyFactory.generatePublic(keySpec); } catch (Exception e) { e.printStackTrace(); return null; } }
獲得公開金鑰後就可以進行RSA加密處理了,這裡還有一點需要提的是,RSA加密解密都有最大長度限制,加密最大長度為117位元組,解密最大長度是128位元組,此外,此處加密得到的資料是經過Base64編碼處理的
public static String encrypt(byte[] source, PublicKey publicKey) throws Exception { String encryptData =""; try { Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); int length = source.length; int offset = 0; byte[] cache; ByteArrayOutputStream outStream = new ByteArrayOutputStream(); int i = 0; while(length - offset > 0){ if(length - offset > MAXENCRYPTSIZE){ cache = cipher.doFinal(source, offset, MAXENCRYPTSIZE); }else{ cache = cipher.doFinal(source, offset, length - offset); } outStream.write(cache, 0, cache.length); i++; offset = i * MAXENCRYPTSIZE; } return encodeBase64(outStream.toByteArray()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } return encryptData; }
加密後的資料提交給C#伺服器端進行解密,當然,這裡也要注意最大長度限制問題
/// <summary> /// RSA解密 /// </summary> /// <param name="encryptData">經過Base64編碼的密文</param> /// <param name="privateKey">私密金鑰</param> /// <returns>RSA解密後的資料</returns> public static string decrypt(string encryptData, string privateKey) { string decryptData = ""; try { RSACryptoServiceProvider provider = new RSACryptoServiceProvider(); provider.FromXmlString(privateKey); byte[] bEncrypt = Convert.FromBase64String(encryptData); int length = bEncrypt.Length; int offset = 0; string cache ; int i = 0; while (length - offset > 0) { if (length - offset > MAXDECRYPTSIZE) { cache = Encoding.UTF8.GetString(provider.Decrypt(getSplit(bEncrypt, offset, MAXDECRYPTSIZE), false)); } else { cache = Encoding.UTF8.GetString(provider.Decrypt(getSplit(bEncrypt, offset, length - offset), false)); } decryptData += cache; i++; offset = i*MAXDECRYPTSIZE; } } catch(Exception e) { throw e; } return decryptData; } /// <summary> /// 截取位元組數組部分位元組 /// </summary> /// <param name="input"></param> /// <param name="offset">起始位移位</param> /// <param name="length">截取長度</param> /// <returns></returns> private static byte[] getSplit(byte[] input, int offset, int length) { byte[] output = new byte[length]; for (int i = offset; i < offset + length; i++) { output[i - offset] = input[i]; } return output; }
這樣,就順利完成了。
經過測試,這樣做的確得到了正確的結果。
若是有什麼地方有問題,還望大家指正!
C# Java間進行RSA加密解密互動