通过RSA算法使客户端数据加密传输

439 阅读3分钟

1.为什么需要对前端的数据进行加密

在实际业务当中,我们可能需要对一些敏感信息进行加密处理,比如用户名和密码,如果是明文传输的时候,极有可能被不法分子通过抓包的方式获取到账户信息,从而导致安全漏洞。基于上述的问题,可以有多种方式进行解决,比如说在客户端对账户信息进行加密处理,或者是通过更加可靠的协议HTTPS等。这里介绍通过加密的方式解决

2.为了更高的安全性,采用RSA算法进行加密

RSA又称为非对称加密,有着公钥和私钥之分,统称密钥对,由服务端颁发,然后将公钥给客户端,私钥存储在服务端,每次客户端在登录之前都要去获取公钥,每次的公钥都不一样,从而进一步提高安全性。

3.前端jsencrypt.js文件

网上有相当多的资源,这里就不放了

4.登录页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
    <script src="encrypt.js"></script>
</head>
<body>
<form>
    用户名:<input type="text" name="username" id="username">
    密码: <input type="text" name="password" id="password">
    <input type="button" onclick="getPublicKey()" value="获取公钥">
    <input type="button" onclick="login()" value="登录"/>
</form>
<script>
    function uuid() {
        var temp_url = URL.createObjectURL(new Blob());
        var uuid = temp_url.toString(); // blob:https://xxx.com/b250d159-e1b6-4a87-9002-885d90033be3
        URL.revokeObjectURL(temp_url);
        return uuid.substr(uuid.lastIndexOf("/") + 1);
    }

    var encrypt = new JSEncrypt();

    function getPublicKey() {
        $.ajax({
            type: "GET",
            url: "getPublicKey",
            cache: false,
            dataType: "json",
            success: function (msg) {
                encrypt.setPublicKey(msg.base64PublicKey);
                localStorage.setItem("uuid", msg.uuid)
            }
        });

    }

    function login() {
        let username = $("#username").val();
        let password = $("#password").val();
        encrUsername = encrypt.encrypt(username);
        encrPassword = encrypt.encrypt(password);

        const body = {
            username: encrUsername,
            password: encrPassword,
            uuid: localStorage.getItem("uuid")
        }
        $.ajax({
            type: "POST",
            url: "login",
            dataType: "json",
            contentType: "application/json",
            data: JSON.stringify(body),
        });
    }
</script>
</body>
</html>

5.请求接口

@RestController
public class TestController {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 登录之前,调用此接口,获取密钥对中的公钥,将公钥和唯一标识给客户端
     * 私钥存储在服务端,这边存储在redis中,每次获取的密钥对都不一样,一个公钥加密的,只能由对应的私钥进行解密
     *
     * @return
     */
    @GetMapping("getPublicKey")
    public HashMap<String, Object> getPublicKey() {
        KeyPair keyPair = RSAUtils2.generateKeyPair();
        String base64PublicKey = RSAUtils2.getBase64PublicKey(keyPair.getPublic());
        String privateKey = RSAUtils2.getBase64PrivateKey(keyPair.getPrivate());
        // 每次获取密钥对的时候,生成一个唯一标识,前端进行存储,在登录的时候带上这个唯一标识
        String uuid = UUID.randomUUID().toString();
        stringRedisTemplate.opsForValue().set("private_key:" + uuid, privateKey, 5L, TimeUnit.MINUTES);
        HashMap<String, Object> map = new HashMap<>(2);
        map.put("uuid", uuid);
        map.put("base64PublicKey", base64PublicKey);
        return map;
    }

    /**
     * 通过唯一标识,从redis中查询对应的秘钥,通过秘钥进行解密,然后将该key从redis中remove
     */
    @PostMapping("login")
    public void login(@RequestBody LoginBody loginBody) {
        String privateKey = stringRedisTemplate.opsForValue().get("private_key:" + loginBody.getUuid());
        String username = RSAUtils2.decrypt(loginBody.getUsername(), privateKey);
        String password = RSAUtils2.decrypt(loginBody.getPassword(), privateKey);
        System.out.println(username);
        System.out.println(password);
        stringRedisTemplate.delete("private_key:" + loginBody.getUuid());
    }

}

@Data
public class LoginBody {

    private String username;

    private String password;

    private String uuid;

}
  1. RSA工具类
package com.angel.item.utils;

import org.apache.tomcat.util.codec.binary.Base64;

