Java Keystore ile SSL Sertifikası Yönetimi

Java tabanlı uygulamalar geliştirip production’a aldığınızda, SSL/TLS sertifika yönetimi kaçınılmaz olarak karşınıza çıkar. Özellikle microservice mimarilerinde, iç servisler arası güvenli iletişimde ve üçüncü parti API bağlantılarında Java Keystore (JKS) dosyalarıyla uğraşmak zorunda kalırsınız. “Neden bağlanamıyor?” diye saatlerce debug yaptıktan sonra sorunun bir sertifika güven sorunu olduğunu keşfetmek, her Java geliştiricisinin ve sysadmin’in başından geçen klasik bir hikayedir. Bu yazıda Keystore kavramından başlayıp gerçek production senaryolarına kadar her şeyi ele alacağız.

Java Keystore Nedir, Ne İşe Yarar?

Java’nın SSL/TLS altyapısı, sertifikaları ve özel anahtarları depolamak için kendi dosya formatlarını kullanır. Bu noktada iki temel kavram devreye girer: Keystore ve Truststore.

Keystore, uygulamanızın kimliğini kanıtlamak için kullandığı sertifikaları ve özel anahtarları tutar. Yani sunucu tarafında “ben buyum” demenizi sağlar. Truststore ise uygulamanızın güvendiği sertifika otoritelerini (CA) ve sertifikaları içerir. “Sana güveniyorum” demenizi sağlayan bu dosya olmadan, bilinmeyen sertifikalara sahip sunuculara bağlanamazsınız.

Teknik olarak her ikisi de aynı dosya formatlarını kullanabilir; ayrımı mantıksal olarak yaparsınız. JDK ile birlikte gelen keytool aracı bu dosyaları yönetmek için temel komut satırı aracınızdır.

Desteklenen başlıca format tipleri:

  • JKS (Java KeyStore): Java’ya özgü, eski format. Oracle tarafından geliştirildi.
  • PKCS12: Endüstri standardı, taşınabilir format. Java 9 sonrasında varsayılan.
  • JCEKS: JKS’nin geliştirilmiş versiyonu, daha güçlü şifreleme.
  • BKS: Android (BouncyCastle) için kullanılan format.

Java 9’dan itibaren PKCS12 varsayılan format oldu. Eğer hala JKS kullanıyorsanız, artık geçiş yapmanın zamanı gelmiştir.

keytool ile Temel İşlemler

Yeni Keystore ve Self-Signed Sertifika Oluşturma

Geliştirme ortamında hızlıca bir keystore oluşturmak için:

keytool -genkeypair 
  -alias myapp 
  -keyalg RSA 
  -keysize 2048 
  -validity 365 
  -keystore /opt/myapp/keystore.p12 
  -storetype PKCS12 
  -storepass changeit123 
  -keypass changeit123 
  -dname "CN=myapp.example.com, OU=IT, O=MyCompany, L=Istanbul, ST=Istanbul, C=TR"

Parametreler:

  • -alias: Keystore içindeki sertifikanın takma adı, benzersiz olmalı
  • -keyalg: Şifreleme algoritması, RSA veya EC kullanın
  • -keysize: Anahtar uzunluğu, minimum 2048 bit önerilir
  • -validity: Sertifikanın geçerlilik süresi (gün cinsinden)
  • -storetype: Dosya formatı, PKCS12 tercih edin
  • -storepass: Keystore şifresi
  • -dname: Distinguished Name, sertifika sahibi bilgileri

Mevcut Keystore İçeriğini Listeleme

Production’da bir keystore dosyasına baktığınızda içinde ne olduğunu görmek için:

keytool -list 
  -keystore /opt/myapp/keystore.p12 
  -storetype PKCS12 
  -storepass changeit123 
  -v

-v parametresi verbose output verir ve sertifikanın geçerlilik tarihleri, issuer bilgisi, fingerprint gibi detayları gösterir. Bu komutu sertifika expire olmadan önce kontrol için monitoring scriptlerinize eklemenizi şiddetle öneririm.

Sertifika Fingerprint Kontrolü

Güvenlik açısından kritik: production’a deploy ettiğiniz sertifikanın beklediğiniz sertifika olup olmadığını doğrulamak için:

keytool -list 
  -keystore /opt/myapp/keystore.p12 
  -storetype PKCS12 
  -storepass changeit123 
  -alias myapp | grep -A2 "SHA"

CSR Oluşturma ve CA’dan Sertifika Alma

Gerçek production ortamlarında self-signed sertifika kullanmazsınız. Let’s Encrypt, DigiCert, Comodo gibi bir CA’dan imzalı sertifika almanız gerekir. Bunun için önce bir CSR (Certificate Signing Request) oluşturursunuz.

