RSA前端加密後端解密(密鑰動態生成)

RSA 是什麼

RSA 是一種非對稱加密算法,簡單概括,就是加密和解密時使用不同的密鑰進行。

問題

一般在進行WEB開發過程中,少不了用戶登錄功能的開發,用戶在登錄時需要輸入用戶名(或郵箱等其它唯一標識信息)和密碼進行系統登錄。如果密碼通過明文的方式進行http傳輸並登錄時,密碼將能被人查看到。此時多數人會想到MD5編碼,通過將密碼進行MD5編碼成無法解密的密文,則他人就算獲取到該密文,也無法解密獲取其原始密碼。但後臺需要獲取該原始密碼進行校驗登錄的操作,那麼MD5編碼不可取。搜索相關如何保證賬號密碼安全的文章,瞭解到RSA這種非對稱加密算法。

方案確定

網上的許多Jsencrypt庫進行前端加密,java後臺解密的代碼,但只適用於公鑰和私鑰確定的情況。本文爲提高安全性,目標是後臺動態生成公鑰私鑰對。
整體方案:後臺動態生成RSA的密鑰,將公鑰發送給前端,並將私鑰通過session保存在服務器,讓前端進行加密操作後纔將密文傳給後端,後端使用對應私鑰進行解密操作。
前端:TypeScript
後端:Java

前端(JavaScript/TypeScript)加密實踐

前端進行RSA加密的第三方庫採用 node-forge 庫,其他如 Jsencrypt 庫等暫時沒有調試成功。

前端代碼:

import * as forge from 'node-forge';

      // publicKey需要先通過http從後臺獲取,後臺可以寫一些geKey接口供前端調用

      const pki = forge.pki;

      // 規定格式:publicKey之前需要加'-----BEGIN PUBLIC KEY-----\n',之後需要加'\n-----END PUBLIC KEY-----'
      const publicK = pki.publicKeyFromPem('-----BEGIN PUBLIC KEY-----\n' + publicKey + '\n-----END PUBLIC KEY-----');

      // forge通過公鑰加密後一般會是亂碼格式,可進行base64編碼操作再進行傳輸,相應的,後臺獲取到密文的密碼後需要先進行base64解碼操作再進行解密
      const passwordCrypto =  forge.util.encode64(publicK.encrypt(password));
      
      // ... 後面就是進行常規的發送登錄請求,不同的是,也需要將publicKey作爲一個參數傳輸到後臺,後臺需要以此找到對應的私鑰

後端(Java)解密實踐

網上後端解密的代碼很多,但質量無法確保,正好我使用的 Hutool 這個Java的工具庫,其中包含了非對稱加密的工具,可以直接使用。

Java代碼:

/**
  * 獲取祕鑰-RSA
  * @return
  */
@RequestMapping(value = "/getKey",method = RequestMethod.POST)
@ResponseBody
public Result<String> getKeyOfRSA() {
    Result<String> result = new Result<>();

    RSA rsa = new RSA();
    String privateKeyBase64 = rsa.getPrivateKeyBase64();
    String publicKeyBase64 = rsa.getPublicKeyBase64();

    // 使用當前用戶的session進行保存公鑰私鑰對
    Subject currentUser = SecurityUtils.getSubject();
    Session session = currentUser.getSession();
    session.setAttribute(publicKeyBase64, privateKeyBase64);
    return result.successWithData(publicKeyBase64);
}


/**
     * 解密password
     * @param password
     * @param publicKey
     * @return
     * @throws Exception
     */
    private String checkPassword(String password, String publicKey, String algorithm) throws Exception {
        Subject currentUser = SecurityUtils.getSubject();
        Session session = currentUser.getSession();

        // publicKey需要前端返回
        String privateKey = (String) session.getAttribute(publicKey);
        if(privateKey ==null){
            log.error("session中私鑰失效.publickey={},password={}",publicKey,password);
            return null;
        }
        // 獲取到私鑰之後,需要將session中的該公鑰私鑰對信息移除
        session.removeAttribute(publicKey);
        // 構建,當只用私鑰進行構造對象時,只允許使用該私鑰進行加密和解密操作,本文只需要進行私鑰解密,故只使用私鑰構造對象
        RSA rsa = new RSA(privateKey, null);
        // 密碼的密文先進行base64解碼,之後再進行解密
        byte[] decrypt = rsa.decrypt(Base64.decode(password), KeyType.PrivateKey);
        String decryptStr = StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8);
        
        return decryptStr;
    }
發佈了20 篇原創文章 · 獲贊 0 · 訪問量 436
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章