一種簡單的登陸加密方案

該方案使用RSA加密和解密。javascript

  每次登陸前,客戶端從服務器端獲取公鑰和隨機值。java

  公鑰用於加密明文;git

  隨機值能夠增強每一次操做的安全性,隨機值也加入明文中一併加密,服務端對隨機值進行校驗,校驗後從緩存中銷燬,這樣就算被別人拿到加密後的密文再次發起請求,因爲隨機值已失效,請求也是無效的。github

 

 

 

下面以js客戶端爲例,演示一下流程:web

一、假設客戶的密碼以SHA256加密後存在數據庫中spring

 

2.、客戶輸入用戶名和密碼點擊 「登陸」後,客戶端發起請求,從服務器端獲取公鑰和隨機值。數據庫

{
    "rand": "SAXpJg",
    "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCK+oqElHP94+1BhhiTKX0pzziepN+C5Ff/qgmind2XvD35eWlCqzypGIXBoki526ZbsqrssbxTy5imhthe4eUTenLGUKkUgYUmDWrus8NmJm6IlXuqbGHaEY1zocsnlqVezOMj0AIUq5L65Y6e5XnEf1ludSzTF73MtFTjW8TRyQIDAQAB"
}

 

三、客戶端將用戶輸入的密碼使用SHA256加密apache

<!--下載地址:https://github.com/Caligatio/jsSHA -->
         <script type="text/javascript" src="sha.js"></script>
         <!--下載地址:https://github.com/travist/jsencrypt-->
          <script type="text/javascript" src="jsencrypt.js"></script>
          <script>
                  
                //用戶輸入的密碼
                var password1 = '123456';
                
                //從服務端得到的公鑰
                var publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCK+oqElHP94+1BhhiTKX0pzziepN+C5Ff/qgmind2XvD35eWlCqzypGIXBoki526ZbsqrssbxTy5imhthe4eUTenLGUKkUgYUmDWrus8NmJm6IlXuqbGHaEY1zocsnlqVezOMj0AIUq5L65Y6e5XnEf1ludSzTF73MtFTjW8TRyQIDAQAB";
    
                
                //從服務端得到的隨機值
                var rand = 'SAXpJg';
                
                //SHA-256加密
                var shaObj = new jsSHA("SHA-256", "TEXT");
                shaObj.update(password1);
                var hash = shaObj.getHash("HEX");
        
                //組裝明文:由加密後的密碼和隨機值組成
                var text = hash + '|' + rand;
                console.log("待加密的文本: " + text);
                
                //使用RSA公鑰加密
                var encrypt = new JSEncrypt();
                  encrypt.setPublicKey(publicKey);
                
                // password就能夠發送到服務端進行解密校驗了
                var password = encrypt.encrypt(text);
                
                console.log("加密後的密文:" + password);
    
          </script>

  控制檯打印出來的結果:緩存

待加密的明文:8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92|SAXpJg
加密後的密文:dgUBkZPZgL76+zMbKckAxb3C072I8b4nqAZlWUD/24Hp7UpAgiKx4P90xgs1UhWM2qputsjgpsgXLCNUg2vtO9MxpQk6zWUbyh4cxL08UcmMv3KIMO5rnbFxKEmuIbQ2G/3UZT8c+w899ERLCpDVyHrKSijdpvVoKrB6PzyjP+w=

  而後將加密後的密文傳到服務器端便可。安全

 

四、服務器端代碼

  RSAUtils.java

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.Security;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;

import javax.crypto.Cipher;

import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;


/**
 * RSA 工具
 * @author Luxh
 *
 */
public class RSAUtils {
    
    
    
    private static final String ALGORITHM = "RSA";
    
    private static final String PROVIDER = "BC";
    
    private static final String TRANSFORMATION = "RSA/None/PKCS1Padding";
    
    private static final int KEY_SIZE = 1024;
    
