Springboot&security Encrypted Login Process Based on Front-end and Back-end Separation of RSA Password

 

 

Introduction to RSA Encryption

RSA encryption is an asymmetric encryption. The decryption can be completed without directly transferring the key. This ensures the security of the information and avoids the risk of being cracked by transferring the key directly. The process of encryption and decryption by a pair of keys is called public key and private key respectively. There is a mathematical correlation between them. The principle of the encryption algorithm is to ensure security by factoring a large integer. Usually the private key is kept by the individual, and the public key is public (which may be held by many people at the same time).

  

2. RSA Encryption

Encryption is for security. In short, encryption is to prevent information from being leaked.

Scene: During the Republic of China, Agent B was supposed to send a message to Agent A, the content of which was an instruction.

The encryption process of RSA is as follows:

(1) Agent A generates a pair of keys (public key and private key), the private key is not public, Agent A keeps it. The public key is public and accessible to anyone.

(2) Agent A passes his public key to Agent B, and Agent B encrypts the message with Agent A's public key.

(3) Agent A receives the message encrypted by Agent B and decrypts it with Agent A's private key.

In this process, there are only two transfers, the first is Agent A delivering public key to Agent B, the second is Agent B delivering encrypted message to Agent A, even if they are intercepted by the enemy, there is no danger, because only Agent A's private key can decrypt the message and prevent the leakage of the message content.

Based on this feature, we can encrypt the password on the front-end and back-end login. Ensure the security of password.

3. Implementation of RSA Cryptographic Encryption (Java)

package com.sparksys.mall.core.utils;

import java.util.Base64;

/**
 * Chinese Class Name: base64 Encryption Tool Class
 * Chinese Description: base64 Encryption Tool Class
 *
 * @author zhouxinlei
 * @date 2019-09-11 11:05:47
 */
public class Base64Utils {

    public static final Base64.Encoder encoder = Base64.getEncoder();
    public static final Base64.Decoder decoder = Base64.getDecoder();

    /**
     * base64 encryption
     *
     * @param encodeText Plaintext
     * @return
     */
    public static byte[] encoder(byte[] encodeText) {
        return encoder.encode(encodeText);
    }

    /**
     * base64 encryption
     *
     * @param decodeText ciphertext
     */
    public static byte[] decoder(byte[] decodeText) {
        return decoder.decode(decodeText);
    }

}

The tool encapsulates base64 encoding. Coding and Decoding for RSA Code

package com.sparksys.mall.core.utils;

import com.sparksys.mall.core.constant.AttributeConstant;
import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * Chinese Class Name: RSA Password Encryption Tool Class
 * Chinese Description: RSA Password Encryption Tool Class
 *
 * @author zhouxinlei
 * @date 2019-09-11 10:30:43
 */
public class RSAUtils {
    /**
     * RSA Maximum Encrypted Plaintext Size
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;

    /**
     * RSA Maximum decrypted ciphertext size
     */
    private static final int MAX_DECRYPT_BLOCK = 128;

    /**
     * Get the key pair
     *
     * @return java.security.KeyPair
     * @author zhouxinlei
     * @date 2019-09-12 15:25:55
     */
    public static KeyPair getKeyPair() throws Exception {
        KeyPairGenerator generator = KeyPairGenerator.getInstance(AttributeConstant.ALGORITHM_NAME);
        generator.initialize(1024);
        return generator.generateKeyPair();
    }

    /**
     * Get private key
     *
     * @param privateKey Private key string
     * @return java.security.PrivateKey
     * @author zhouxinlei
     * @date 2019-09-12 15:26:15
     */
    public static PrivateKey getPrivateKey(String privateKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance(AttributeConstant.ALGORITHM_NAME);
        byte[] decodedKey = Base64Utils.decoder(privateKey.getBytes());
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
        return keyFactory.generatePrivate(keySpec);
    }

    /**
     * Get public key
     *
     * @param publicKey Public key string
     * @return java.security.PublicKey
     * @author zhouxinlei
     * @date 2019-09-12 15:26:40
     */
    public static PublicKey getPublicKey(String publicKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance(AttributeConstant.ALGORITHM_NAME);
        byte[] decodedKey = Base64Utils.decoder(publicKey.getBytes());
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
        return keyFactory.generatePublic(keySpec);
    }

    /**
     * RSA encryption
     *
     * @param data      Data to be encrypted
     * @param publicKey public key
     * @return java.lang.String
     * @author zhouxinlei
     * @date 2019-09-12 15:27:07
     */
    public static String encrypt(String data, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance(AttributeConstant.ALGORITHM_NAME);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        int inputLen = data.getBytes().length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offset = 0;
        byte[] cache;
        int i = 0;
        // Sectional Encryption of Data
        while (inputLen - offset > 0) {
            if (inputLen - offset > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data.getBytes(), offset, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data.getBytes(), offset, inputLen - offset);
            }
            out.write(cache, 0, cache.length);
            i++;
            offset = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        // Getting encrypted content is coded using base64 and converted to string using UTF-8 as standard.
        // Encrypted string
        return new String(Base64Utils.encoder(encryptedData));
    }