# Önce private key ve keystore oluştur
keytool -genkeypair 
  -alias production-cert 
  -keyalg RSA 
  -keysize 2048 
  -validity 730 
  -keystore /etc/ssl/java/production.p12 
  -storetype PKCS12 
  -storepass "G3rekliSifre#2024" 
  -dname "CN=api.mycompany.com, OU=Backend Team, O=MyCompany Ltd, L=Ankara, ST=Ankara, C=TR"

# CSR oluştur
keytool -certreq 
  -alias production-cert 
  -keystore /etc/ssl/java/production.p12 
  -storetype PKCS12 
  -storepass "G3rekliSifre#2024" 
  -file /tmp/mycompany-api.csr

Bu mycompany-api.csr dosyasını CA’nıza gönderirsiniz. CA size imzalı sertifikayı geri gönderir. Ardından zinciri keystore’a import edersiniz:

# Önce intermediate CA sertifikasını import et
keytool -import 
  -trustcacerts 
  -alias intermediate-ca 
  -file /tmp/intermediate.crt 
  -keystore /etc/ssl/java/production.p12 
  -storetype PKCS12 
  -storepass "G3rekliSifre#2024"

# Sonra imzalanmış sertifikayı import et
keytool -import 
  -alias production-cert 
  -file /tmp/mycompany-api.crt 
  -keystore /etc/ssl/java/production.p12 
  -storetype PKCS12 
  -storepass "G3rekliSifre#2024"

Import sırası önemlidir. Önce CA zincirini, sonra leaf sertifikayı import etmelisiniz. Aksi takdirde “unable to establish chain” hatası alırsınız.

PEM’den PKCS12’ye Dönüşüm

Çoğu CA sertifikaları PEM formatında teslim eder. Let’s Encrypt kullanıyorsanız elinizde cert.pem, chain.pem, fullchain.pem ve privkey.pem dosyaları vardır. Bunları Java’nın anlayacağı formata dönüştürmek için openssl kullanırsınız:

# PEM dosyalarını PKCS12'ye dönüştür
openssl pkcs12 -export 
  -in /etc/letsencrypt/live/myapp.com/fullchain.pem 
  -inkey /etc/letsencrypt/live/myapp.com/privkey.pem 
  -out /opt/myapp/keystore.p12 
  -name myapp 
  -passout pass:changeit123

# Oluşturulan keystore'u doğrula
keytool -list 
  -keystore /opt/myapp/keystore.p12 
  -storetype PKCS12 
  -storepass changeit123 
  -v

Let’s Encrypt sertifikaları 90 günde bir yenilenir. Bu dönüşümü otomatize etmek için bir renewal hook scripti yazmanız gerekir. /etc/letsencrypt/renewal-hooks/deploy/ dizinine koyduğunuz script, sertifika yenilendiğinde otomatik çalışır:

#!/bin/bash
# /etc/letsencrypt/renewal-hooks/deploy/java-keystore-update.sh

DOMAIN="myapp.com"
KEYSTORE_PATH="/opt/myapp/keystore.p12"
KEYSTORE_PASS="changeit123"
ALIAS="myapp"
APP_USER="myapp"

# Eski keystore'u yedekle
cp "$KEYSTORE_PATH" "${KEYSTORE_PATH}.backup.$(date +%Y%m%d)"

# Yeni keystore oluştur
openssl pkcs12 -export 
  -in "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" 
  -inkey "/etc/letsencrypt/live/${DOMAIN}/privkey.pem" 
  -out "$KEYSTORE_PATH" 
  -name "$ALIAS" 
  -passout "pass:${KEYSTORE_PASS}"

# Dosya izinlerini düzelt
chown "${APP_USER}:${APP_USER}" "$KEYSTORE_PATH"
chmod 600 "$KEYSTORE_PATH"

# Uygulamayı yeniden başlat
systemctl restart myapp.service

echo "Keystore güncellendi: $(date)"

Bu scripti çalıştırılabilir yapın: chmod +x /etc/letsencrypt/renewal-hooks/deploy/java-keystore-update.sh

Truststore Yönetimi

Uygulamanız dış servislere bağlanıyorsa ve o servislerin sertifikaları Java’nın varsayılan truststore’unda (cacerts) yoksa, “PKIX path building failed” hatası alırsınız. Bu durumu ele almak için iki yol var.

Yol 1: Java’nın varsayılan cacerts dosyasına sertifika ekle

# Java'nın cacerts dosyasını bul
JAVA_HOME=$(java -XshowSettings:all -version 2>&1 | grep java.home | awk '{print $3}')
CACERTS="${JAVA_HOME}/lib/security/cacerts"