    private static KeyPair keyPair = null;
    
    
    /**
     * 初始化密鑰對
     */
    static {
        try{
             Security.addProvider(new BouncyCastleProvider());
             SecureRandom secureRandom = new SecureRandom();
             KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM, PROVIDER);
             keyPairGenerator.initialize(KEY_SIZE, secureRandom);
             keyPair = keyPairGenerator.generateKeyPair();
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
    
    
    /**
     * 獲取公鑰
     * @return
     */
    public static RSAPublicKey getRSAPublicKey() {
         return (RSAPublicKey)keyPair.getPublic();
    } 
    
    /**
     * 獲取Base64編碼的公鑰
     * @return
     */
    public static String getBase64PublicKey() {
        RSAPublicKey publicKey = getRSAPublicKey();
        //return new String(Base64.encodeBase64(publicKey.getEncoded()));
        return Base64.encodeBase64String(publicKey.getEncoded());
    } 
    
    
    /**
     * 使用公鑰加密
     * @param data
     * @return
     */
    public static String encrypt(byte[] data) {
        String ciphertext = "";
        try {
            Cipher cipher = Cipher.getInstance(keyPair.getPublic().getAlgorithm());
            cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
            ciphertext = Base64.encodeBase64String(cipher.doFinal(data));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ciphertext;
    }
    
    
    
    /**
     * 使用私鑰解密
     * @param ciphertext
     * @return
     */
    public static String decrypt(String ciphertext) {
        String plaintext = "";
        try {
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance(TRANSFORMATION, PROVIDER);
            RSAPrivateKey pbk = (RSAPrivateKey)keyPair.getPrivate();
            cipher.init(Cipher.DECRYPT_MODE, pbk);
            byte[] data = cipher.doFinal(Base64.decodeBase64(ciphertext));
            plaintext = new String(data);
        }catch (Exception e) {
            e.printStackTrace();
        }
        return plaintext;
    }
    
}
View Code

 

  DemoController.java

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.google.common.collect.Maps;

import io.caimi.util.RSAUtils;

@RestController
public class DemoController {
    
    
    /**
     * 獲取公鑰和隨機值
     * 
     */
    @RequestMapping("/secret")
    public Map<String, Object> secret(HttpServletRequest request) {
        
        Map<String, Object> resultMap = Maps.newHashMap();
        
        // 獲取公鑰
        String publicKey = RSAUtils.getBase64PublicKey();
        resultMap.put("publicKey", publicKey);
        
        // 生成隨機值
        String rand =  RandomStringUtils.randomAlphabetic(6);
        resultMap.put("rand", rand);
        
        // 將生成的隨機值存到session中,實際使用能夠存到第三方緩存中,並設置失效時間
        request.getSession().setAttribute("rand", rand);
        
        return resultMap;
        
    }
    
    
    /**
     * 校驗
     * 
     */
    @RequestMapping(value="/check", method=RequestMethod.POST)
    public String check(HttpServletRequest request) {
        // 取得密文
        String password = request.getParameter("password");
        
        // 解密
        String plaintext = RSAUtils.decrypt(password);
        
        String[] arr = plaintext.split("\\|");
        
        // 校驗隨機值
        String rand = arr[1];
        String randInSession = (String) request.getSession().getAttribute("rand");
        //隨機值失效
        request.getSession().removeAttribute("rand");
        
        if(!rand.equals(randInSession)) {
            return "非法的請求";
        }
        
        // 校驗密碼
        String passwd = arr[0];
        
        // 實際中根據用戶名從數據庫中查詢出密碼
        String realPasswd = "8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92"; 
        
        if(!realPasswd.equals(passwd)) {
            return "密碼輸入錯誤";
        }
        
        return "校驗經過";
    }
    
}
View Code

 

  maven依賴的一些jar

<dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.10</version>
        </dependency>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15on</artifactId>
            <version>1.54</version>
        </dependency>
        
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>18.0</version>
        </dependency>
        
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
        </dependency>
        
相關文章
相關標籤/搜索