    /**
     * RSA Decrypt
     *
     * @param data       Data to be decrypted
     * @param privateKey private key
     * @return java.lang.String
     * @author zhouxinlei
     * @date 2019-09-12 15:27:29
     */
    public static String decrypt(String data, PrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance(AttributeConstant.ALGORITHM_NAME);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] dataBytes = Base64.decodeBase64(data);
        int inputLen = dataBytes.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offset = 0;
        byte[] cache;
        int i = 0;
        //Sectional Decryption of Data
        while (inputLen - offset > 0) {
            if (inputLen - offset > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(dataBytes, offset, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(dataBytes, offset, inputLen - offset);
            }
            out.write(cache, 0, cache.length);
            i++;
            offset = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        // Decrypted content
        return new String(decryptedData, "UTF-8");
    }

    /**
     * autograph
     *
     * @param data       Data to be signed
     * @param privateKey private key
     * @return java.lang.String
     * @author zhouxinlei
     * @date 2019-09-12 15:24:08
     */
    public static String sign(String data, PrivateKey privateKey) throws Exception {
        byte[] keyBytes = privateKey.getEncoded();
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(AttributeConstant.ALGORITHM_NAME);
        PrivateKey key = keyFactory.generatePrivate(keySpec);
        Signature signature = Signature.getInstance(AttributeConstant.MD5_RSA);
        signature.initSign(key);
        signature.update(data.getBytes());
        return new String(Base64Utils.encoder(signature.sign()));
    }

    /**
     * Check sign
     *
     * @param srcData   Original string
     * @param publicKey public key
     * @param sign      autograph
     * @return boolean Whether the signature is approved or not
     * @author zhouxinlei
     * @date 2019-09-12 15:23:38
     */
    public static boolean verify(String srcData, PublicKey publicKey, String sign) throws Exception {
        byte[] keyBytes = publicKey.getEncoded();
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(AttributeConstant.ALGORITHM_NAME);
        PublicKey key = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance(AttributeConstant.MD5_RSA);
        signature.initVerify(key);
        signature.update(srcData.getBytes());
        return signature.verify(Base64Utils.decoder(sign.getBytes()));
    }

    public static void main(String[] args) {
        try {
            // Generate key pairs
            KeyPair keyPair = getKeyPair();
            String privateKey = new String(Base64Utils.encoder(keyPair.getPrivate().getEncoded()));
            String publicKey = new String(Base64Utils.encoder(keyPair.getPublic().getEncoded()));
            System.out.println("private key:" + privateKey);
            System.out.println("public key:" + publicKey);
            // RSA encryption
            String data = "123456";
            String encryptData = encrypt(data, getPublicKey(publicKey));
            System.out.println("Encrypted content:" + encryptData);
            // RSA decryption
            String decryptData = decrypt(encryptData, getPrivateKey(privateKey));
            System.out.println("Decrypted content:" + decryptData);
            // RSA signatures
            String sign = sign(data, getPrivateKey(privateKey));
            // RSA check sign
            boolean result = verify(data, getPublicKey(publicKey), sign);
            System.out.print("Verification result:" + result);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.print("Encryption and decryption anomalies");
        }
    }
}

Tool classes encapsulated by RSA encryption, decryption, signature verification, etc.

4. Implementation of Springboot Business Code

Logic: The front-end obtains publickey, encrypts the password with the public key, and the back-end obtains the password, decrypts the encrypted data with the corresponding private key, and compares the password with the database.

controller layer does not write, mainly look at service layer business logic code:

@Override
    public ResultEntity getPublicKey() {
        try {
            String privateKey = redisUtil.get(AttributeConstant.RSA_PRIVATE_KEY);
            String publicKey = redisUtil.get(AttributeConstant.RSA_PUBLIC_KEY);
            if (StringUtils.isEmpty(publicKey) || StringUtils.isEmpty(privateKey)) {
                KeyPair keyPair = RSAUtils.getKeyPair();
                privateKey = new String(Base64Utils.encoder(keyPair.getPrivate().getEncoded()));
                publicKey = new String(Base64Utils.encoder(keyPair.getPublic().getEncoded()));
                redisUtil.set(AttributeConstant.RSA_PRIVATE_KEY, privateKey);
                redisUtil.set(AttributeConstant.RSA_PUBLIC_KEY, publicKey);
            }
            log.info("privateKey = {},publicKey = {}",privateKey,publicKey);
            return ResultEntity.success(publicKey, "Get the public key successfully!");
        } catch (Exception e) {
            e.printStackTrace();
            return ResultEntity.failed("Failed to obtain public key!");
        }
    }

 

Notes:

String privateKey = redisUtil.get(AttributeConstant.RSA_PRIVATE_KEY);
String publicKey = redisUtil.get(AttributeConstant.RSA_PUBLIC_KEY);
if (StringUtils.isEmpty(publicKey) || StringUtils.isEmpty(privateKey)) {
    KeyPair keyPair = RSAUtils.getKeyPair();
    privateKey = new String(Base64Utils.encoder(keyPair.getPrivate().getEncoded()));
    publicKey = new String(Base64Utils.encoder(keyPair.getPublic().getEncoded()));
    redisUtil.set(AttributeConstant.RSA_PRIVATE_KEY, privateKey);
    redisUtil.set(AttributeConstant.RSA_PUBLIC_KEY, publicKey);
}

Generate key pairs, store them in redis, and return the front-end public key

Front end: based on iview and vue framework

The front end is encrypted with crypto-js.
npm i jsencrypt,
Then import JSEncrypt from'jsencrypt'is introduced into the page header.
const encrypt = new JSEncrypt();
encrypt.setPublicKey('your public key');
password = encrypt.encrypt('your password'); // encrypted string
getPublicKey().then(res => {
            let password = this.form.password
            let publicKey = res.data.data.data
            console.log(publicKey)
            const encrypt = new JSEncrypt()
            encrypt.setPublicKey(publicKey)
            password = encrypt.encrypt(password)
            console.log(password)
            this.$emit('on-success-valid', {
              userName: this.form.userName,
              password: password
            })
          })

Login service:

@Master
    @Override
    public ResultEntity login(UmsAdminLoginInDto umsAdminLoginInDto,
                              HttpServletResponse response) {
        try {
            String token;
            QueryWrapper<UmsAdmin> wrapper = new QueryWrapper();
            wrapper.eq("username", umsAdminLoginInDto.getUserName());
            UmsAdmin umsAdmin = adminDao.selectOne(wrapper);
            if (ObjectUtils.isEmpty(umsAdmin)) {
                return ResultEntity.failed("Account does not exist!");
            }
            String privateKey = redisUtil.get(AttributeConstant.RSA_PRIVATE_KEY);
            String inputDecryptData = RSAUtils.decrypt(umsAdminLoginInDto.getPassword(),
                    RSAUtils.getPrivateKey(privateKey));
            String decryptData = RSAUtils.decrypt(umsAdmin.getPassword(),
                    RSAUtils.getPrivateKey(privateKey));
            if (!StringUtils.equals(decryptData, inputDecryptData)) {
                return ResultEntity.failed("Error password!");
            }
            umsAdmin.setLoginTime(new Date());
            adminDao.updateById(umsAdmin);
            UserDetails userDetails =
                    userDetailsService.loadUserByUsername(umsAdminLoginInDto.getUserName());
            UsernamePasswordAuthenticationToken authentication =
                    new UsernamePasswordAuthenticationToken(userDetails, null,
                            userDetails.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authentication);
            //Save logon log
            UmsAdminLoginLog umsAdminLoginLog = new UmsAdminLoginLog();
            umsAdminLoginLog.setAdminId(umsAdmin.getId());
            umsAdminLoginLog.setCreateTime(new Date());
            adminLoginLogDao.insert(umsAdminLoginLog);
            token = JwtTokenUtil.generateToken(userDetails);
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("token", token);
            jsonObject.put("tokenHead", AttributeConstant.JWT_TOKEN_HEAD);
            response.setHeader(AttributeConstant.JWT_TOKEN_HEADER,
                    AttributeConstant.JWT_TOKEN_HEAD.concat(token));
            return ResultEntity.success(jsonObject, "Log in successfully!");
        } catch (Exception e) {
            e.printStackTrace();
            return ResultEntity.failed("Logon failed!");
        }
    }

Notes:

String privateKey = redisUtil.get(AttributeConstant.RSA_PRIVATE_KEY);
String inputDecryptData = RSAUtils.decrypt(umsAdminLoginInDto.getPassword(),
        RSAUtils.getPrivateKey(privateKey));
String decryptData = RSAUtils.decrypt(umsAdmin.getPassword(),
        RSAUtils.getPrivateKey(privateKey));
if (!StringUtils.equals(decryptData, inputDecryptData)) {
    return ResultEntity.failed("Error password!");
}

First get the private key, I put it in redis, then decrypt the encrypted data with the private key, colleagues get the database user object, and get the password using the private key decryption to compare with whether the password entered by the user is the same.

Here, Springboot & Security Based on front-end and back-end separation of RSA password encryption login process is completed, thank you for your view, there are problems can @I

Keywords: Java SpringBoot Database Redis

Added by alkhatareykha on Fri, 13 Sep 2019 09:01:14 +0300