一种简单的登录加密方案


该方案使用RSA加密和解密。

  每次登录前,客户端从服务器端获取公钥和随机值。

  公钥用于加密明文;

  随机值可以加强每一次操作的安全性,随机值也加入明文中一并加密,服务端对随机值进行校验,校验后从缓存中销毁,这样就算被别人拿到加密后的密文再次发起请求,由于随机值已失效,请求也是无效的。

下面以js客户端为例,演示一下流程:

1、假设客户的密码以SHA256加密后存在数据库中

2.、客户输入用户名和密码点击 “登录”后,客户端发起请求,从服务器端获取公钥和随机值。

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

3、客户端将用户输入的密码使用SHA256加密


         
         
          
          

  控制台打印出来的结果:

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

  然后将加密后的密文传到服务器端即可。

4、服务器端代码

  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;
    }
    
}

  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 secret(HttpServletRequest request) {
        
        Map 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 "校验通过";
    }
    
}

  maven依赖的一些jar

<dependency>
            <groupId>commons-codecgroupId>
            <artifactId>commons-codecartifactId>
            <version>1.10version>
        dependency>
        <dependency>
            <groupId>org.bouncycastlegroupId>
            <artifactId>bcprov-jdk15onartifactId>
            <version>1.54version>
        dependency>
        
        <dependency>
            <groupId>com.google.guavagroupId>
            <artifactId>guavaartifactId>
            <version>18.0version>
        dependency>
        
        <dependency>
            <groupId>org.apache.commonsgroupId>
            <artifactId>commons-lang3artifactId>
            <version>3.4version>
        dependency>