
--- src/main/java/kr/co/takensoft/ai/system/auth/service/impl/AuthServiceImpl.java
+++ src/main/java/kr/co/takensoft/ai/system/auth/service/impl/AuthServiceImpl.java
... | ... | @@ -18,10 +18,10 @@ |
18 | 18 |
import kr.co.takensoft.ai.system.auth.vo.MemberVO; |
19 | 19 |
import kr.co.takensoft.ai.system.common.idgen.service.IdgenService; |
20 | 20 |
import kr.co.takensoft.ai.system.common.util.JwtUtil; |
21 |
+import kr.co.takensoft.ai.system.common.util.PasswordEncryptor; |
|
21 | 22 |
import kr.co.takensoft.ai.system.common.util.Secret; |
22 | 23 |
import lombok.RequiredArgsConstructor; |
23 | 24 |
import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl; |
24 |
-import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; |
|
25 | 25 |
import org.springframework.stereotype.Service; |
26 | 26 |
|
27 | 27 |
import java.util.HashMap; |
... | ... | @@ -32,10 +32,11 @@ |
32 | 32 |
public class AuthServiceImpl extends EgovAbstractServiceImpl implements AuthService { |
33 | 33 |
|
34 | 34 |
private final AuthDAO authDAO; |
35 |
- private final BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); |
|
36 | 35 |
private final IdgenService memberIdgn; |
37 | 36 |
private final JwtUtil jwtUtil; |
38 | 37 |
private final RefreshDAO refreshDAO; |
38 |
+ |
|
39 |
+ private static final int ITERATIONS = 10000; |
|
39 | 40 |
|
40 | 41 |
/** |
41 | 42 |
* @param member 사용자 정보 |
... | ... | @@ -47,7 +48,11 @@ |
47 | 48 |
try { |
48 | 49 |
String memberId = memberIdgn.getNextStringId(); // 사용자 구분 아이디 생성 |
49 | 50 |
member.setMemberId(memberId); // 사용자 구분 아이디 등록 |
50 |
- member.setPassword(bCryptPasswordEncoder.encode(member.getPassword())); // 비밀 번호 단방향 암호화 |
|
51 |
+ String salt = PasswordEncryptor.generateSalt(); // 비밀번호 솔트 생성 |
|
52 |
+ String hashedPassword = PasswordEncryptor.sha256EncryptWithSalt(member.getPassword(), salt, ITERATIONS); // 비밀번호 SHA256 암호화 |
|
53 |
+ |
|
54 |
+ member.setPassword(hashedPassword); |
|
55 |
+ member.setSalt(salt); // 비밀번호 솔트 저장 |
|
51 | 56 |
member.setEmail(Secret.encrypt(member.getEmail())); // 이메일 양방향 암호화 |
52 | 57 |
member.setPhoneNumber(Secret.encrypt(member.getPhoneNumber())); // 전화번호 양방향 암호화 |
53 | 58 |
return authDAO.memberRegister(member); |
... | ... | @@ -69,7 +74,7 @@ |
69 | 74 |
if (member == null) { |
70 | 75 |
throw new Exception("잘못된 아이디입니다"); // 아이디가 없을 경우 |
71 | 76 |
} |
72 |
- if(!bCryptPasswordEncoder.matches(loginDTO.getPassword(), member.getPassword())) { |
|
77 |
+ if(!PasswordEncryptor.isPasswordMatch(loginDTO.getPassword(), member.getPassword(), member.getSalt(), ITERATIONS)) { |
|
73 | 78 |
throw new Exception("잘못된 비밀번호입니다"); // 비밀번호가 틀릴 경우 |
74 | 79 |
} |
75 | 80 |
|
--- src/main/java/kr/co/takensoft/ai/system/auth/vo/MemberVO.java
+++ src/main/java/kr/co/takensoft/ai/system/auth/vo/MemberVO.java
... | ... | @@ -26,6 +26,7 @@ |
26 | 26 |
private String email; |
27 | 27 |
private String phoneNumber; |
28 | 28 |
private String memberName; |
29 |
+ private String salt; |
|
29 | 30 |
private String createdAt; |
30 | 31 |
private String updatedDt; |
31 | 32 |
} |
--- src/main/java/kr/co/takensoft/ai/system/auth/web/AuthController.java
+++ src/main/java/kr/co/takensoft/ai/system/auth/web/AuthController.java
... | ... | @@ -47,7 +47,7 @@ |
47 | 47 |
* @param loginDTO 로그인 정보 |
48 | 48 |
* @return ResponseEntity 액세스 토큰 / 리프레시 토큰을 포함하는 응답 |
49 | 49 |
* |
50 |
- * 사용자 회원 가입 |
|
50 |
+ * 사용자 로그인 |
|
51 | 51 |
*/ |
52 | 52 |
@PostMapping("/login.json") |
53 | 53 |
public ResponseEntity<?> login(@RequestBody LoginDTO loginDTO) throws Exception { |
+++ src/main/java/kr/co/takensoft/ai/system/common/util/KISA_SHA256.java
... | ... | @@ -0,0 +1,432 @@ |
1 | +package kr.co.takensoft.ai.system.common.util; | |
2 | + | |
3 | +/** | |
4 | + @file KISA_SHA256.java | |
5 | + @brief SHA256 암호 알고리즘 | |
6 | + @author Copyright (c) 2013 by KISA | |
7 | + @remarks http://seed.kisa.or.kr/ | |
8 | + */ | |
9 | +public class KISA_SHA256 { | |
10 | + | |
11 | + // DEFAULT : JAVA = BIG_ENDIAN | |
12 | + private static int ENDIAN = Common.BIG_ENDIAN; | |
13 | + | |
14 | + private static final int SHA256_DIGEST_BLOCKLEN = 64; | |
15 | + private static final int SHA256_DIGEST_VALUELEN = 32; | |
16 | + | |
17 | + private static final int SHA256_K[] = | |
18 | + { | |
19 | + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, | |
20 | + 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, | |
21 | + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, | |
22 | + 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, | |
23 | + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, | |
24 | + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, | |
25 | + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, | |
26 | + 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, | |
27 | + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, | |
28 | + 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, | |
29 | + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 | |
30 | + }; | |
31 | + | |
32 | + | |
33 | + private static final int ROTL_ULONG(int x, int n) { | |
34 | + return (x << n) | Common.URShift(x, 32 - n); | |
35 | + } | |
36 | + | |
37 | + private static final int ROTR_ULONG(int x, int n) { | |
38 | + return Common.URShift(x, n) | (x << (32 - (n))); | |
39 | + } | |
40 | + | |
41 | + private static final int ENDIAN_REVERSE_ULONG(int dwS) { | |
42 | + return ( (ROTL_ULONG((dwS), 8) & 0x00ff00ff) | (ROTL_ULONG((dwS), 24) & 0xff00ff00) ); | |
43 | + } | |
44 | + | |
45 | + private static final void BIG_D2B(int D, byte[] B, int B_offset) { | |
46 | + Common.int_to_byte_unit(B, B_offset, D, ENDIAN); | |
47 | + } | |
48 | + | |
49 | + private static final int RR(int x, int n) { return ROTR_ULONG(x, n); } | |
50 | + private static final int SS(int x, int n) { return Common.URShift(x, n); } | |
51 | + | |
52 | + private static final int Ch(int x, int y, int z) { return ((x & y) ^ ((~x) & z)); } | |
53 | + private static final int Maj(int x, int y, int z) { return ((x & y) ^ (x & z) ^ (y & z)); } | |
54 | + private static final int Sigma0(int x) { return (RR(x, 2) ^ RR(x, 13) ^ RR(x, 22)); } | |
55 | + private static final int Sigma1(int x) { return (RR(x, 6) ^ RR(x, 11) ^ RR(x, 25)); } | |
56 | + | |
57 | + private static final int RHO0(int x) { return (RR(x, 7) ^ RR(x, 18) ^ SS(x, 3)); } | |
58 | + private static final int RHO1(int x) { return (RR(x, 17) ^ RR(x, 19) ^ SS(x, 10)); } | |
59 | + | |
60 | + private static final int abcdefgh_a = 0; | |
61 | + private static final int abcdefgh_b = 1; | |
62 | + private static final int abcdefgh_c = 2; | |
63 | + private static final int abcdefgh_d = 3; | |
64 | + private static final int abcdefgh_e = 4; | |
65 | + private static final int abcdefgh_f = 5; | |
66 | + private static final int abcdefgh_g = 6; | |
67 | + private static final int abcdefgh_h = 7; | |
68 | + | |
69 | + private static final void FF(int[] abcdefgh, int a, int b, int c, int d, int e, int f, int g, int h, int[] X, int j) { | |
70 | + long T1; | |
71 | + | |
72 | + T1 = Common.intToUnsigned(abcdefgh[h]) + Common.intToUnsigned(Sigma1(abcdefgh[e])) + Common.intToUnsigned(Ch(abcdefgh[e], abcdefgh[f], abcdefgh[g])) + Common.intToUnsigned(SHA256_K[j]) + Common.intToUnsigned(X[j]); | |
73 | + abcdefgh[d] += T1; | |
74 | + abcdefgh[h] = (int)(T1 + Common.intToUnsigned(Sigma0(abcdefgh[a])) + Common.intToUnsigned(Maj(abcdefgh[a], abcdefgh[b], abcdefgh[c]))); | |
75 | + } | |
76 | + | |
77 | + private static final int GetData(byte[] x, int x_offset) { | |
78 | + return Common.byte_to_int(x, x_offset, ENDIAN); | |
79 | + } | |
80 | + | |
81 | + //********************************************************************************************************************************* | |
82 | + // o SHA256_Transform() : 512 비트 단위 블록의 메시지를 입력 받아 연쇄변수를 갱신하는 압축 함수로써 | |
83 | + // 4 라운드(64 단계)로 구성되며 8개의 연쇄변수(a, b, c, d, e, f, g, h)를 사용 | |
84 | + // o 입력 : Message - 입력 메시지의 포인터 변수 | |
85 | + // ChainVar - 연쇄변수의 포인터 변수 | |
86 | + // o 출력 : | |
87 | + //********************************************************************************************************************************* | |
88 | + private static void SHA256_Transform(byte[] Message, int[] ChainVar) { | |
89 | + int abcdefgh[] = new int[8]; | |
90 | + int T1[] = new int[1]; | |
91 | + int X[] = new int[64]; | |
92 | + int j; | |
93 | + | |
94 | + for (j = 0; j < 16; j++) | |
95 | + X[j] = GetData(Message, j*4); | |
96 | + | |
97 | + for (j = 16; j < 64; j++) | |
98 | + X[j] = (int)(Common.intToUnsigned(RHO1(X[j - 2])) + Common.intToUnsigned(X[j - 7]) + Common.intToUnsigned(RHO0(X[j - 15])) + Common.intToUnsigned(X[j - 16])); | |
99 | + | |
100 | + abcdefgh[abcdefgh_a] = ChainVar[0]; | |
101 | + abcdefgh[abcdefgh_b] = ChainVar[1]; | |
102 | + abcdefgh[abcdefgh_c] = ChainVar[2]; | |
103 | + abcdefgh[abcdefgh_d] = ChainVar[3]; | |
104 | + abcdefgh[abcdefgh_e] = ChainVar[4]; | |
105 | + abcdefgh[abcdefgh_f] = ChainVar[5]; | |
106 | + abcdefgh[abcdefgh_g] = ChainVar[6]; | |
107 | + abcdefgh[abcdefgh_h] = ChainVar[7]; | |
108 | + | |
109 | + for (j = 0; j < 64; j += 8) | |
110 | + { | |
111 | + FF(abcdefgh, abcdefgh_a, abcdefgh_b, abcdefgh_c, abcdefgh_d, abcdefgh_e, abcdefgh_f, abcdefgh_g, abcdefgh_h, X, j + 0); | |
112 | + FF(abcdefgh, abcdefgh_h, abcdefgh_a, abcdefgh_b, abcdefgh_c, abcdefgh_d, abcdefgh_e, abcdefgh_f, abcdefgh_g, X, j + 1); | |
113 | + FF(abcdefgh, abcdefgh_g, abcdefgh_h, abcdefgh_a, abcdefgh_b, abcdefgh_c, abcdefgh_d, abcdefgh_e, abcdefgh_f, X, j + 2); | |
114 | + FF(abcdefgh, abcdefgh_f, abcdefgh_g, abcdefgh_h, abcdefgh_a, abcdefgh_b, abcdefgh_c, abcdefgh_d, abcdefgh_e, X, j + 3); | |
115 | + FF(abcdefgh, abcdefgh_e, abcdefgh_f, abcdefgh_g, abcdefgh_h, abcdefgh_a, abcdefgh_b, abcdefgh_c, abcdefgh_d, X, j + 4); | |
116 | + FF(abcdefgh, abcdefgh_d, abcdefgh_e, abcdefgh_f, abcdefgh_g, abcdefgh_h, abcdefgh_a, abcdefgh_b, abcdefgh_c, X, j + 5); | |
117 | + FF(abcdefgh, abcdefgh_c, abcdefgh_d, abcdefgh_e, abcdefgh_f, abcdefgh_g, abcdefgh_h, abcdefgh_a, abcdefgh_b, X, j + 6); | |
118 | + FF(abcdefgh, abcdefgh_b, abcdefgh_c, abcdefgh_d, abcdefgh_e, abcdefgh_f, abcdefgh_g, abcdefgh_h, abcdefgh_a, X, j + 7); | |
119 | + } | |
120 | + | |
121 | + ChainVar[0] += abcdefgh[abcdefgh_a]; | |
122 | + ChainVar[1] += abcdefgh[abcdefgh_b]; | |
123 | + ChainVar[2] += abcdefgh[abcdefgh_c]; | |
124 | + ChainVar[3] += abcdefgh[abcdefgh_d]; | |
125 | + ChainVar[4] += abcdefgh[abcdefgh_e]; | |
126 | + ChainVar[5] += abcdefgh[abcdefgh_f]; | |
127 | + ChainVar[6] += abcdefgh[abcdefgh_g]; | |
128 | + ChainVar[7] += abcdefgh[abcdefgh_h]; | |
129 | + } | |
130 | + | |
131 | + /** | |
132 | + @brief 연쇄변수와 길이변수를 초기화하는 함수 | |
133 | + @param Info : SHA256_Process 호출 시 사용되는 구조체 | |
134 | + */ | |
135 | + public static void SHA256_Init( SHA256_INFO Info ) { | |
136 | + Info.uChainVar[0] = 0x6a09e667; | |
137 | + Info.uChainVar[1] = 0xbb67ae85; | |
138 | + Info.uChainVar[2] = 0x3c6ef372; | |
139 | + Info.uChainVar[3] = 0xa54ff53a; | |
140 | + Info.uChainVar[4] = 0x510e527f; | |
141 | + Info.uChainVar[5] = 0x9b05688c; | |
142 | + Info.uChainVar[6] = 0x1f83d9ab; | |
143 | + Info.uChainVar[7] = 0x5be0cd19; | |
144 | + | |
145 | + Info.uHighLength = Info.uLowLength = Info.remainNum = 0; | |
146 | + } | |
147 | + | |
148 | + /** | |
149 | + @brief 연쇄변수와 길이변수를 초기화하는 함수 | |
150 | + @param Info : SHA256_Init 호출하여 초기화된 구조체(내부적으로 사용된다.) | |
151 | + @param pszMessage : 사용자 입력 평문 | |
152 | + @param inLen : 사용자 입력 평문 길이 | |
153 | + */ | |
154 | + public static void SHA256_Process( SHA256_INFO Info, byte[] pszMessage, int uDataLen ) { | |
155 | + int pszMessage_offset = Info.remainNum; | |
156 | + | |
157 | + if((Info.uLowLength += (uDataLen << 3)) < 0) { | |
158 | + Info.uHighLength++; | |
159 | + } | |
160 | + | |
161 | + Info.uHighLength += Common.URShift(uDataLen,29); | |
162 | + | |
163 | + while (uDataLen + pszMessage_offset >= SHA256_DIGEST_BLOCKLEN) { | |
164 | + Common.arraycopy_offset(Info.szBuffer, pszMessage_offset, pszMessage, 0, SHA256_DIGEST_BLOCKLEN); | |
165 | + SHA256_Transform(Info.szBuffer, Info.uChainVar); | |
166 | + pszMessage_offset += SHA256_DIGEST_BLOCKLEN - pszMessage_offset; | |
167 | + uDataLen -= SHA256_DIGEST_BLOCKLEN - pszMessage_offset; | |
168 | + pszMessage_offset = 0; | |
169 | + } | |
170 | + | |
171 | + Common.arraycopy_offset(Info.szBuffer, pszMessage_offset, pszMessage, 0, uDataLen); | |
172 | + Info.remainNum = pszMessage_offset + uDataLen; | |
173 | + } | |
174 | + | |
175 | + /** | |
176 | + @brief 메시지 덧붙이기와 길이 덧붙이기를 수행한 후 마지막 메시지 블록을 가지고 압축함수를 호출하는 함수 | |
177 | + @param Info : SHA256_Init 호출하여 초기화된 구조체(내부적으로 사용된다.) | |
178 | + @param pszDigest : 암호문 | |
179 | + */ | |
180 | + public static void SHA256_Close( SHA256_INFO Info, byte[] pszDigest ) { | |
181 | + int i, Index; | |
182 | + | |
183 | + Index = Common.URShift(Info.uLowLength, 3) % SHA256_DIGEST_BLOCKLEN; | |
184 | + Info.szBuffer[Index++] = (byte)0x80; | |
185 | + | |
186 | + if (Index > SHA256_DIGEST_BLOCKLEN - 8) { | |
187 | + Common.arrayinit_offset(Info.szBuffer, Index, (byte)0, SHA256_DIGEST_BLOCKLEN - Index); | |
188 | + SHA256_Transform(Info.szBuffer, Info.uChainVar); | |
189 | + Common.arrayinit(Info.szBuffer, (byte)0, SHA256_DIGEST_BLOCKLEN - 8); | |
190 | + } | |
191 | + else { | |
192 | + Common.arrayinit_offset(Info.szBuffer, Index, (byte)0, SHA256_DIGEST_BLOCKLEN - Index - 8); | |
193 | + } | |
194 | + | |
195 | + if(ENDIAN == Common.LITTLE_ENDIAN) { | |
196 | + Info.uLowLength = ENDIAN_REVERSE_ULONG(Info.uLowLength); | |
197 | + Info.uHighLength = ENDIAN_REVERSE_ULONG(Info.uHighLength); | |
198 | + } | |
199 | + | |
200 | + Common.int_to_byte_unit(Info.szBuffer, ((int)(SHA256_DIGEST_BLOCKLEN / 4 - 2))*4, Info.uHighLength, ENDIAN); | |
201 | + Common.int_to_byte_unit(Info.szBuffer, ((int)(SHA256_DIGEST_BLOCKLEN / 4 - 1))*4, Info.uLowLength, ENDIAN); | |
202 | + | |
203 | + SHA256_Transform(Info.szBuffer, Info.uChainVar); | |
204 | + | |
205 | + for (i = 0; i < SHA256_DIGEST_VALUELEN; i += 4) | |
206 | + BIG_D2B((Info.uChainVar)[i / 4], pszDigest, i); | |
207 | + } | |
208 | + | |
209 | + /** | |
210 | + @brief 사용자 입력 평문을 한번에 처리 | |
211 | + @param pszMessage : 사용자 입력 평문 | |
212 | + @param pszDigest : 암호문 | |
213 | + @remarks 내부적으로 SHA256_Init, SHA256_Process, SHA256_Close를 호출한다. | |
214 | + */ | |
215 | + public static void SHA256_Encrpyt( byte[] pszMessage, int uPlainTextLen, byte[] pszDigest ) { | |
216 | + SHA256_INFO info = new SHA256_INFO(); | |
217 | + SHA256_Init( info ); | |
218 | + SHA256_Process( info, pszMessage, uPlainTextLen ); | |
219 | + SHA256_Close( info, pszDigest ); | |
220 | + } | |
221 | + | |
222 | + | |
223 | + public static class SHA256_INFO { | |
224 | + public int uChainVar[] = new int[SHA256_DIGEST_VALUELEN / 4]; | |
225 | + public int uHighLength; | |
226 | + public int uLowLength; | |
227 | + public int remainNum; | |
228 | + public byte szBuffer[] = new byte[SHA256_DIGEST_BLOCKLEN]; | |
229 | + } | |
230 | + | |
231 | + public static class Common { | |
232 | + | |
233 | + public static final int BIG_ENDIAN = 0; | |
234 | + public static final int LITTLE_ENDIAN = 1; | |
235 | + | |
236 | + public static void arraycopy(byte[] dst, byte[] src, int length) { | |
237 | + for(int i=0; i<length; i++) { | |
238 | + dst[i] = src[i]; | |
239 | + } | |
240 | + } | |
241 | + | |
242 | + public static void arraycopy_offset(byte[] dst, int dst_offset, byte[] src, int src_offset, int length) { | |
243 | + for(int i=0; i<length; i++) { | |
244 | + dst[dst_offset+i] = src[src_offset+i]; | |
245 | + } | |
246 | + } | |
247 | + | |
248 | + public static void arrayinit(byte[] dst, byte value, int length) { | |
249 | + for(int i=0; i<length; i++) { | |
250 | + dst[i] = value; | |
251 | + } | |
252 | + } | |
253 | + | |
254 | + public static void arrayinit_offset(byte[] dst, int dst_offset, byte value, int length) { | |
255 | + for(int i=0; i<length; i++) { | |
256 | + dst[dst_offset+i] = value; | |
257 | + } | |
258 | + } | |
259 | + | |
260 | + public static void memcpy(int[] dst, byte[] src, int length, int ENDIAN) { | |
261 | + int iLen = length / 4; | |
262 | + for(int i=0; i<iLen; i++) { | |
263 | + byte_to_int(dst, i, src, i*4, ENDIAN); | |
264 | + } | |
265 | + } | |
266 | + | |
267 | + public static void memcpy(int[] dst, int[] src, int src_offset, int length) { | |
268 | + int iLen = length / 4 + ((length % 4 != 0)?1:0); | |
269 | + for(int i=0; i<iLen; i++) { | |
270 | + dst[i] = src[src_offset+i]; | |
271 | + } | |
272 | + } | |
273 | + | |
274 | + public static void set_byte_for_int(int[] dst, int b_offset, byte value, int ENDIAN) { | |
275 | + if(ENDIAN == BIG_ENDIAN) { | |
276 | + int shift_value = (3-b_offset%4)*8; | |
277 | + int mask_value = 0x0ff << shift_value; | |
278 | + int mask_value2 = ~mask_value; | |
279 | + int value2 = (value&0x0ff) << shift_value; | |
280 | + dst[b_offset/4] = (dst[b_offset/4] & mask_value2) | (value2 & mask_value); | |
281 | + } else { | |
282 | + int shift_value = (b_offset%4)*8; | |
283 | + int mask_value = 0x0ff << shift_value; | |
284 | + int mask_value2 = ~mask_value; | |
285 | + int value2 = (value&0x0ff) << shift_value; | |
286 | + dst[b_offset/4] = (dst[b_offset/4] & mask_value2) | (value2 & mask_value); | |
287 | + } | |
288 | + } | |
289 | + | |
290 | + public static byte get_byte_for_int(int[] src, int b_offset, int ENDIAN) { | |
291 | + if(ENDIAN == BIG_ENDIAN) { | |
292 | + int shift_value = (3-b_offset%4)*8; | |
293 | + int mask_value = 0x0ff << shift_value; | |
294 | + int value = (src[b_offset/4] & mask_value) >> shift_value; | |
295 | + return (byte)value; | |
296 | + } else { | |
297 | + int shift_value = (b_offset%4)*8; | |
298 | + int mask_value = 0x0ff << shift_value; | |
299 | + int value = (src[b_offset/4] & mask_value) >> shift_value; | |
300 | + return (byte)value; | |
301 | + } | |
302 | + | |
303 | + } | |
304 | + | |
305 | + public static byte[] get_bytes_for_ints(int[] src, int offset, int ENDIAN) { | |
306 | + int iLen = src.length-offset; | |
307 | + byte[] result = new byte[(iLen)*4]; | |
308 | + for(int i=0; i<iLen; i++) { | |
309 | + int_to_byte(result, i*4, src, offset+i, ENDIAN); | |
310 | + } | |
311 | + | |
312 | + return result; | |
313 | + } | |
314 | + | |
315 | + public static void byte_to_int(int[] dst, int dst_offset, byte[] src, int src_offset, int ENDIAN) { | |
316 | + if(ENDIAN == BIG_ENDIAN) { | |
317 | + dst[dst_offset] = ((0x0ff&src[src_offset]) << 24) | ((0x0ff&src[src_offset+1]) << 16) | ((0x0ff&src[src_offset+2]) << 8) | ((0x0ff&src[src_offset+3])); | |
318 | + } else { | |
319 | + dst[dst_offset] = ((0x0ff&src[src_offset])) | ((0x0ff&src[src_offset+1]) << 8) | ((0x0ff&src[src_offset+2]) << 16) | ((0x0ff&src[src_offset+3]) << 24); | |
320 | + } | |
321 | + } | |
322 | + | |
323 | + public static int byte_to_int(byte[] src, int src_offset, int ENDIAN) { | |
324 | + if(ENDIAN == BIG_ENDIAN) { | |
325 | + return ((0x0ff&src[src_offset]) << 24) | ((0x0ff&src[src_offset+1]) << 16) | ((0x0ff&src[src_offset+2]) << 8) | ((0x0ff&src[src_offset+3])); | |
326 | + } else { | |
327 | + return ((0x0ff&src[src_offset])) | ((0x0ff&src[src_offset+1]) << 8) | ((0x0ff&src[src_offset+2]) << 16) | ((0x0ff&src[src_offset+3]) << 24); | |
328 | + } | |
329 | + } | |
330 | + | |
331 | + public static int byte_to_int_big_endian(byte[] src, int src_offset) { | |
332 | + return ((0x0ff&src[src_offset]) << 24) | ((0x0ff&src[src_offset+1]) << 16) | ((0x0ff&src[src_offset+2]) << 8) | ((0x0ff&src[src_offset+3])); | |
333 | + } | |
334 | + | |
335 | + public static void int_to_byte(byte[] dst, int dst_offset, int[] src, int src_offset, int ENDIAN) { | |
336 | + int_to_byte_unit(dst, dst_offset, src[src_offset], ENDIAN); | |
337 | + } | |
338 | + | |
339 | + public static void int_to_byte_unit(byte[] dst, int dst_offset, int src, int ENDIAN) { | |
340 | + if(ENDIAN == BIG_ENDIAN) { | |
341 | + dst[dst_offset] = (byte)((src>> 24) & 0x0ff); | |
342 | + dst[dst_offset+1] = (byte)((src >> 16) & 0x0ff); | |
343 | + dst[dst_offset+2] = (byte)((src >> 8) & 0x0ff); | |
344 | + dst[dst_offset+3] = (byte)((src) & 0x0ff); | |
345 | + } else { | |
346 | + dst[dst_offset] = (byte)((src) & 0x0ff); | |
347 | + dst[dst_offset+1] = (byte)((src >> 8) & 0x0ff); | |
348 | + dst[dst_offset+2] = (byte)((src >> 16) & 0x0ff); | |
349 | + dst[dst_offset+3] = (byte)((src >> 24) & 0x0ff); | |
350 | + } | |
351 | + | |
352 | + } | |
353 | + | |
354 | + public static void int_to_byte_unit_big_endian(byte[] dst, int dst_offset, int src) { | |
355 | + dst[dst_offset] = (byte)((src>> 24) & 0x0ff); | |
356 | + dst[dst_offset+1] = (byte)((src >> 16) & 0x0ff); | |
357 | + dst[dst_offset+2] = (byte)((src >> 8) & 0x0ff); | |
358 | + dst[dst_offset+3] = (byte)((src) & 0x0ff); | |
359 | + } | |
360 | + | |
361 | + public static int URShift(int x, int n) { | |
362 | + if(n == 0) | |
363 | + return x; | |
364 | + if(n >= 32) | |
365 | + return 0; | |
366 | + int v = x >> n; | |
367 | + int v_mask = ~(0x80000000 >> (n-1)); | |
368 | + return v & v_mask; | |
369 | + } | |
370 | + | |
371 | + public static final long INT_RANGE_MAX = (long)Math.pow(2, 32); | |
372 | + | |
373 | + public static long intToUnsigned(int x) { | |
374 | + if(x >= 0) | |
375 | + return x; | |
376 | + return x + INT_RANGE_MAX; | |
377 | + } | |
378 | + } | |
379 | + | |
380 | + public static void main(String[] args) | |
381 | + { | |
382 | + byte pbData[] = {(byte)0x00, (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04, (byte)0x05, (byte)0x06, (byte)0x07, | |
383 | + (byte)0x08, (byte)0x09, (byte)0x0A, (byte)0x0B, (byte)0x0C, (byte)0x0D, (byte)0x0E, (byte)0x0F, | |
384 | + (byte)0x08, (byte)0x09, (byte)0x0A, (byte)0x0B, (byte)0x0C, (byte)0x0D, (byte)0x0E, (byte)0x0F, | |
385 | + (byte)0x00, (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04, (byte)0x05, (byte)0x06, (byte)0x07, | |
386 | + (byte)0x08, (byte)0x09, (byte)0x0A, (byte)0x0B, (byte)0x0C, (byte)0x0D, (byte)0x0E, (byte)0x0F, | |
387 | + (byte)0x08, (byte)0x09, (byte)0x0A, (byte)0x0B, (byte)0x0C, (byte)0x0D, (byte)0x0E, (byte)0x0F}; | |
388 | + byte pbData1[] = {(byte)0x61}; | |
389 | + | |
390 | + byte pbCipher[] = new byte[32]; | |
391 | + byte pbPlain[] = new byte[16]; | |
392 | + | |
393 | + System.out.print("[ Test SHA256 reference code ]"+"\n"); | |
394 | + System.out.print("\n\n"); | |
395 | + System.out.print("[ Test HASH mode ]"+"\n"); | |
396 | + System.out.print("\n"); | |
397 | + | |
398 | + int Plaintext_length = 1; | |
399 | + | |
400 | + for(int k=0; k<30; k++) | |
401 | + { | |
402 | + | |
403 | + System.out.print("Plaintext\t: "); | |
404 | + for (int i=0; i<Plaintext_length; i++) System.out.print(Integer.toHexString(0xff&pbData[i])+" "); | |
405 | + System.out.print("\n"); | |
406 | + | |
407 | + // Encryption | |
408 | + SHA256_Encrpyt( pbData, Plaintext_length, pbCipher ); | |
409 | + | |
410 | + System.out.print("Ciphertext\t: "); | |
411 | + for (int i=0; i<32; i++) System.out.print(Integer.toHexString(0xff&pbCipher[i])+" "); | |
412 | + System.out.print("\n\n"); | |
413 | + | |
414 | + Plaintext_length++; | |
415 | + | |
416 | + } | |
417 | + | |
418 | + | |
419 | + System.out.print("Plaintext\t: "); | |
420 | + for (int i=0; i<1; i++) System.out.print(Integer.toHexString(0xff&pbData1[i])+" "); | |
421 | + System.out.print("\n"); | |
422 | + // Encryption | |
423 | + SHA256_Encrpyt( pbData1, 1, pbCipher ); | |
424 | + System.out.print("Ciphertext\t: "); | |
425 | + for (int i=0; i<32; i++) System.out.print(Integer.toHexString(0xff&pbCipher[i])+" "); | |
426 | + System.out.print("\n\n"); | |
427 | + | |
428 | + | |
429 | + | |
430 | + } | |
431 | + | |
432 | +} |
+++ src/main/java/kr/co/takensoft/ai/system/common/util/PasswordEncryptor.java
... | ... | @@ -0,0 +1,47 @@ |
1 | +package kr.co.takensoft.ai.system.common.util; | |
2 | + | |
3 | +import java.nio.charset.StandardCharsets; | |
4 | +import java.security.SecureRandom; | |
5 | +import java.util.Base64; | |
6 | + | |
7 | +public class PasswordEncryptor { | |
8 | + | |
9 | + /** | |
10 | + * 랜덤 Salt 생성 (16바이트 = 128비트) | |
11 | + */ | |
12 | + public static String generateSalt() { | |
13 | + byte[] salt = new byte[16]; | |
14 | + new SecureRandom().nextBytes(salt); | |
15 | + return Base64.getEncoder().encodeToString(salt); | |
16 | + } | |
17 | + | |
18 | + /** | |
19 | + * SHA-256 + Salt + 반복 해싱 | |
20 | + */ | |
21 | + public static String sha256EncryptWithSalt(String password, String salt, int iterations) { | |
22 | + byte[] hash = (password + salt).getBytes(StandardCharsets.UTF_8); | |
23 | + | |
24 | + for (int i = 0; i < iterations; i++) { | |
25 | + byte[] hashed = new byte[32]; // SHA-256은 32바이트 | |
26 | + KISA_SHA256.SHA256_Encrpyt(hash, hash.length, hashed); | |
27 | + hash = hashed; // 다음 라운드의 입력으로 사용 | |
28 | + } | |
29 | + | |
30 | + // 바이트 배열 → Hex 문자열 | |
31 | + StringBuilder sb = new StringBuilder(); | |
32 | + for (byte b : hash) { | |
33 | + sb.append(String.format("%02x", b)); | |
34 | + } | |
35 | + | |
36 | + return sb.toString(); | |
37 | + } | |
38 | + | |
39 | + /** | |
40 | + * 비밀번호 비교 | |
41 | + */ | |
42 | + public static boolean isPasswordMatch(String inputPassword, String storedHash, String salt, int iterations) { | |
43 | + String hashedInput = sha256EncryptWithSalt(inputPassword, salt, iterations); | |
44 | + return hashedInput.equals(storedHash); | |
45 | + } | |
46 | + | |
47 | +} |
--- src/main/resources/mybatis/mapper/auth/auth-SQL.xml
+++ src/main/resources/mybatis/mapper/auth/auth-SQL.xml
... | ... | @@ -11,6 +11,7 @@ |
11 | 11 |
(member_id, |
12 | 12 |
login_id, |
13 | 13 |
password, |
14 |
+ salt, |
|
14 | 15 |
email, |
15 | 16 |
phone_number, |
16 | 17 |
member_name, |
... | ... | @@ -21,6 +22,7 @@ |
21 | 22 |
#{memberId}, |
22 | 23 |
#{loginId}, |
23 | 24 |
#{password}, |
25 |
+ #{salt}, |
|
24 | 26 |
#{email}, |
25 | 27 |
#{phoneNumber}, |
26 | 28 |
#{memberName}, |
... | ... | @@ -39,7 +41,7 @@ |
39 | 41 |
select |
40 | 42 |
* |
41 | 43 |
from member |
42 |
- where member_id = #{memberId} |
|
44 |
+ where login_id = #{loginId} |
|
43 | 45 |
</select> |
44 | 46 |
|
45 | 47 |
</mapper>(No newline at end of file) |
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?