KamuSMXmlDepoOfflineResolver.java
package io.mersel.dss.signer.api.services;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import eu.europa.esig.dss.model.x509.CertificateToken;
/**
* KamuSM XML Deposu Offline Resolver
* Yerel dosya sisteminden KamuSM sertifika deposunu okur ve yönetir.
* Offline ortamlarda kullanım için tasarlanmıştır.
*/
@Service("kamuSMXmlDepoOfflineResolver")
public class KamuSMXmlDepoOfflineResolver extends AbstractKamuSMXmlDepoResolver {
private final ResourceLoader resourceLoader;
private final String xmlFilePath;
public KamuSMXmlDepoOfflineResolver(ResourceLoader resourceLoader,
@Value("${kamusm.root.offline.path:}") String xmlFilePath) {
this.resourceLoader = resourceLoader;
// Path'teki baştaki ve sondaki tırnakları temizle (Spring properties'te çift tırnak kullanımı için)
if (xmlFilePath != null) {
xmlFilePath = xmlFilePath.trim();
// Çift tırnak veya tek tırnak ile başlayıp bitiyorsa kaldır
if ((xmlFilePath.startsWith("\"") && xmlFilePath.endsWith("\"")) ||
(xmlFilePath.startsWith("'") && xmlFilePath.endsWith("'"))) {
xmlFilePath = xmlFilePath.substring(1, xmlFilePath.length() - 1);
}
// Encoding sorununu çöz: Eğer path ISO-8859-1 olarak yanlış okunduysa UTF-8'e çevir
try {
// ISO-8859-1 olarak yanlış okunmuş gibi görünen karakterleri UTF-8'e çevir
// Örnek: "Ãn" -> "Ön", "Hazırlık" -> "Hazırlık"
if (xmlFilePath.contains("Ã") || xmlFilePath.contains("Ä")) {
byte[] bytes = xmlFilePath.getBytes("ISO-8859-1");
String correctedPath = new String(bytes, "UTF-8");
// Eğer düzeltilmiş path Türkçe karakterler içeriyorsa kullan
if (correctedPath.contains("Ö") || correctedPath.contains("ö") ||
correctedPath.contains("ı") || correctedPath.contains("İ") ||
correctedPath.contains("ş") || correctedPath.contains("Ş") ||
correctedPath.contains("ğ") || correctedPath.contains("Ğ") ||
correctedPath.contains("ü") || correctedPath.contains("Ü") ||
correctedPath.contains("ç") || correctedPath.contains("Ç")) {
xmlFilePath = correctedPath;
logger.debug("Path encoding düzeltildi: {}", xmlFilePath);
}
}
} catch (Exception e) {
logger.debug("Path encoding düzeltme hatası: {}", e.getMessage());
}
}
this.xmlFilePath = xmlFilePath;
}
@Override
public void refreshTrustedRoots() {
// Mevcut sertifikaları sakla (başarısız olursa geri yüklemek için)
List<X509Certificate> previousRoots = new ArrayList<>(trustedRoots.get());
List<CertificateToken> previousTokens = new ArrayList<>(trustedRootTokens.get());
try {
if (xmlFilePath == null || xmlFilePath.trim().isEmpty()) {
logger.warn("Offline KamuSM XML dosya yolu belirtilmemiş. Sertifika yüklenemiyor.");
return;
}
logger.info("KamuSM XML deposu offline olarak yukleniyor: {}", xmlFilePath);
String xmlBody = loadRepositoryXml();
if (xmlBody == null || xmlBody.trim().isEmpty()) {
logger.warn("KamuSM kok sertifika verisi bos - mevcut liste korunuyor");
return;
}
List<X509Certificate> certificates = parseCertificates(xmlBody);
if (certificates.isEmpty()) {
logger.warn("KamuSM kok sertifika listesi bos - mevcut liste korunuyor");
return;
}
List<CertificateToken> tokens = new ArrayList<CertificateToken>(certificates.size());
for (X509Certificate certificate : certificates) {
tokens.add(new CertificateToken(certificate));
}
trustedRoots.set(Collections.unmodifiableList(certificates));
trustedRootTokens.set(Collections.unmodifiableList(tokens));
logger.info("KamuSM kok sertifikalari basariyla yuklendi ({} adet)", certificates.size());
// Trusted certificate source'u da guncelle
updateTrustedCertificateSource();
} catch (Exception ex) {
logger.error("KamuSM kok sertifikalarini yukleme basarisiz: {} - mevcut liste korunuyor", ex.getMessage(), ex);
// Başarısız olursa önceki sertifikaları geri yükle
if (!previousRoots.isEmpty()) {
trustedRoots.set(Collections.unmodifiableList(previousRoots));
trustedRootTokens.set(Collections.unmodifiableList(previousTokens));
logger.info("Onceki sertifikalar geri yuklendi ({} adet)", previousRoots.size());
}
}
}
@Override
protected String loadRepositoryXml() throws Exception {
// Direkt dosya sistemi yolu kontrolü
// Desteklenen formatlar:
// - file:/path/to/file.xml (Unix/Linux/Mac)
// - file:/C:/path/to/file.xml (Windows)
// - /absolute/path/to/file.xml (Unix/Linux/Mac absolute path)
// - C:\path\to\file.xml (Windows absolute path)
// - C:/path/to/file.xml (Windows absolute path with forward slash)
boolean isDirectFileSystemPath = xmlFilePath.startsWith("file:") ||
xmlFilePath.startsWith("/") ||
(xmlFilePath.length() >= 2 && xmlFilePath.charAt(1) == ':' &&
(xmlFilePath.charAt(2) == '\\' || xmlFilePath.charAt(2) == '/')) ||
(!xmlFilePath.startsWith("classpath:") && !xmlFilePath.startsWith("http"));
if (isDirectFileSystemPath) {
String filePath = xmlFilePath.startsWith("file:") ? xmlFilePath.substring(5) : xmlFilePath;
// Windows path'lerinde file: prefix'i sonrası /C:/ gibi olabilir, bunu düzelt
if (filePath.startsWith("/") && filePath.length() > 3 && filePath.charAt(2) == ':') {
filePath = filePath.substring(1); // Baştaki /'yi kaldır (file:/C:/ -> C:/)
}
File file = new File(filePath);
if (!file.exists()) {
throw new IllegalStateException("KamuSM XML dosyasi bulunamadi: " + filePath);
}
if (!file.isFile()) {
throw new IllegalStateException("Belirtilen yol bir dosya degil: " + filePath);
}
try (FileInputStream fis = new FileInputStream(file)) {
byte[] bytes = IOUtils.toByteArray(fis);
return new String(bytes, StandardCharsets.UTF_8);
}
}
// Classpath veya diğer Spring Resource formatları için
Resource resource = resourceLoader.getResource(xmlFilePath);
if (!resource.exists()) {
throw new IllegalStateException("KamuSM XML dosyasi bulunamadi: " + xmlFilePath);
}
try (InputStream inputStream = resource.getInputStream()) {
byte[] bytes = IOUtils.toByteArray(inputStream);
return new String(bytes, StandardCharsets.UTF_8);
}
}
}