554 lines
24 KiB
Java
554 lines
24 KiB
Java
package com.erp.service;
|
|
|
|
import com.erp.common.BaseService;
|
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
import org.springframework.http.HttpHeaders;
|
|
import org.springframework.http.HttpMethod;
|
|
import org.springframework.http.MediaType;
|
|
import org.springframework.http.RequestEntity;
|
|
import org.springframework.http.ResponseEntity;
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
import org.springframework.web.client.RestTemplate;
|
|
|
|
import javax.crypto.Cipher;
|
|
import javax.crypto.SecretKeyFactory;
|
|
import javax.crypto.spec.GCMParameterSpec;
|
|
import javax.crypto.spec.PBEKeySpec;
|
|
import javax.crypto.spec.SecretKeySpec;
|
|
import java.net.URI;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.security.SecureRandom;
|
|
import java.security.spec.KeySpec;
|
|
import java.util.*;
|
|
|
|
@Service
|
|
@Slf4j
|
|
public class ExternalRestApiConnectionService extends BaseService {
|
|
|
|
@Autowired private CommonService commonService;
|
|
@Autowired private ObjectMapper objectMapper;
|
|
|
|
@Value("${erp.encryption.secret:default-secret-key-change-in-production}")
|
|
private String encryptionKey;
|
|
|
|
private static final String ALGORITHM = "AES/GCM/NoPadding";
|
|
private static final int GCM_TAG_LENGTH = 128;
|
|
private static final int IV_LENGTH = 16;
|
|
|
|
// ── 목록 조회 ──────────────────────────────────────────────────────────
|
|
|
|
public List<Map<String, Object>> getExternalRestApiConnectionList(Map<String, Object> params) {
|
|
String rawSearch = (String) params.get("search");
|
|
if (rawSearch != null && !rawSearch.isBlank()) {
|
|
params.put("search", "%" + rawSearch.trim() + "%");
|
|
}
|
|
String filterCompany = (String) params.get("filter_company_code");
|
|
if (filterCompany != null) {
|
|
params.put("filter_company_code", filterCompany);
|
|
}
|
|
String isActive = (String) params.get("is_active");
|
|
if (isActive != null) params.put("is_active", isActive);
|
|
String authType = (String) params.get("auth_type");
|
|
if (authType != null) params.put("auth_type", authType);
|
|
|
|
commonService.applyCompanyCodeFilter(params);
|
|
List<Map<String, Object>> connections = sqlSession.selectList("externalRestApiConnection.getExternalRestApiConnectionList", params);
|
|
|
|
for (Map<String, Object> conn : connections) {
|
|
parseJsonFields(conn);
|
|
decryptAuthConfig(conn);
|
|
}
|
|
return connections;
|
|
}
|
|
|
|
// ── 상세 조회 ──────────────────────────────────────────────────────────
|
|
|
|
public Map<String, Object> getExternalRestApiConnectionInfo(Map<String, Object> params) {
|
|
commonService.applyCompanyCodeFilter(params);
|
|
Map<String, Object> conn = sqlSession.selectOne("externalRestApiConnection.getExternalRestApiConnectionInfo", params);
|
|
if (conn != null) {
|
|
parseJsonFields(conn);
|
|
decryptAuthConfig(conn);
|
|
}
|
|
return conn;
|
|
}
|
|
|
|
// ── 생성 ──────────────────────────────────────────────────────────────
|
|
|
|
@Transactional
|
|
public Map<String, Object> insertExternalRestApiConnection(Map<String, Object> params) {
|
|
validateConnectionData(params);
|
|
|
|
serializeJsonFields(params);
|
|
encryptAuthConfig(params);
|
|
|
|
if (params.get("timeout") == null) params.put("timeout", 30000);
|
|
if (params.get("retry_count") == null) params.put("retry_count", 0);
|
|
if (params.get("retry_delay") == null) params.put("retry_delay", 1000);
|
|
if (params.get("is_active") == null) params.put("is_active", "Y");
|
|
if (params.get("default_method") == null) params.put("default_method", "GET");
|
|
if (params.get("save_to_history") == null) params.put("save_to_history", "N");
|
|
|
|
sqlSession.insert("externalRestApiConnection.insertExternalRestApiConnection", params);
|
|
log.info("REST API 연결 생성 성공: {}", params.get("connection_name"));
|
|
|
|
return params;
|
|
}
|
|
|
|
// ── 수정 ──────────────────────────────────────────────────────────────
|
|
|
|
@Transactional
|
|
public Map<String, Object> updateExternalRestApiConnection(int id, Map<String, Object> params) {
|
|
params.put("id", id);
|
|
commonService.applyCompanyCodeFilter(params);
|
|
|
|
Map<String, Object> existing = sqlSession.selectOne("externalRestApiConnection.getExternalRestApiConnectionInfo", params);
|
|
if (existing == null) {
|
|
return null;
|
|
}
|
|
|
|
serializeJsonFields(params);
|
|
encryptAuthConfig(params);
|
|
|
|
if (params.containsKey("default_body")) {
|
|
params.put("has_default_body", "Y");
|
|
}
|
|
|
|
sqlSession.update("externalRestApiConnection.updateExternalRestApiConnection", params);
|
|
log.info("REST API 연결 수정 성공: ID {}", id);
|
|
|
|
return sqlSession.selectOne("externalRestApiConnection.getExternalRestApiConnectionInfo", Map.of("id", id));
|
|
}
|
|
|
|
// ── 삭제 ──────────────────────────────────────────────────────────────
|
|
|
|
@Transactional
|
|
public boolean deleteExternalRestApiConnection(int id, Map<String, Object> params) {
|
|
params.put("id", id);
|
|
commonService.applyCompanyCodeFilter(params);
|
|
int deleted = sqlSession.delete("externalRestApiConnection.deleteExternalRestApiConnection", params);
|
|
if (deleted > 0) {
|
|
log.info("REST API 연결 삭제 성공: ID {}", id);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// ── 연결 테스트 (테스트 데이터 기반) ────────────────────────────────────
|
|
|
|
public Map<String, Object> testConnection(Map<String, Object> testRequest, String userCompanyCode) {
|
|
long startTime = System.currentTimeMillis();
|
|
try {
|
|
String baseUrl = (String) testRequest.get("base_url");
|
|
String endpoint = (String) testRequest.get("endpoint");
|
|
String method = (String) testRequest.getOrDefault("method", "GET");
|
|
String authType = (String) testRequest.getOrDefault("auth_type", "none");
|
|
Object authConfigObj = testRequest.get("auth_config");
|
|
Object headersObj = testRequest.get("headers");
|
|
Object bodyObj = testRequest.get("body");
|
|
int timeout = testRequest.get("timeout") != null
|
|
? Integer.parseInt(testRequest.get("timeout").toString()) : 30000;
|
|
|
|
String url = buildUrl(baseUrl, endpoint);
|
|
|
|
HttpHeaders httpHeaders = new HttpHeaders();
|
|
applyCustomHeaders(httpHeaders, headersObj);
|
|
applyAuthHeaders(httpHeaders, authType, authConfigObj, url, userCompanyCode);
|
|
|
|
if (bodyObj != null) {
|
|
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
|
|
}
|
|
|
|
String bodyStr = null;
|
|
if (bodyObj != null) {
|
|
bodyStr = bodyObj instanceof String ? (String) bodyObj : objectMapper.writeValueAsString(bodyObj);
|
|
}
|
|
|
|
HttpMethod httpMethod = HttpMethod.valueOf(method.toUpperCase());
|
|
RequestEntity<String> requestEntity = new RequestEntity<>(
|
|
bodyStr, httpHeaders, httpMethod, URI.create(url));
|
|
|
|
RestTemplate restTemplate = new RestTemplate();
|
|
ResponseEntity<String> response = restTemplate.exchange(requestEntity, String.class);
|
|
|
|
long responseTime = System.currentTimeMillis() - startTime;
|
|
boolean success = response.getStatusCode().is2xxSuccessful();
|
|
|
|
Object responseData = null;
|
|
try {
|
|
responseData = objectMapper.readValue(response.getBody(), Object.class);
|
|
} catch (Exception e) {
|
|
responseData = response.getBody();
|
|
}
|
|
|
|
Map<String, Object> result = new LinkedHashMap<>();
|
|
result.put("success", success);
|
|
result.put("message", success ? "연결 성공" : "연결 실패 (" + response.getStatusCode() + ")");
|
|
result.put("response_time", responseTime);
|
|
result.put("status_code", response.getStatusCode().value());
|
|
result.put("response_data", responseData);
|
|
return result;
|
|
} catch (Exception e) {
|
|
long responseTime = System.currentTimeMillis() - startTime;
|
|
log.error("REST API 연결 테스트 오류:", e);
|
|
Map<String, Object> result = new LinkedHashMap<>();
|
|
result.put("success", false);
|
|
result.put("message", "연결 실패");
|
|
result.put("response_time", responseTime);
|
|
result.put("error_details", e.getMessage());
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// ── 연결 테스트 (ID 기반) ──────────────────────────────────────────────
|
|
|
|
public Map<String, Object> testConnectionById(int id, String endpoint) {
|
|
Map<String, Object> conn = sqlSession.selectOne("externalRestApiConnection.getExternalRestApiConnectionInfo", Map.of("id", id));
|
|
if (conn == null) {
|
|
return Map.of("success", false, "message", "연결을 찾을 수 없습니다.");
|
|
}
|
|
parseJsonFields(conn);
|
|
decryptAuthConfig(conn);
|
|
|
|
String effectiveEndpoint = endpoint != null ? endpoint
|
|
: (String) conn.get("endpoint_path");
|
|
|
|
Map<String, Object> testRequest = new LinkedHashMap<>();
|
|
testRequest.put("base_url", conn.get("base_url"));
|
|
testRequest.put("endpoint", effectiveEndpoint);
|
|
testRequest.put("method", conn.getOrDefault("default_method", "GET"));
|
|
testRequest.put("headers", conn.get("default_headers"));
|
|
testRequest.put("body", conn.get("default_body"));
|
|
testRequest.put("auth_type", conn.get("auth_type"));
|
|
testRequest.put("auth_config", conn.get("auth_config"));
|
|
testRequest.put("timeout", conn.get("timeout"));
|
|
|
|
String companyCode = (String) conn.get("company_code");
|
|
Map<String, Object> result = testConnection(testRequest, companyCode);
|
|
|
|
try {
|
|
Map<String, Object> updateParams = new HashMap<>();
|
|
updateParams.put("id", id);
|
|
updateParams.put("last_test_result", Boolean.TRUE.equals(result.get("success")) ? "Y" : "N");
|
|
updateParams.put("last_test_message", result.get("message"));
|
|
sqlSession.update("externalRestApiConnection.updateExternalRestApiConnectionTestResult", updateParams);
|
|
} catch (Exception e) {
|
|
log.warn("테스트 결과 저장 실패: {}", e.getMessage());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// ── 데이터 조회 (프록시) ────────────────────────────────────────────────
|
|
|
|
public Map<String, Object> fetchData(int connectionId, String endpoint,
|
|
String jsonPath, Map<String, Object> params) {
|
|
commonService.applyCompanyCodeFilter(params);
|
|
params.put("id", connectionId);
|
|
Map<String, Object> conn = sqlSession.selectOne("externalRestApiConnection.getExternalRestApiConnectionInfo", params);
|
|
if (conn == null) {
|
|
return Map.of("success", false, "message", "REST API 연결을 찾을 수 없습니다.");
|
|
}
|
|
parseJsonFields(conn);
|
|
decryptAuthConfig(conn);
|
|
|
|
if (!"Y".equals(conn.get("is_active"))) {
|
|
return Map.of("success", false, "message", "비활성화된 REST API 연결입니다.");
|
|
}
|
|
|
|
String effectiveEndpoint = endpoint != null ? endpoint
|
|
: (String) conn.getOrDefault("endpoint_path", "");
|
|
|
|
Map<String, Object> testRequest = new LinkedHashMap<>();
|
|
testRequest.put("base_url", conn.get("base_url"));
|
|
testRequest.put("endpoint", effectiveEndpoint);
|
|
testRequest.put("method", conn.getOrDefault("default_method", "GET"));
|
|
testRequest.put("headers", conn.get("default_headers"));
|
|
testRequest.put("body", conn.get("default_body"));
|
|
testRequest.put("auth_type", conn.get("auth_type"));
|
|
testRequest.put("auth_config", conn.get("auth_config"));
|
|
testRequest.put("timeout", conn.get("timeout"));
|
|
|
|
String companyCode = (String) conn.get("company_code");
|
|
Map<String, Object> testResult = testConnection(testRequest, companyCode);
|
|
|
|
if (!Boolean.TRUE.equals(testResult.get("success"))) {
|
|
return Map.of("success", false,
|
|
"message", testResult.getOrDefault("message", "REST API 호출에 실패했습니다."));
|
|
}
|
|
|
|
Object responseData = testResult.get("response_data");
|
|
Object extractedData = extractByJsonPath(responseData, jsonPath);
|
|
|
|
List<Object> dataArray;
|
|
if (extractedData == null) {
|
|
dataArray = responseData instanceof List ? (List<Object>) responseData
|
|
: (responseData != null ? List.of(responseData) : List.of());
|
|
} else {
|
|
dataArray = extractedData instanceof List ? (List<Object>) extractedData
|
|
: List.of(extractedData);
|
|
}
|
|
|
|
List<Map<String, Object>> columns = new ArrayList<>();
|
|
if (!dataArray.isEmpty() && dataArray.get(0) instanceof Map) {
|
|
@SuppressWarnings("unchecked")
|
|
Map<String, Object> firstItem = (Map<String, Object>) dataArray.get(0);
|
|
for (Map.Entry<String, Object> entry : firstItem.entrySet()) {
|
|
Map<String, Object> col = new LinkedHashMap<>();
|
|
col.put("column_name", entry.getKey());
|
|
col.put("column_label", entry.getKey());
|
|
col.put("data_type", entry.getValue() != null ? entry.getValue().getClass().getSimpleName() : "String");
|
|
columns.add(col);
|
|
}
|
|
}
|
|
|
|
Map<String, Object> connectionInfo = new LinkedHashMap<>();
|
|
connectionInfo.put("connection_id", conn.get("id"));
|
|
connectionInfo.put("connection_name", conn.get("connection_name"));
|
|
connectionInfo.put("base_url", conn.get("base_url"));
|
|
connectionInfo.put("endpoint", effectiveEndpoint);
|
|
|
|
Map<String, Object> data = new LinkedHashMap<>();
|
|
data.put("rows", dataArray);
|
|
data.put("columns", columns);
|
|
data.put("total", dataArray.size());
|
|
data.put("connection_info", connectionInfo);
|
|
|
|
Map<String, Object> result = new LinkedHashMap<>();
|
|
result.put("success", true);
|
|
result.put("data", data);
|
|
result.put("message", dataArray.size() + "개의 데이터를 조회했습니다.");
|
|
return result;
|
|
}
|
|
|
|
// ── Private helpers ────────────────────────────────────────────────────
|
|
|
|
private String buildUrl(String baseUrl, String endpoint) {
|
|
if (endpoint == null || endpoint.isBlank()) return baseUrl;
|
|
if (endpoint.startsWith("/")) return baseUrl + endpoint;
|
|
return baseUrl + "/" + endpoint;
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
private void applyCustomHeaders(HttpHeaders httpHeaders, Object headersObj) {
|
|
if (headersObj instanceof Map) {
|
|
((Map<String, Object>) headersObj).forEach((k, v) ->
|
|
httpHeaders.set(k, v != null ? v.toString() : ""));
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
private void applyAuthHeaders(HttpHeaders httpHeaders, String authType,
|
|
Object authConfigObj, String url, String companyCode) {
|
|
if (authConfigObj == null || "none".equals(authType)) return;
|
|
|
|
Map<String, Object> authConfig;
|
|
if (authConfigObj instanceof Map) {
|
|
authConfig = (Map<String, Object>) authConfigObj;
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
switch (authType) {
|
|
case "bearer" -> {
|
|
String token = (String) authConfig.get("token");
|
|
if (token != null) httpHeaders.setBearerAuth(token);
|
|
}
|
|
case "basic" -> {
|
|
String username = (String) authConfig.get("username");
|
|
String password = (String) authConfig.get("password");
|
|
if (username != null && password != null) {
|
|
httpHeaders.setBasicAuth(username, password);
|
|
}
|
|
}
|
|
case "api-key" -> {
|
|
String keyLocation = (String) authConfig.getOrDefault("key_location", "header");
|
|
String keyName = (String) authConfig.get("key_name");
|
|
String keyValue = (String) authConfig.get("key_value");
|
|
if ("header".equals(keyLocation) && keyName != null && keyValue != null) {
|
|
httpHeaders.set(keyName, keyValue);
|
|
}
|
|
}
|
|
default -> { }
|
|
}
|
|
}
|
|
|
|
private Object extractByJsonPath(Object data, String jsonPath) {
|
|
if (jsonPath == null || jsonPath.isBlank() || data == null) return data;
|
|
String[] parts = jsonPath.split("\\.");
|
|
Object current = data;
|
|
for (String part : parts) {
|
|
if (current instanceof Map) {
|
|
current = ((Map<?, ?>) current).get(part);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
return current;
|
|
}
|
|
|
|
private void validateConnectionData(Map<String, Object> params) {
|
|
String name = (String) params.get("connection_name");
|
|
String baseUrl = (String) params.get("base_url");
|
|
if (name == null || name.isBlank()) throw new IllegalArgumentException("연결명은 필수입니다.");
|
|
if (baseUrl == null || baseUrl.isBlank()) throw new IllegalArgumentException("기본 URL은 필수입니다.");
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
private void parseJsonFields(Map<String, Object> row) {
|
|
parseJsonField(row, "default_headers");
|
|
parseJsonField(row, "auth_config");
|
|
}
|
|
|
|
private void parseJsonField(Map<String, Object> row, String key) {
|
|
Object val = row.get(key);
|
|
if (val instanceof String str && !str.isBlank()) {
|
|
try {
|
|
row.put(key, objectMapper.readValue(str, Object.class));
|
|
} catch (JsonProcessingException e) {
|
|
log.warn("JSON 파싱 실패 ({}): {}", key, e.getMessage());
|
|
}
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
private void serializeJsonFields(Map<String, Object> params) {
|
|
Object headers = params.get("default_headers");
|
|
if (headers != null && !(headers instanceof String)) {
|
|
try {
|
|
params.put("default_headers", objectMapper.writeValueAsString(headers));
|
|
} catch (JsonProcessingException e) {
|
|
params.put("default_headers", "{}");
|
|
}
|
|
}
|
|
if (params.get("default_headers") == null) {
|
|
params.put("default_headers", "{}");
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
private void encryptAuthConfig(Map<String, Object> params) {
|
|
Object authConfigObj = params.get("auth_config");
|
|
if (authConfigObj == null) return;
|
|
|
|
Map<String, Object> authConfig;
|
|
if (authConfigObj instanceof Map) {
|
|
authConfig = new LinkedHashMap<>((Map<String, Object>) authConfigObj);
|
|
} else if (authConfigObj instanceof String str) {
|
|
try {
|
|
authConfig = objectMapper.readValue(str, Map.class);
|
|
} catch (JsonProcessingException e) {
|
|
return;
|
|
}
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
encryptField(authConfig, "key_value");
|
|
encryptField(authConfig, "token");
|
|
encryptField(authConfig, "password");
|
|
encryptField(authConfig, "client_secret");
|
|
|
|
try {
|
|
params.put("auth_config", objectMapper.writeValueAsString(authConfig));
|
|
} catch (JsonProcessingException e) {
|
|
log.error("authConfig 직렬화 실패", e);
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
private void decryptAuthConfig(Map<String, Object> row) {
|
|
Object authConfigObj = row.get("auth_config");
|
|
if (authConfigObj == null) return;
|
|
|
|
Map<String, Object> authConfig;
|
|
if (authConfigObj instanceof Map) {
|
|
authConfig = new LinkedHashMap<>((Map<String, Object>) authConfigObj);
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
decryptField(authConfig, "key_value");
|
|
decryptField(authConfig, "token");
|
|
decryptField(authConfig, "password");
|
|
decryptField(authConfig, "client_secret");
|
|
row.put("auth_config", authConfig);
|
|
} catch (Exception e) {
|
|
log.warn("민감 정보 복호화 실패 (암호화되지 않은 데이터일 수 있음)");
|
|
}
|
|
}
|
|
|
|
private void encryptField(Map<String, Object> map, String key) {
|
|
Object val = map.get(key);
|
|
if (val instanceof String str && !str.isBlank()) {
|
|
map.put(key, encrypt(str));
|
|
}
|
|
}
|
|
|
|
private void decryptField(Map<String, Object> map, String key) {
|
|
Object val = map.get(key);
|
|
if (val instanceof String str && !str.isBlank()) {
|
|
map.put(key, decrypt(str));
|
|
}
|
|
}
|
|
|
|
private String encrypt(String plainText) {
|
|
try {
|
|
byte[] iv = new byte[IV_LENGTH];
|
|
new SecureRandom().nextBytes(iv);
|
|
byte[] keyBytes = deriveKey();
|
|
|
|
Cipher cipher = Cipher.getInstance(ALGORITHM);
|
|
cipher.init(Cipher.ENCRYPT_MODE,
|
|
new SecretKeySpec(keyBytes, "AES"),
|
|
new GCMParameterSpec(GCM_TAG_LENGTH, iv));
|
|
|
|
byte[] encrypted = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
|
|
return HexFormat.of().formatHex(iv)
|
|
+ ":" + HexFormat.of().formatHex(Arrays.copyOfRange(encrypted, encrypted.length - 16, encrypted.length))
|
|
+ ":" + HexFormat.of().formatHex(Arrays.copyOf(encrypted, encrypted.length - 16));
|
|
} catch (Exception e) {
|
|
log.error("암호화 실패", e);
|
|
return plainText;
|
|
}
|
|
}
|
|
|
|
private String decrypt(String cipherText) {
|
|
String[] parts = cipherText.split(":");
|
|
if (parts.length != 3) return cipherText;
|
|
|
|
try {
|
|
byte[] iv = HexFormat.of().parseHex(parts[0]);
|
|
byte[] authTag = HexFormat.of().parseHex(parts[1]);
|
|
byte[] encryptedText = HexFormat.of().parseHex(parts[2]);
|
|
byte[] keyBytes = deriveKey();
|
|
|
|
byte[] combined = new byte[encryptedText.length + authTag.length];
|
|
System.arraycopy(encryptedText, 0, combined, 0, encryptedText.length);
|
|
System.arraycopy(authTag, 0, combined, encryptedText.length, authTag.length);
|
|
|
|
Cipher cipher = Cipher.getInstance(ALGORITHM);
|
|
cipher.init(Cipher.DECRYPT_MODE,
|
|
new SecretKeySpec(keyBytes, "AES"),
|
|
new GCMParameterSpec(GCM_TAG_LENGTH, iv));
|
|
|
|
return new String(cipher.doFinal(combined), StandardCharsets.UTF_8);
|
|
} catch (Exception e) {
|
|
log.warn("복호화 실패 (평문 데이터 반환)");
|
|
return cipherText;
|
|
}
|
|
}
|
|
|
|
private byte[] deriveKey() throws Exception {
|
|
KeySpec spec = new PBEKeySpec(encryptionKey.toCharArray(), "salt".getBytes(StandardCharsets.UTF_8), 65536, 256);
|
|
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
|
|
return factory.generateSecret(spec).getEncoded();
|
|
}
|
|
}
|