# Dış servisin sertifikasını export et
openssl s_client -connect external-service.com:443 -showcerts </dev/null 2>/dev/null 
  | openssl x509 -outform PEM > /tmp/external-service.crt

# cacerts'e ekle (varsayılan şifre: changeit)
keytool -import 
  -trustcacerts 
  -alias external-service 
  -file /tmp/external-service.crt 
  -keystore "$CACERTS" 
  -storepass changeit 
  -noprompt

Yol 2: Uygulama için özel truststore oluştur

Production’da JDK’yı doğrudan değiştirmek yerine, uygulamaya özel bir truststore kullanmak daha temiz bir yaklaşımdır:

# Özel truststore oluştur
keytool -import 
  -trustcacerts 
  -alias internal-ca 
  -file /etc/ssl/certs/company-internal-ca.crt 
  -keystore /opt/myapp/truststore.p12 
  -storetype PKCS12 
  -storepass trustpass123 
  -noprompt

# Uygulamayı bu truststore ile başlat
java 
  -Djavax.net.ssl.trustStore=/opt/myapp/truststore.p12 
  -Djavax.net.ssl.trustStorePassword=trustpass123 
  -Djavax.net.ssl.trustStoreType=PKCS12 
  -jar /opt/myapp/myapp.jar

JKS’den PKCS12’ye Format Geçişi

Eski bir Java uygulamasını devraldıysanız büyük ihtimalle JKS formatında bir keystore ile karşılaşırsınız. Modern Java sürümleriyle çalışmak ve cross-platform uyumluluğu için PKCS12’ye geçiş yapın:

# JKS'yi PKCS12'ye dönüştür
keytool -importkeystore 
  -srckeystore /opt/legacy-app/old-keystore.jks 
  -srcstoretype JKS 
  -srcstorepass "eski_sifre" 
  -destkeystore /opt/legacy-app/new-keystore.p12 
  -deststoretype PKCS12 
  -deststorepass "yeni_sifre" 
  -noprompt

# Dönüşümü doğrula
keytool -list 
  -keystore /opt/legacy-app/new-keystore.p12 
  -storetype PKCS12 
  -storepass "yeni_sifre"

Spring Boot ile Keystore Konfigürasyonu

Pratikte en çok karşılaşılan senaryo: Spring Boot uygulamasını SSL ile çalıştırmak. application.properties veya application.yml dosyasında keystore ayarlarını tanımlarsınız.

application.yml örneği:

server:
  port: 8443
  ssl:
    enabled: true
    key-store: /opt/myapp/keystore.p12
    key-store-password: ${KEYSTORE_PASSWORD}
    key-store-type: PKCS12
    key-alias: myapp
    trust-store: /opt/myapp/truststore.p12
    trust-store-password: ${TRUSTSTORE_PASSWORD}
    trust-store-type: PKCS12
    protocol: TLS
    enabled-protocols: TLSv1.2,TLSv1.3
    ciphers: >
      TLS_AES_128_GCM_SHA256,
      TLS_AES_256_GCM_SHA384,
      TLS_CHACHA20_POLY1305_SHA256,
      TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256

Şifreleri doğrudan konfig dosyasına yazmayın, environment variable olarak geçirin. Systemd service dosyasında:

# /etc/systemd/system/myapp.service
[Unit]
Description=My Java Application
After=network.target

