PEM'den Çalışan Stack'e — Bölüm 2: Java'nın Okumayı Reddettiği Keystore

OpenSSL · keytool · Docker · PKCS#12

PEM'den Çalışan Stack'e — Bölüm 2: Java'nın Okumayı Reddettiği Keystore

Serinin 2. bölümü. 1. bölümde sertifika formatlarını çözdük. Şimdi onlardan PKCS#12 truststore oluşturuyoruz — ve dosya teknik olarak doğru olduğu halde bile iki engelle karşılaşıyoruz.

Your keystore contains 0 entries

Keycloak (Java tabanlı bir kimlik doğrulama sunucusu) bir PKCS#12 truststore istiyordu: servisin güvenmesi gereken public CA sertifikasını tutan bir dosya. Truststore, kasıtlı olarak özel anahtar içermez — yalnızca bir CA'nın public sertifikasını barındırır. Bununla servise şunu söylüyorsunuz: "Bu CA tarafından imzalanan her şeye güvenebilirsin."

Temiz CA sertifikam hazırdı. Akla gelen ilk komut:

openssl pkcs12 -export -nokeys -in ca.crt -out root.p12 -passout pass:<TRUSTSTORE_PW>

-nokeys, çünkü truststore'un anahtara ihtiyacı yok. Çalışması gerekiyordu. Ve çalıştı — görünürde.


CASE #1BOŞ KEYSTORE

Dosya oluşturulmuştu, doğru parolaya sahipti ve OpenSSL ile açılıyordu. Ama Java, truststore'un boş olduğunu bildirdi. Kanıt, Java'nın kendi aracı keytool ile:

keytool -list -keystore root.p12 -storetype PKCS12 -storepass <TRUSTSTORE_PW>
# -> Your keystore contains 0 entries

KANIT: sıfır girdi — OpenSSL dosyayı oluştururken şikâyet etmediği ve incelendiğinde düzgünce bir Certificate bag gösterdiği hâlde.

Nedenin inceliği şu: openssl pkcs12 -export -nokeys ile ilişkili özel anahtarı olmayan salt bir sertifikayı paketlediğinizde, sertifika kaba alias (diğer adıyla "friendlyName") olmadan yerleşir. Java/keytool ise alias'sız girdileri kullanılabilir girdi olarak listelemiyor. Java'nın gözünden bakınca: boş.

Çözüm, truststore'u Java'nın kendi aracıyla oluşturmak — çünkü keytool -importcert tam da bunun için var: güvenilen bir sertifikayı alias ile birlikte içe aktarmak:

keytool -importcert -noprompt \
  -alias corp-ca \
  -file ca.crt \
  -keystore root.p12 \
  -storetype PKCS12 \
  -storepass <TRUSTSTORE_PW>

Artık keytool -list düzgünce 1 entry ve trustedCertEntry gösteriyor. Java sertifikayı görüyor.

HÜKÜM: Java truststore'ları için doğru araç keytool'dur, OpenSSL export değil. OpenSSL'in geçerli saydığı şeyin Java dünyasında kullanılabilir olacağı kesinlikle garanti değildir.

CASE #2"YANLIŞ PAROLA" — DOĞRU PAROLA İLE

Boş keystore sorunu çözülür çözülmez bir sonraki klasik sahneye geçildi:

ERROR: keystore password was incorrect
javax.crypto.BadPaddingException: Given final block not properly padded

Sinsi olan kısım şu: aynı dosyayı OpenSSL ile ve tam bu parola ile sorunsuz açabiliyordum:

openssl pkcs12 -info -in root.p12 -passin pass:<TRUSTSTORE_PW> -noout
# runs through cleanly

KANIT: OpenSSL dosyayı parola ile açarken Java "yanlış parola" diyorsa sorun parola değilşifreleme algoritması.

Arka plan: OpenSSL 3.x, PKCS#12 dosyalarını varsayılan olarak modern PBES2 / AES-256 / SHA-256 ile paketliyor. Birçok container imajındaki JDK 17 gibi eski Java sürümleri ise bu algoritmayı çözerken anlayamıyor — ve yanıltıcı biçimde "parola yanlış" diyor, oysa parola tastamam doğru.

Modern formatı OpenSSL çıktısından anlayabilirsiniz:

openssl pkcs12 -info -in root.p12 -passin pass:<TRUSTSTORE_PW> -noout
# PKCS7 Encrypted data: PBES2, PBKDF2, AES-256-CBC ...   <- Java doesn't like this
# MAC: sha256

