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-protocolsile 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.
