Java Web項目RSA加密,javawebrsa加密
最近做的一個項目,伺服器為Java,採用SSH架構,用戶端為Android和IOS。當使用者登入時,從用戶端向伺服器提交使用者名稱和密碼。這就存在一個問題,如果資料包在網路上被其他人截取了,密碼就有可能泄露。
可以採用Base64對密碼編碼,但是Base64要進行解碼是很容易的事。
另一種方法是對密碼進行MD5加密,MD5是無法復原的,只能加密不能解密。但是其他人截取了密碼的MD5字串以後,可以原封不動的將MD5加密後的字串提交給伺服器,伺服器肯定會判斷這是正確的密碼,這樣還是可以登入進去。
解決的方法就只能採用密碼編譯演算法了。密碼編譯演算法分為對稱式加密和非對稱式加密。對稱式加密演算法,加密和解密使用相同的密鑰,密鑰在網路傳輸的過程中有可能被截取,所以不是很安全。非對稱式加密,使用公開金鑰加密,只能使用私密金鑰解密,公開金鑰是公開的,私密金鑰是不公開的。即使在傳遞的過程中,公開金鑰被其他人擷取了也無所謂,因為公開金鑰是用來加密的,只有私密金鑰才能解密,而私密金鑰是不會傳遞的,也就不可能被其他人擷取。
非對稱式加密最常用的就是RSA演算法,RSA演算法是由羅納德·李維斯特(Ron Rivest)、阿迪·薩莫爾(Adi Shamir)和倫納德·阿德曼(Leonard Adleman)一起提出的,取了他們姓的第一個字母來命名。RSA演算法的原理就不講了。密鑰長度為768的RSA演算法有可能被破解,密鑰長度為1024的RSA演算法還沒有被破解,所以可以認為密鑰長度為1024的RSA演算法是比較安全的。但是RSA演算法的計算量大,一般只用於關鍵資訊的加密,如密碼、對稱式加密演算法的密鑰等。在我們的項目中,就使用RSA演算法對使用者密碼進行加密。具體的步驟如下:
1. 用戶端向伺服器申請密鑰;
2. 伺服器接收到用戶端的申請以後,產生一對密鑰,將公開金鑰發給用戶端,私密金鑰自己儲存;
3. 用戶端接收到公開金鑰以後,使用公開金鑰對密碼加密,然後將密文發給伺服器;
4. 伺服器接收到密文以後,使用私密金鑰解密,判斷是否是正確的密碼。
下面是關鍵代碼。
產生密鑰和加密、解密的代碼:
/** * 產生公開金鑰和私密金鑰 * @throws NoSuchAlgorithmException * */ public static HashMap<String, Object> getKeys() throws NoSuchAlgorithmException{ HashMap<String, Object> map = new HashMap<String, Object>(); KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); keyPairGen.initialize(1024); KeyPair keyPair = keyPairGen.generateKeyPair(); RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); map.put("public", publicKey); map.put("private", privateKey); return map; } /** * 使用模和指數產生RSA公開金鑰 * 注意:【此代碼用了預設補位方式,為RSA/None/PKCS1Padding,不同JDK預設的補位方式可能不同,如Android預設是RSA * /None/NoPadding】 * * @param modulus * 模 * @param exponent * 指數 * @return */ public static RSAPublicKey getPublicKey(String modulus, String exponent) { try { BigInteger b1 = new BigInteger(modulus); BigInteger b2 = new BigInteger(exponent); 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/None/PKCS1Padding,不同JDK預設的補位方式可能不同,如Android預設是RSA * /None/NoPadding】 * * @param modulus * 模 * @param exponent * 指數 * @return */ public static RSAPrivateKey getPrivateKey(String modulus, String exponent) { try { BigInteger b1 = new BigInteger(modulus); BigInteger b2 = new BigInteger(exponent); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(b1, b2); return (RSAPrivateKey) keyFactory.generatePrivate(keySpec); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 公開金鑰加密 * * @param data * @param publicKey * @return * @throws Exception */ public static String encryptByPublicKey(String data, RSAPublicKey publicKey) throws Exception { Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); // 模長 int key_len = publicKey.getModulus().bitLength() / 8; // 加密資料長度 <= 模長-11 String[] datas = splitString(data, key_len - 11); String mi = ""; //如果明文長度大於模長-11則要區塊編碼器 for (String s : datas) { mi += bcd2Str(cipher.doFinal(s.getBytes())); } return mi; } /** * 私密金鑰解密 * * @param data * @param privateKey * @return * @throws Exception */ public static String decryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception { Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding"); cipher.init(Cipher.DECRYPT_MODE, privateKey); //模長 int key_len = privateKey.getModulus().bitLength() / 8; byte[] bytes = data.getBytes(); byte[] bcd = ASCII_To_BCD(bytes, bytes.length); System.err.println(bcd.length); //如果密文長度大於模長則要分組解密 String ming = ""; byte[][] arrays = splitArray(bcd, key_len); for(byte[] arr : arrays){ ming += new String(cipher.doFinal(arr)); } return ming; }
伺服器收到用戶端的請求時,產生一對密鑰:
HashMap<String, Object> mymap = RSAUtils.getKeys(); // 產生公開金鑰和私密金鑰 RSAPublicKey publicKey = (RSAPublicKey) mymap.get("public"); RSAPrivateKey privateKey = (RSAPrivateKey) mymap.get("private"); // 模 String modulus = publicKey.getModulus().toString(); // 公開金鑰指數 String public_exponent = publicKey.getPublicExponent().toString(); // 私密金鑰指數 String private_exponent = privateKey.getPrivateExponent().toString(); // 使用模和指數產生公開金鑰和私密金鑰 RSAPublicKey pubKey = RSAUtils.getPublicKey(modulus, public_exponent); RSAPrivateKey priKey = RSAUtils.getPrivateKey(modulus, private_exponent);
將其中的模和私密金鑰指數發給用戶端,用戶端收到以後,使用getPrivateKey(String modulus, String exponent)產生私密金鑰,使用私密金鑰對密碼加密,然後發給伺服器。伺服器收到密文以後,使用decryptByPrivateKey(String data, RSAPrivateKey privateKey)解密,獲得密碼明文,然後就可以判斷密碼是否正確。