# Encrypt - SHA256(with salt) MessageDigest example code
스프링 부트로 작성해보는 MessageDigest 를 이용한 sha-256 암호화 (+ salt) example code
# 참조
SHA-256
SHA(Secure Hash Algorithm) 알고리즘의 한 종류로서 256비트로 구성되며 64자리 문자열을 반환한다.
SHA-256은 미국의 국립표준기술연구소 (NIST; National Institute of Standards and Technology)에 의해 공표된 표준 해시 알고리즘인 SHA-2 계열 중 하나이며 블록체인에서 가장 많이 채택하여 사용하고 있다.
# 환경
# tool: STS 4.13.0
# version: 2.7.3-SNAPSHOT
# java: 11
# type: MAVEN
# view: THYMELEAF
# jQuery: 3.6.0
# PROPERTIES FILE
## CRYPTO
crypto.sha256.algorithm = SHA-256
crypto.sha256.salt = ENC(5IV49eD42Ftq6aTb9BRLqnW6s00M2KsK0cNFbkduWdQKc79le17/6wyHR9W+qi0R)
# Configuration
@Configuration
public class CryptoConfig {
public final static String DEFAULT_CHAR_SET = "UTF-8";
@Bean("sha256Encryptor")
public MessageDigest sha256Encryptor(@Value("${crypto.sha256.algorithm}") String algorithm, @Value("${crypto.sha256.salt}") String salt) {
try {
MessageDigest md = MessageDigest.getInstance(algorithm);
md.update(salt.getBytes(DEFAULT_CHAR_SET));
return md;
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
}
# Service
@Service
public class EncryptService {
@Autowired
private MessageDigest sha256Encryptor;
/**
* SHA256 ENCRYPT : length 64
*/
public String encryptSha256(String password, String charSet) {
String encryptStr = "";
try {
byte[] encryptBytes = sha256Encryptor.digest(password.getBytes());
encryptStr = EncryptService.byteArrayToHexString(encryptBytes);
} catch (Exception e) {
e.printStackTrace();
}
return encryptStr;
}
/**
* SHA256 ENCRYPT EQUAL CHECK
*/
public boolean checkSha256(String password, String charSet, String encPassword) {
String encrypt = this.encryptSha256(password, charSet);
if(encrypt.equals(encPassword)) {
return true;
}
return false;
}
public static String byteArrayToHexString(byte[] bytes) {
if(bytes == null || bytes.length == 0) {
return "";
}
StringBuilder builder = new StringBuilder();
for (byte b: bytes) {
builder.append(String.format("%02X", b));
}
return builder.toString();
}
}
# Controller
@Slf4j
@Controller
@RequestMapping("/test/encrypt")
public class TestEncryptController {
@Autowired
private EncryptService encryptService;
@PostMapping("/sha/encrypt")
@ResponseBody
public Map<String, String> shaEncrypt(@RequestParam String password) {
log.debug("[PARAMETER] {}", password);
String encPassword = encryptService.encryptSha256(password, CryptoConfig.DEFAULT_CHAR_SET);
Map<String, String> response = new HashMap<String, String>();
response.put("password", password);
response.put("encPassword", encPassword);
return response;
}
@PostMapping("/sha/check")
@ResponseBody
public Map<String, Boolean> shaCheck(@RequestParam String password, @RequestParam String encPassword) {
log.debug("[PARAMETER] {}, {}", password, encPassword);
boolean isEqual = encryptService.checkSha256(password, CryptoConfig.DEFAULT_CHAR_SET, encPassword);
Map<String, Boolean> response = new HashMap<String, Boolean>();
response.put("isEqual", isEqual);
return response;
}
}
# 페이지
# HTML
<div>
<h3>SHA 256 MessageDigetst</h3>
<input type="text" id="password" placeholder="Encrypt Password" style="display: inline;"/>
<input type="text" id="encPassword" placeholder="Password Check" style="display: inline;"/>
<br>
<input type="button" onclick="test.shaEncrypt()" value="ENCRYPT" style="display: inline;"/>
<input type="button" onclick="test.shaCheck()" value="CHECK" style="display: inline;"/>
</div>
# javascript (jQuery)
const test = {
shaEncrypt: function() {
const input = {
password: $('#password').val()
}
AjaxUtils.sendPost(`/test/encrypt/sha/encrypt`, input,
function(result) {
console.log("result:", result.encPassword);
$('#encPassword').val(result.encPassword);
}
);
},
shaCheck: function() {
const input = {
password: $('#password').val(),
encPassword: $('#encPassword').val()
}
AjaxUtils.sendPost(`/test/encrypt/sha/check`, input,
function(result) {
console.log("result:", result.isEqual);
}
);
},
}
const AjaxUtils = {
sendPost: function(url, input, cbSuccess) {
$.ajax(url, {
// options
method : "POST",
data : input,
dataType : "json",
// success
}).done(function(output, _, _) {
cbSuccess(output);
})
}
}
# update 와 digest
- 위키의 example code는 update에서 message를 입력하고 digest로 암호화 하지만,
- 블로그 본문에선 update는 salt를 digest에서 message를 입력하고 암호화한다.
함수 doc을 번역하면 아래와 같다.
- update: 지정된 바이트 배열을 사용하여 요약을 업데이트합니다.
- digest: 요약이 완료되기 전에 업데이트할 입력을 입력합니다.
따라서, 순서의 차이는 있겠지만
- update(salt) > update(data) > digest()
- update(salt) > digest(data)
- 위 두개는 같은 값이 나오고,
- update(data) > digest(salt) 는 다른 값이 나옴.
# 암복호화 시리즈
https://hjho95.tistory.com/25 Encrypt - AES128/256 ECB and CBC(with random iv) 테스트 코드
https://hjho95.tistory.com/26 Encrypt - AES128 CBC(with iv) example code
https://hjho95.tistory.com/27 Encrypt - SHA256(with salt) MessageDigest example code
# 암복호화 시리즈 참조 페이지
aes example: https://aesencryption.net/
cbc vs ecb: https://yoda.wiki/wiki/Block_cipher_mode_of_operation
sha example: http://wiki.hash.kr/index.php/SHA256#cite_note-8
'스프링 부트' 카테고리의 다른 글
[SpringBoot] 스프링 부트 프로젝트 생성하기 (0) | 2022.11.06 |
---|---|
[SpringBoot] 필터를 이용하여 요청 로그 남기기! (CommonsRequestLoggingFilter) (0) | 2022.10.22 |
[SpringBoot] Encrypt - AES128 CBC(with iv) example code (0) | 2022.10.03 |
[SpringBoot] Encrypt - AES128/256 ECB and CBC(with random iv) 테스트 코드 (2) | 2022.10.03 |
[SpringBoot] jdk 1.8 > jdk 11 로 변경하기 (2) | 2022.09.12 |