[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
EnvironmentFile=/etc/myapp/secrets.env
ExecStart=/usr/bin/java -jar /opt/myapp/myapp.jar
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

/etc/myapp/secrets.env dosyası:

KEYSTORE_PASSWORD=G3rekliSifre#2024
TRUSTSTORE_PASSWORD=TrustSifre#2024

Bu dosyanın izinleri mutlaka kısıtlı olmalı: chmod 600 /etc/myapp/secrets.env ve chown myapp:myapp /etc/myapp/secrets.env

Sertifika Expiry Monitoring

Production’da sertifikanın ne zaman expire olacağını izlememek büyük bir hatadır. Basit bir Bash scripti ile bu kontrolü otomatize edebilirsiniz:

#!/bin/bash
# /usr/local/bin/check-keystore-expiry.sh

KEYSTORE="/opt/myapp/keystore.p12"
STOREPASS="changeit123"
WARNING_DAYS=30
CRITICAL_DAYS=7

# Keystore içindeki tüm alias'ları kontrol et
while IFS= read -r alias; do
  expiry_date=$(keytool -list -v 
    -keystore "$KEYSTORE" 
    -storetype PKCS12 
    -storepass "$STOREPASS" 
    -alias "$alias" 2>/dev/null | grep "Valid until" | awk -F': ' '{print $2}')

  if [ -z "$expiry_date" ]; then
    continue
  fi

  expiry_epoch=$(date -d "$expiry_date" +%s 2>/dev/null)
  current_epoch=$(date +%s)
  days_left=$(( (expiry_epoch - current_epoch) / 86400 ))

  if [ "$days_left" -le "$CRITICAL_DAYS" ]; then
    echo "CRITICAL: $alias sertifikası $days_left gün içinde expire olacak!"
    # Buraya mail/slack/pagerduty notification ekleyin
  elif [ "$days_left" -le "$WARNING_DAYS" ]; then
    echo "WARNING: $alias sertifikası $days_left gün içinde expire olacak."
  else
    echo "OK: $alias sertifikası $days_left gün geçerli."
  fi
done < <(keytool -list 
  -keystore "$KEYSTORE" 
  -storetype PKCS12 
  -storepass "$STOREPASS" 2>/dev/null | grep "Alias name" | awk '{print $3}')

Bu scripti crontab’a ekleyin:

0 9 * * * /usr/local/bin/check-keystore-expiry.sh >> /var/log/ssl-expiry.log 2>&1

Yaygın Hatalar ve Çözümleri

Günlük hayatta en çok karşılaştığınız hata mesajları ve çözümleri:

“sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target”

Bu hatanın sebebi truststore’da hedef sunucunun CA sertifikasının bulunmamasıdır. Hedef sunucunun sertifikasını çekip truststore’a ekleyin:

openssl s_client -connect hedef-sunucu.com:443 </dev/null 2>/dev/null 
  | openssl x509 -outform PEM > /tmp/hedef-cert.pem

keytool -import -alias hedef-sunucu 
  -file /tmp/hedef-cert.pem 
  -keystore /opt/myapp/truststore.p12 
  -storetype PKCS12 
  -storepass trustpass123 
  -noprompt

“java.security.UnrecoverableKeyException: Cannot recover key”

Key şifresi ile store şifresi uyuşmuyor. PKCS12 formatında her ikisi de aynı olmalıdır. Sertifikayı yeniden oluşturun ya da dönüştürün.

“keytool error: java.io.IOException: Invalid keystore format”

Dosya bozulmuş ya da format yanlış belirtilmiş. file komutuyla dosya tipini kontrol edin:

file /opt/myapp/keystore.p12
# "data" dönüyorsa muhtemelen PKCS12
# "Java KeyStore" dönüyorsa JKS format

“Alias does not exist”

Alias adı büyük/küçük harf duyarlıdır. -list komutuyla önce mevcut alias’ları listeleyin, sonra doğru adla işlem yapın.

Güvenlik İpuçları

Keystore yönetiminde güvenliği göz ardı etmek ciddi riskler doğurur. Dikkat etmeniz gerekenler:

  • Keystore şifrelerini kod içine veya git repository’sine asla yazmayın. HashiCorp Vault, AWS Secrets Manager veya benzeri bir araç kullanın.
  • Keystore dosyalarının file permission’larını kısıtlayın: chmod 600 keystore.p12, sadece uygulama kullanıcısı okuyabilmeli.
  • Weak cipher suite’leri devre dışı bırakın. TLS 1.0 ve 1.1’i Spring Boot konfigürasyonunda enabled-protocols ile kısıtlayın.
  • Keystore dosyalarını versiyon kontrolüne eklemeyin. .gitignore‘a eklemek yeterli değil, dikkatli olun.
  • Private key’i asla email veya chat üzerinden paylaşmayın. Güvenli bir kanal kullanın.
  • Sertifika yenileme işlemlerini belgelendirin ve ticketing sisteminize kaydedin.

Sonuç

Java Keystore yönetimi başlangıçta karmaşık görünse de temel kavramları ve araçları öğrendikten sonra oldukça sistematik hale geliyor. keytool ve openssl ikilisi ile neredeyse her senaryoyu çözebilirsiniz. Önemli olan birkaç nokta var: Format olarak PKCS12’yi tercih edin, şifreleri environment variable ile yönetin, sertifika sürelerini otomatik izleyin ve renewal scriptlerinizi hazırlayın.

Microservice ortamlarında her servis için ayrı keystore ve truststore yönetmek yorucu olabilir. Bu noktada HashiCorp Vault’un PKI secrets engine’i veya cert-manager gibi araçları araştırmanızı öneririm. Özellikle Kubernetes üzerinde Java servisleri çalıştırıyorsanız, cert-manager sertifika yaşam döngüsünü büyük ölçüde otomatize eder. Ama temeli sağlam atmadan araçlara bağımlı olmak da kendi sorunlarını getiriyor, o yüzden burada anlattıklarını kavramak önemli.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir