Java笔记-一种表单提交AES加密案例(仿某大学bbs加密逻辑)

x33g5p2x  于2022-04-25 转载在 Java  
字(20.2k)|赞(0)|评价(0)|浏览(281)

最近分析了一大学bbs登录时,加密的流程,在此自己用spring boot做了个类似的,在此记录下,感觉还是有点东西的。逻辑很简单:

登录页面:

输入用户名/密码后,这里是admin/admin

用Fiddler抓包是这样的:

这里的Password用了AES加密,这里前端从后端再加载页面的时候,就获取了aes的密钥和向量,当然这里可以用JS逆向,得到这个值(这里和某大学BBS一样)

对应的流程图是这样的:

程序结构如下:

代码如下:

LoginController.java

package cn.it1995.MyController;

import cn.it1995.service.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Controller
public class LoginController {

    @Autowired
    LoginService loginService;

    @GetMapping("/")
    public String index(HttpServletResponse response,
                        Model model){


        String cookie = loginService.generateCookie();
        String salt = loginService.getSaltByCookie(cookie);
        String offset = loginService.getOffsetByCookie(cookie);
        response.addCookie(new Cookie("token", cookie));
        model.addAttribute("saltStr", salt);
        model.addAttribute("offsetStr", offset);

        return "login.html";
    }

    @PostMapping
    public String login(@RequestParam("userName") String userName,
                        @RequestParam("password") String password,
                        HttpServletRequest request,
                        HttpServletResponse response){

        Cookie[] cookies = request.getCookies();
        String cookie = "";
        for(Integer i = 0; i < cookies.length; i++){

            if(cookies[i].getName().equals("token")){

                cookie = cookies[i].getValue();
            }
        }

        if(cookie.equals("")){

            return "redircet:/";
        }

//        String salt = loginService.getSaltByCookie(cookie);
        try{

            boolean passwordCorrect = loginService.isPasswordCorrect(cookie, password);
            System.out.println("The passwordCorrect is " + passwordCorrect);
            if(!passwordCorrect){

                return "redirect:/";
            }
        }
        catch (Exception e){

            e.printStackTrace();
            return "redirect:/";
        }

//        System.out.println(userName);
//        System.out.println(password);
//        System.out.println(salt);

        return "success.html";
    }
}

MySession.java

package cn.it1995.Object;

public class MySession {

    public String cookie;
    public String salt;
    public String offset;

    public MySession(String cookie, String salt, String offset) {

        this.cookie = cookie;
        this.salt = salt;
        this.offset = offset;
    }
}

SessionRepository.java

package cn.it1995.repository;

import cn.it1995.Object.MySession;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;

@Repository
public class SessionRepository {

    private ArrayList<MySession> sessionList = new ArrayList<MySession>();

    public void addSession(MySession session){

        sessionList.add(session);
    }

    public String getSaltByCookie(String cookie){

        for(MySession mySession : sessionList){

            if(mySession.cookie.equals(cookie)){

                return mySession.salt;
            }
        }

        return null;
    }

    public String getOffsetByCookie(String cookie){

        for(MySession mySession : sessionList){

            if(mySession.cookie.equals(cookie)){

                return mySession.offset;
            }
        }

        return null;
    }

    public boolean isSessionExist(MySession session){

        for(MySession mySession : sessionList){

            if(mySession.cookie.equals(session.cookie)){

                return true;
            }
        }

        return false;
    }

    public void clearAllSession(){

        sessionList.clear();
    }

}

LoginServer.java

package cn.it1995.service;

import cn.it1995.Object.MySession;
import cn.it1995.repository.SessionRepository;
import cn.it1995.tool.AESUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Random;

@Service
public class LoginService {

    @Autowired
    SessionRepository sessionRepository;

    public String generateCookie(){

        String cookie = generateString(32);
        String salt = generateString(16);
        String offset = generateString(16);

        MySession mySession = new MySession(cookie, salt, offset);
        sessionRepository.addSession(mySession);
        return cookie;
    }

    public String getSaltByCookie(String cookie){

        return sessionRepository.getSaltByCookie(cookie);
    }

    public String getOffsetByCookie(String cookie){

        return sessionRepository.getOffsetByCookie(cookie);
    }

    public boolean isUserExist(String userName){

        if(userName.equals("admin")){

            return true;
        }

        return false;
    }

    public boolean isPasswordCorrect(String cookie, String password) throws Exception {

        String saltByCookie = sessionRepository.getSaltByCookie(cookie);
        String vi = sessionRepository.getOffsetByCookie(cookie);
        String decrypt = AESUtil.decrypt(password, saltByCookie, vi);
        if(decrypt.equals("admin")){

            return true;
        }

        return false;
    }

