一种简单的登录加密方案

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

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

  公钥用于加密明文;

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

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

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

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

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

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

<!--下载地址: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=

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

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<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 "校验通过";
    }

}

  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>
        
时间: 04-20

一种简单的登录加密方案的相关文章

.net 签名加密实现的一种简单方法

加密方法有很多,以下是其中一种简单的签名模式 1.首先客户端通过webapi按照IP地址,时间戳,随机数生成签名,并传递序列号 private Result_Sign Valid()        {            string ServerIP = "192.168.1.6";// HttpContext.Request.ServerVariables.Get("Local_Addr").ToString(); //地址            string

关于登录加密问题的一些讨论

本文收集了一些关于登录加密问题的一些讨论. ****************************************************************** 一. QQ网站登录的RSA加密传输缺陷分析 From: http://www.cnbeta.com/articles/43687.htm 感谢匿名人士的投递 QQ网站登录处没有使用https进行加密,而是采用了RSA非对称加密来保护传输过程中的密码以及敏感信息的安全性. QQ是在javascript中实现整个过程的.这个

Spring3.0第三讲:Spring实现简单的登录

学习Spring这些技术性框架,光掌握理论知识是远远不够了,我们要懂得学以致用,用键盘将学到的敲出来,在正确与错误中寻找Spring的用法. 为了给读者一个直观的概念,这里我用Spring搭建一个简单的登录,可以让你很快的了解Spring在持久层.业务层.表现层是怎么运作的,这样后面我们分模块讲解的时候,读者也能很快的知道. 本文所用工具为Eclipse IDE,数据库为Oracle 11g. 首先我们来了解登录这个功能,用户访问登录页面,输入账号和密码,点击登录,后台验证是否有账号和密码匹配,

JEESZ分布式框架--单点登录集成方案

  JEESZ分布式框架单点登录集成方案第一节:单点登录简介 第一步:了解单点登录SSO主要特点是: SSO应用之间使用Web协议(如HTTPS) ,并且只有一个登录入口.SSO的体系中有下面三种角色:1) User(多个)2) Web应用(多个)3) SSO认证中心(一个) SSO实现包含以下三个原则:1) 所有的登录都在 SSO 认证中心进行.  2) SSO认证中心通过一些方法来告诉Web应用当前访问用户究竟是不是通过认证的用户.  3) SSO认证中心和所有的 Web 应用建立一种信任关

一种简单的ELF加固方法

介绍一种ELF文件函数粒度的加固方法,可以有效防止对程序的静态分析.这是一种有源码加固方式,需要被加固程序中代码配合.加固流程如下: 1)读取ELF文件头,获取e_phoff和e_phnum2)通过Elf64_Phdr中的p_type字段,找到DYNAMIC3)遍历.dynamic,找到.dynsym..dynstr 节区偏移,和.dynstr节区的大小4)遍历.dynsym,找到函数对应的Elf64_Sym符号后,根据st_value和st_size字段找到函数在ELF的偏移和函数大小5)根据

Spring MVC +MyBatis +MySQL 简单的登录查询 Demo 解决了mybatis异常

忙活了大半天,饭也没顾得上吃,哎许久不动手,一动手就出事,下面请看今天的重头戏,额吃个饭回来再发了! 1.整体结构 2.准备工作 数据库: --Mysql 5.6 创建数据库 wolf CREATE DATABASE wolf; 创建用户表 user create table user( id int  AUTO_INCREMENT  primary key, name varchar(25) not null, pwd varchar(20) not null, create_time dat

HttpClient的一种简单实现Demo

1 /** 2 * 测试HttpClient2种请求网络方式的Activity 3 * get和post 4 * 5 */ 6 public class HttpClientActivity extends Activity { 7 private HttpParams httpParams ; 8 private HttpClient httpClient ; 9 @Override 10 protected void onCreate(Bundle savedInstanceState) {

Linux终端下简单的登录程序 密码不回显

在Linux进行登录是输入密码不会被回显,所以我也写了个简单的登入程序,使得在输入密码时不再进行回显. #include <stdio.h> #include <stdlib.h> #include <termios.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <stdbool.h> #define USER_NAME &qu

java 程序执行输出有两种简单方式

java 程序执行输出有两种简单方式: 1. System.out.println("需要输出的内容"): 该方法可参看运行一个简单的Java程序 结果图: 2. System.out.print("需要输出的内容"): 1 public class HelloWorld 2 { 3 //Java程序的入口方法,程序将从这里开始运行 4 public static void main(String[] args) 5 { 6 //向控制台打印一条语句 7 Syste