Java Web項目RSA加密

最近作的一個項目,服務器爲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. 客戶端接收到公鑰之後,使用公鑰對密碼加密,而後將密文發給服務器;spa

4. 服務器接收到密文之後,使用私鑰解密,判斷是不是正確的密碼。code

下面是關鍵代碼。blog

生成密鑰和加密、解密的代碼:

/** 
     * 生成公鑰和私鑰 
     * @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);

將其中的模和私鑰指數發給客戶端,客戶端收到之後,使用getPublicKey(String modulus, String exponent)生成公鑰,使用公鑰對密碼加密,而後發給服務器。服務器收到密文之後,使用decryptByPrivateKey(String data, RSAPrivateKey privateKey)解密,得到密碼明文,而後就能夠判斷密碼是否正確。

相關文章
相關標籤/搜索