change directory
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
package com.example.httpencryption;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.web.servlet.ServletComponentScan;
|
||||
|
||||
@ServletComponentScan(basePackages = {"com.example.httpencryption.common"})
|
||||
@SpringBootApplication
|
||||
public class HttpEncryptionApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(HttpEncryptionApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.example.httpencryption.common;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.annotation.WebFilter;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
@Slf4j
|
||||
@WebFilter("/*")
|
||||
public class HttpEncryptionFilter implements Filter {
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
|
||||
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
|
||||
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
|
||||
|
||||
RequestDecryptWrapper requestDecryptWrapper = new RequestDecryptWrapper(httpServletRequest);
|
||||
ResponseEncryptWrapper responseEncryptWrapper = new ResponseEncryptWrapper(httpServletResponse);
|
||||
|
||||
chain.doFilter(requestDecryptWrapper, responseEncryptWrapper);
|
||||
|
||||
httpServletResponse.getOutputStream().write(responseEncryptWrapper.encryptResponse());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.example.httpencryption.common;
|
||||
|
||||
import com.example.httpencryption.utils.AESUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import javax.servlet.ReadListener;
|
||||
import javax.servlet.ServletInputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@Slf4j
|
||||
public class RequestDecryptWrapper extends HttpServletRequestWrapper {
|
||||
private final Charset encoding;
|
||||
private String decodingBody;
|
||||
private byte[] rawData;
|
||||
|
||||
public RequestDecryptWrapper(HttpServletRequest request) {
|
||||
super(request);
|
||||
String charEncoding = request.getCharacterEncoding();
|
||||
|
||||
this.encoding = ObjectUtils.isEmpty(charEncoding) ? StandardCharsets.UTF_8 : Charset.forName(charEncoding);
|
||||
|
||||
try {
|
||||
InputStream inputStream = request.getInputStream();
|
||||
rawData = IOUtils.toByteArray(inputStream);
|
||||
|
||||
if (ObjectUtils.isEmpty(rawData)) {
|
||||
return;
|
||||
}
|
||||
AESUtil aesUtil = new AESUtil();
|
||||
|
||||
this.decodingBody = aesUtil.decrypt(new String(rawData, StandardCharsets.UTF_8));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletInputStream getInputStream() {
|
||||
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decodingBody == null ? "".getBytes(encoding) : decodingBody.getBytes(encoding));
|
||||
return new ServletInputStream() {
|
||||
@Override
|
||||
public boolean isFinished() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReady() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadListener(ReadListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() {
|
||||
return byteArrayInputStream.read();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedReader getReader() {
|
||||
return new BufferedReader(new InputStreamReader(this.getInputStream()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.example.httpencryption.common;
|
||||
|
||||
import com.example.httpencryption.utils.AESUtil;
|
||||
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.WriteListener;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponseWrapper;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class ResponseEncryptWrapper extends HttpServletResponseWrapper {
|
||||
private final ByteArrayOutputStream output;
|
||||
|
||||
public ResponseEncryptWrapper(HttpServletResponse response) {
|
||||
super(response);
|
||||
output = new ByteArrayOutputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletOutputStream getOutputStream() throws IOException {
|
||||
return new ServletOutputStream() {
|
||||
@Override
|
||||
public boolean isReady() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWriteListener(WriteListener listener) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
output.write(b);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public byte[] encryptResponse() {
|
||||
String responseMessage = new String(output.toByteArray(), StandardCharsets.UTF_8);
|
||||
AESUtil aesUtil = new AESUtil();
|
||||
return aesUtil.encrypt(responseMessage).getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.example.httpencryption.controller;
|
||||
|
||||
import com.example.httpencryption.dto.TestDto;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.ServletInputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@RestController
|
||||
public class TestController {
|
||||
|
||||
@CrossOrigin
|
||||
@PostMapping("/")
|
||||
public TestDto hello(HttpServletRequest request, @RequestBody TestDto dto) throws IOException {
|
||||
|
||||
ServletInputStream inputStream = request.getInputStream();
|
||||
byte[] bytes = IOUtils.toByteArray(inputStream);
|
||||
String s = new String(bytes, StandardCharsets.UTF_8);
|
||||
System.out.println("s = " + s);
|
||||
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.example.httpencryption.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class TestDto {
|
||||
|
||||
private String username;
|
||||
private int age;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.example.httpencryption.utils;
|
||||
|
||||
import org.springframework.util.Base64Utils;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.*;
|
||||
|
||||
public class AESUtil {
|
||||
|
||||
private final String ALGORITHM = "AES/CBC/PKCS5PADDING";
|
||||
private final String KEY = "example";
|
||||
private String iv;
|
||||
|
||||
public String encrypt(String data) {
|
||||
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(ALGORITHM);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, createKeySpec(), createIvSpec());
|
||||
byte[] encryptData = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
|
||||
return iv + Base64Utils.encodeToString(encryptData);
|
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
|
||||
throw new RuntimeException("encrypt fail : " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public String decrypt(String data) {
|
||||
String ivStr = data.substring(0,16);
|
||||
String content = data.substring(16);
|
||||
byte[] dataBytes = Base64Utils.decodeFromString(content);
|
||||
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(ALGORITHM);
|
||||
cipher.init(Cipher.DECRYPT_MODE, createKeySpec(), new IvParameterSpec(ivStr.getBytes(StandardCharsets.UTF_8)));
|
||||
byte[] original = cipher.doFinal(dataBytes);
|
||||
return new String(original, StandardCharsets.UTF_8);
|
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
|
||||
throw new RuntimeException("decrypt fail : " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private IvParameterSpec createIvSpec() {
|
||||
try {
|
||||
String iv = StringUtil.randomStr(16);
|
||||
this.iv = iv;
|
||||
return new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("createIvSpec fail : " + e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Key createKeySpec() {
|
||||
try {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
byte[] hashBytes = digest.digest(KEY.getBytes(StandardCharsets.UTF_8));
|
||||
return new SecretKeySpec(hashBytes, "AES");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("createKeySpec fail : " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.example.httpencryption.utils;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class StringUtil {
|
||||
|
||||
public static String randomStr(int length) {
|
||||
Random random = new Random();
|
||||
StringBuilder str = new StringBuilder();
|
||||
for (int i = 0; i < length; i++) {
|
||||
int choice = random.nextInt(3);
|
||||
switch(choice) {
|
||||
case 0:
|
||||
str.append((char)(random.nextInt(25)+97));
|
||||
break;
|
||||
case 1:
|
||||
str.append((char)(random.nextInt(25) +65));
|
||||
break;
|
||||
case 2:
|
||||
str.append((char)(random.nextInt(10) +48));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return str.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
77
blog/http-encryption/src/main/resources/templates/index.html
Normal file
77
blog/http-encryption/src/main/resources/templates/index.html
Normal file
@@ -0,0 +1,77 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
<form id="form">
|
||||
<div>
|
||||
<span>이름</span> <input name="username" type="text"/>
|
||||
</div>
|
||||
<div>
|
||||
<span>나이</span> <input name="age" type="text"/>
|
||||
</div>
|
||||
<div>
|
||||
<button type="button" onclick="btn()">등록</button>
|
||||
</div>
|
||||
</form>
|
||||
</body>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
|
||||
<script>
|
||||
const btn = () => {
|
||||
const key = "example";
|
||||
const sha256 = CryptoJS.SHA256(key);
|
||||
const url = "http://localhost:8080"
|
||||
fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: encrypt(sha256)
|
||||
})
|
||||
.then(resp => resp.text())
|
||||
.then(data => {
|
||||
console.log(data);
|
||||
decrypt(data, sha256);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
const encrypt = (sha256) => {
|
||||
let form = document.querySelector('#form');
|
||||
let formData = new FormData(form);
|
||||
let obj = {};
|
||||
for (let key of formData.keys()) {
|
||||
obj[key] = formData.get(key);
|
||||
}
|
||||
let iv = Math.random().toString(36).substring(2, 10) + Math.random().toString(36).substring(2, 10);
|
||||
let string = CryptoJS.AES.encrypt(
|
||||
JSON.stringify(obj),
|
||||
sha256,
|
||||
{
|
||||
iv: CryptoJS.enc.Utf8.parse(iv),
|
||||
mode: CryptoJS.mode.CBC,
|
||||
padding: CryptoJS.pad.Pkcs7,
|
||||
},
|
||||
).toString();
|
||||
return iv + string;
|
||||
}
|
||||
|
||||
const decrypt = (data, sha256) => {
|
||||
const iv = data.substring(0, 16);
|
||||
const content = data.substring(16);
|
||||
|
||||
let result = CryptoJS.AES.decrypt(
|
||||
content,
|
||||
sha256,
|
||||
{
|
||||
iv: CryptoJS.enc.Utf8.parse(iv),
|
||||
padding: CryptoJS.pad.Pkcs7,
|
||||
}
|
||||
).toString(CryptoJS.enc.Utf8);
|
||||
|
||||
console.log(JSON.parse(result));
|
||||
}
|
||||
</script>
|
||||
</html>
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.example.httpencryption;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class HttpEncryptionApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.example.httpencryption.utils;
|
||||
|
||||
import com.example.httpencryption.dto.TestDto;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class AESUtilTest {
|
||||
|
||||
AESUtil util = new AESUtil();
|
||||
|
||||
@Test
|
||||
void encryptTest() throws JsonProcessingException, NoSuchAlgorithmException {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
TestDto testDto = new TestDto("김", 20);
|
||||
|
||||
String data = objectMapper.writeValueAsString(testDto);
|
||||
System.out.println("data = " + data);
|
||||
String encrypt = util.encrypt(data);
|
||||
System.out.println("encrypt = " + encrypt);
|
||||
|
||||
String decrypt = util.decrypt(encrypt);
|
||||
System.out.println("decrypt = " + decrypt);
|
||||
TestDto origin = objectMapper.readValue(decrypt, TestDto.class);
|
||||
|
||||
assertEquals(testDto.getUsername(), origin.getUsername());
|
||||
assertEquals(testDto.getAge(), origin.getAge());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user