Java ise daha eski legacy formatını istiyor:

PKCS7 Encrypted data: pbeWithSHA1And3-KeyTripleDES-CBC ...   <- Java understands this
MAC: sha1

HÜKÜM: OpenSSL dosyayı sorunsuz açarken Java'dan "parola yanlış" geliyorsa bu neredeyse her zaman yanlış PKCS#12 şifreleme algoritması anlamına gelir — yanlış parola değil.

Catch-22 — ve çözümü

Böylece projenin tamamına yayılan bir kısır döngüye girmiştim:

  • OpenSSL export, legacy formatta → Java tarafından okunabilir ama boş keystore (alias yok).
  • keytool import → alias'lı dolu bir keystore, ama Java'nın reddettiği modern AES formatında.

Her ikisini aynı anda istiyordum: alias'lı bir girdi ve legacy format.

Çözüm, oluşturma için keytool kullanmak (alias sorununu çözüyor) ve aynı zamanda JDK sistem özelliklerini kullanarak legacy formatı zorlamaktı:

keytool -importcert -noprompt -alias corp-ca \
  -file ca.crt \
  -keystore root.p12 -storetype PKCS12 -storepass <TRUSTSTORE_PW> \
  -J-Dkeystore.pkcs12.certProtectionAlgorithm=PBEWithSHA1AndDESede \
  -J-Dkeystore.pkcs12.certPbeIterationCount=2048 \
  -J-Dkeystore.pkcs12.macAlgorithm=HmacPBESHA1 \
  -J-Dkeystore.pkcs12.macIterationCount=2048

Dört -J-D seçeneği "eski 3DES/SHA1 formatını kullan" talimatını keytool'u çalıştıran JVM'e iletir. Sonuç: alias'lı, Java'nın okuyabileceği formatta bir girdi. İki koşul tek hamlede karşılandı.

Dosyanın container içinde oluşturulması gerektiğinde

Pratik bir yan engel daha: hangi keytool'u kullandığınız önemsiz değil. En güvenilir yöntem, keystore'u onu daha sonra okuyacak olan Java ile oluşturmak — yani container'ın içinde. Bu şekilde üretilen formatın uyumlu olacağı garanti altına alınmış olur.

Container'daki sertifika dizini genellikle salt okunur (read-only) bağlandığından, dosyayı /tmp'ye oluşturup dışarı akıtmak gerekiyor:

docker compose run --rm -v /host/certs:/out:ro --entrypoint sh keycloak -c '
  keytool -importcert -noprompt -alias corp-ca \
    -file /out/ca.crt \
    -keystore /tmp/root.p12 -storetype PKCS12 -storepass <TRUSTSTORE_PW> \
    -J-Dkeystore.pkcs12.certProtectionAlgorithm=PBEWithSHA1AndDESede \
    -J-Dkeystore.pkcs12.macAlgorithm=HmacPBESHA1 && cat /tmp/root.p12
' > /host/certs/root.p12

Numara şu: keytool /tmp'ye yazar (her zaman yazılabilir), sonucu cat ile standart çıktıya döker, host'taki shell ise bunu > ile hedef dosyaya yönlendirir — yazma izni container'da değil, sizde var.

Bölüm 2'den Öğrendiklerim

  1. Başarılı oluşturmaya rağmen "0 entries" = sertifikanın alias'ı yok. keytool -importcert'i -alias ile kullanın.
  2. Parola doğru olduğu hâlde "parola yanlış" = aslında bir algoritma sorunu. OpenSSL 3 AES-256 yazar, eski Java okuyamaz.
  3. Java legacy formatı ister: pbeWithSHA1And3-KeyTripleDES-CBC / MAC: sha1.
  4. keytool + -J-D seçenekleri tek seferde hem dolu hem legacy üretir.
  5. Doğrulamayı yalnızca OpenSSL ile değil, keytool ile yapın — yalnızca o Java'nın bakış açısını gösterir.
  6. Keystore'ları hedef container içinde oluşturun, böylece format uyumluluğu garanti olur.

Sonraki bölümde iş organizasyonel olarak tehlikeli bir hal alıyor: birden fazla servis aynı dosya için farklı parolalar beklediğinde ne oluyor — ve üç farklı parola değişkenini birbirinden nasıl ayırt edebilirsiniz.

Yorumlar (0)

Henüz yorum yok. İlk yorumu sen yap!

Yorum Yaz