    public void removeAllSession(){

        sessionRepository.clearAllSession();
    }

    protected String generateString(Integer length){

        String str = "zxcvbnmlkjhgfdsaqwertyuiopQWERTYUIOPASDFGHJKLZXCVBNM1234567890";
        Random random = new Random();

        //cookie
        StringBuffer cookieSb = new StringBuffer();
        for(int i = 0; i < length; ++i){

            //产生0-61的数字
            int number = random.nextInt(62);

            //将产生的数字通过length次承载到sb中
            cookieSb.append(str.charAt(number));
        }

        return new String(cookieSb);
    }
}

AESUtil.java

package cn.it1995.tool;

import org.apache.tomcat.util.codec.binary.Base64;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;

public class AESUtil {

    public static String decrypt(String content, String key, String vi) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException, UnsupportedEncodingException, InvalidAlgorithmParameterException {

        Key k = toKey(key.getBytes());
        byte[] encoded = k.getEncoded();
        SecretKeySpec aes = new SecretKeySpec(encoded, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
        IvParameterSpec iv = new IvParameterSpec(vi.getBytes());
        cipher.init(Cipher.DECRYPT_MODE, aes, iv);

        byte[] bytes = cipher.doFinal(Base64.decodeBase64(content));

        return new String(bytes, "UTF-8");
    }

    public static String encrypt(String data, String key, String vi) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException {

        //Key k = toKey(Base64.decodeBase64(key));
        Key k = toKey(key.getBytes());
        byte[] encoded = k.getEncoded();
        SecretKeySpec aes = new SecretKeySpec(encoded, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
        IvParameterSpec iv = new IvParameterSpec(vi.getBytes());
        cipher.init(Cipher.ENCRYPT_MODE, aes, iv);
        byte[] bytes = cipher.doFinal(data.getBytes("UTF-8"));
        return Base64.encodeBase64String(bytes);
    }

    private static Key toKey(byte[] key){

        SecretKeySpec aes = new SecretKeySpec(key, "AES");
        return aes;
    }

    public static void main(String[] args) throws NoSuchPaddingException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IllegalBlockSizeException, UnsupportedEncodingException, InvalidKeyException {

        String content = "7mv2MJPHj1o/rdar1I4i0Q==";
        String key = "sN1DEJAVZNf3OdM3";
        String vi = "GDHgt7hbKpsIR4b4";

        System.out.println("原文 : root");

        String e = AESUtil.encrypt("root", key, vi);
        System.out.println("密文 : " + e);
        String f = AESUtil.decrypt(e, key, vi);
        System.out.println("解密 : " + f);
    }

}

WebAppMain.java

package cn.it1995;

import org.apache.catalina.Context;
import org.apache.catalina.connector.Connector;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class WebAppMain {

    public static void main(String[] args) {

        SpringApplication.run(WebAppMain.class, args);
    }

    @Bean
    public TomcatServletWebServerFactory servletContainer() {

        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {

            @Override
            protected void postProcessContext(Context context) {

                SecurityConstraint constraint = new SecurityConstraint();
                constraint.setUserConstraint("CONFIDENTIAL");
                SecurityCollection collection = new SecurityCollection();
                collection.addPattern("/*");
                constraint.addCollection(collection);
                context.addConstraint(constraint);
            }
        };
        tomcat.addAdditionalTomcatConnectors(httpConnector());
        return tomcat;
    }

    @Bean
    public Connector httpConnector() {

        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
        connector.setScheme("http");
        //Connector监听的http的端口号
        connector.setPort(8081);
        connector.setSecure(false);
        //监听到http的端口号后转向到的https的端口号
        connector.setRedirectPort(8443);
        return connector;
    }
}

login.html

<!DOCTYPE html>
<html  xmlns="http://www.w3.org/1999/xhtml"
       xmlns:th="https://www.thymeleaf.org"
       xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
    <!-- Standard Meta -->
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">

    <!-- Site Properties -->
    <title>Login Example - Semantic</title>
    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/reset.css">
    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/site.css">

    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/container.css">
    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/grid.css">
    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/header.css">
    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/image.css">
    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/menu.css">

    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/divider.css">
    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/segment.css">
    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/form.css">
    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/input.css">
    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/button.css">
    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/list.css">
    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/message.css">
    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/icon.css">

    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script src="https://semantic-ui.com/dist/components/form.js"></script>
    <script src="https://semantic-ui.com/dist/components/transition.js"></script>

    <!-- 引入 CDN Crypto.js 开始 AES加密 注意引入顺序 -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/core.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/enc-base64.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/md5.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/evpkdf.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/cipher-core.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/aes.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/pad-pkcs7.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/mode-ecb.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/enc-utf8.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/enc-hex.min.js"></script>
    <!-- 引入 CDN Crypto.js 结束 -->


    <style type="text/css">
        body {
            background-color: #DADADA;
        }
        body > .grid {
            height: 100%;
        }
        .image {
            margin-top: -100px;
        }
        .column {
            max-width: 450px;
        }
    </style>
    <script language="JavaScript">

        function login(){

            var password = document.getElementById("password").value;
            var offset = document.getElementById("offset").value;
            var salt = document.getElementById("salt").value;

            let key = CryptoJS.enc.Utf8.parse(salt);
            let srcs = CryptoJS.enc.Utf8.parse(password);
            let vi = CryptoJS.enc.Utf8.parse(offset);

            var encrypted = CryptoJS.AES.encrypt(srcs, key, {
                iv: vi,
                mode: CryptoJS.mode.CBC,
                padding: CryptoJS.pad.Pkcs7
            });

            console.log("原始数据 : ", password);
            console.log("vi : ", offset);
            console.log("salt : ", salt);

            document.getElementById("password").value = encrypted.toString();
            console.log("ase加密 : ", encrypted.toString());
            console.log("解密");

            let decrypted = CryptoJS.AES.decrypt(encrypted.toString(), key, {
                iv: vi,
                mode: CryptoJS.mode.CBC,
                padding: CryptoJS.pad.Pkcs7
            });

            console.log("ase 解码 : " , CryptoJS.enc.Utf8.stringify(decrypted).toString());
        }
    </script>
</head>
<body>

<div class="ui middle aligned center aligned grid">
    <div class="column">
        <h2 class="ui teal image header">
            <div class="content">
                Log-in to your account
            </div>
        </h2>
        <form action="login" method="post" class="ui large form" onsubmit="login()">
            <div class="ui stacked segment">
                <div class="field">
                    <div class="ui left icon input">
                        <i class="user icon"></i>
                        <input type="text" id="userName" name="userName" placeholder="userName">
                    </div>
                </div>
                <div class="field">
                    <div class="ui left icon input">
                        <i class="lock icon"></i>
                        <input type="password" id="password" name="password" placeholder="password">
                    </div>
                </div>
                <button class="ui fluid large teal button submit">Login</button>
            </div>

            <input name="salt" id="salt" type="hidden" th:value="${saltStr}">
            <input name="offset" id="offset" type="hidden" th:value="${offsetStr}">

            <div class="ui error message"></div>

        </form>

    </div>
</div>

</body>

</html>

success.html

<!DOCTYPE html>
<html  xmlns="http://www.w3.org/1999/xhtml"
       xmlns:th="https://www.thymeleaf.org"
       xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
    <!-- Standard Meta -->
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">

    <!-- Site Properties -->
    <title>Success</title>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/reset.css">
    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/site.css">

    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/container.css">
    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/grid.css">
    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/header.css">
    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/image.css">
    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/menu.css">

    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/divider.css">
    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/segment.css">
    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/form.css">
    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/input.css">
    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/button.css">
    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/list.css">
    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/message.css">
    <link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/icon.css">

    <script src="https://semantic-ui.com/dist/components/form.js"></script>
    <script src="https://semantic-ui.com/dist/components/transition.js"></script>

    <style type="text/css">
        body {
            background-color: #DADADA;
        }
        body > .grid {
            height: 100%;
        }
        .image {
            margin-top: -100px;
        }
        .column {
            max-width: 450px;
        }
    </style>
</head>
<body>

<div class="ui middle aligned center aligned grid">
    <div class="column">
        <h1>Success</h1>
    </div>
</div>

</body>

</html>

application.properties

#Thymeleaf配置
spring.thymeleaf.cache=false
spring.thymeleaf.encoding=utf-8
spring.thymeleaf.mode=HTML5
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html

server.port=8443
server.ssl.key-alias=selfsigned_localhost_sslserver
server.ssl.key-password=it1995
server.ssl.key-store=classpath:ssl-server.jks
server.ssl.key-store-provider=SUN
server.ssl.key-store-type=JKS

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>LoginAuthority</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.1.10.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
            <version>2.3.12.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-ext-jdk16</artifactId>
            <version>1.45</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.1.10.RELEASE</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

源码打包下载地址:

Java/LoginAuthority at master · fengfanchen/Java · GitHub

相关文章