import javax.crypto.Cipher;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;

public class RSAUtils2 {

    public static final String RSA_ECB_PKCS1_PADDING = "RSA/ECB/PKCS1Padding";

    public static final int KEY_SIZE_2048 = 2048;
    public static final int KEY_SIZE_1024 = 1024;

    private static final String ALGORITHM = "RSA";


    public static KeyPair generateKeyPair() {
        return generateKeyPair(KEY_SIZE_2048);
    }

    public static KeyPair generateKeyPair(int keySize) {
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
            keyPairGenerator.initialize(keySize);
            return keyPairGenerator.generateKeyPair();
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalArgumentException("Failed to generate key pair!", e);
        }
    }

    public static PublicKey getPublicKey(String base64PublicKey) {
        try {
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decodeBase64(base64PublicKey));
            KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
            PublicKey publicKey = keyFactory.generatePublic(keySpec);
            return publicKey;
        } catch (Exception e) {
            throw new IllegalArgumentException("Failed to get public key!", e);
        }
    }

    public static PublicKey getPublicKey(BigInteger modulus, BigInteger exponent) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
            RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, exponent);
            return keyFactory.generatePublic(keySpec);
        } catch (Exception e) {
            throw new IllegalArgumentException("Failed to get public key!", e);
        }
    }

    public static String getBase64PublicKey(PublicKey publicKey) {
        return Base64.encodeBase64String(publicKey.getEncoded());
    }

    public static PrivateKey getPrivateKey(String base64PrivateKey) {
        try {
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(base64PrivateKey));
            KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
            PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
            return privateKey;
        } catch (Exception e) {
            throw new IllegalArgumentException("Failed to get private key!", e);
        }
    }

    public static PrivateKey getPrivateKey(BigInteger modulus, BigInteger exponent) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
            RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(modulus, exponent);
            return keyFactory.generatePrivate(keySpec);
        } catch (Exception e) {
            throw new IllegalArgumentException("Failed to get private key!", e);
        }
    }

    public static String getBase64PrivateKey(PrivateKey privateKey) {
        return Base64.encodeBase64String(privateKey.getEncoded());
    }

    public static byte[] encryptAsByteArray(String data, PublicKey publicKey) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_ECB_PKCS1_PADDING);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            return cipher.doFinal(data.getBytes());
        } catch (Exception e) {
            throw new IllegalArgumentException("Encrypt failed!", e);
        }
    }

    public static byte[] encryptAsByteArray(String data, String base64PublicKey) {
        return encryptAsByteArray(data, getPublicKey(base64PublicKey));
    }

    public static String encryptAsString(String data, PublicKey publicKey) {
        return Base64.encodeBase64String(encryptAsByteArray(data, publicKey));
    }

    public static String encryptAsString(String data, String base64PublicKey) {
        return Base64.encodeBase64String(encryptAsByteArray(data, getPublicKey(base64PublicKey)));
    }

    public static String decrypt(byte[] data, PrivateKey privateKey) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_ECB_PKCS1_PADDING);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return new String(cipher.doFinal(data));
        } catch (Exception e) {
            throw new IllegalArgumentException("解码失败!", e);
        }
    }

    public static String decrypt(byte[] data, String base64PrivateKey) {
        return decrypt(data, getPrivateKey(base64PrivateKey));
    }

    public static String decrypt(String data, PrivateKey privateKey) {
        return decrypt(Base64.decodeBase64(data), privateKey);
    }

    public static String decrypt(String data, String base64PrivateKey) {
        return decrypt(Base64.decodeBase64(data), getPrivateKey(base64PrivateKey));
    }

    public static void main(String[] args) {
         /*生成密钥对*/
        KeyPair keyPair = RSAUtils2.generateKeyPair();
        String privateKey = Base64.encodeBase64String(keyPair.getPrivate().getEncoded());
        String publicKey = Base64.encodeBase64String(keyPair.getPublic().getEncoded());
        System.out.println("私钥privateKey:" + privateKey);
        System.out.println("公钥publicKey:" + publicKey);

        String content = "18811122222";
        System.out.println("原始字符串:" + content);
        //公钥加密
        String encryptAsString = RSAUtils2.encryptAsString(content, publicKey);
        System.out.println("rsa加密后字符串:" + encryptAsString);
        //私钥解密
        String decrypt = RSAUtils2.decrypt(encryptAsString, privateKey);
        System.out.println("rsa解密后字符串:" + decrypt);
    }
}