PEM'den Çalışan Stack'e — Bölüm 1: Sertifikam Neden Bir Türlü Uymadı
Dockerize edilmiş bir ERP stack'i için yapılacak hızlı bir sertifika değişimi, tüm bir güne yayılan bir keşif yolculuğuna dönüştü — çünkü "sertifika" aslında birbirinden çok farklı yarım düzine şeyin ortak adı. Bu seri o yolculuğu dört bölümde anlatıyor.
Elimde root.pem olarak bir self-signed root sertifikası vardı. Yeni kurulmuş bir Docker uygulaması (Keycloak'ı auth sunucusu olarak kullanan bir ERP stack'i) ise root.p12 istiyordu. İlk düşüncem: "Bu sadece farklı bir dosya formatı — bir kere dönüştür, iş biter."
Spoiler: iş dört blog yazısına yetti. Bu yolculuğu öğretici kılan tek bir zor komut değil. Asıl fark edilmesi gereken şu: "sertifika" aslında birbirinden farklı davranıp da aynı görünen yarım düzine şeyin şemsiye adı. Bunu içselleştirince bu tür sorunları saatler yerine dakikalar içinde çözüyorsunuz.
Önce: ne nedir?
Herhangi bir şeyi dönüştürmeden önce karşılaşacağımız formatlara kısaca bakmak gerekiyor:
- PEM (
.pem,.crt,.cer): Metin tabanlı bir format.-----BEGIN CERTIFICATE-----satırlarıyla tanınır. Tek bir sertifika, birden fazla sertifika, özel anahtar ya da bunların hepsini bir arada tutabilir. - DER: PEM ile aynı içerik, yalnızca metin yerine ikili (binary) formatta. Bir metin editöründe açılırsa anlamsız karakterler olarak görünür.
- PKCS#12 (
.p12,.pfx): Genellikle bir sertifikayı ve özel anahtarı birlikte paketleyen, parola korumalı bir ikili kap formatı. Windows buna.pfxder, Linux dünyası.p12. Format birebir aynı.
İlk "ah, anladım!" anı şu oldu: Windows'tan gelen bir .pfx dosyası olduğu gibi root.p12 olarak yeniden adlandırılabilir. Dönüştürmeye gerek yok. Bunu baştan bilseydim epey zaman kazanırdım.
Asıl soru: içinde ne var?
Herhangi bir dönüşüme başlamadan önce kaynak dosyanın ne içerdiğini bilmek gerekiyor. Bir truststore (başkalarına güvenmek için) yalnızca public sertifikaya ihtiyaç duyar. Bir sunucu keystore'u (kendini tanıtmak için) buna ek olarak özel anahtarı da gerektirir.
Bir PEM dosyasını hızla kontrol etmek için:
grep -E "BEGIN (CERTIFICATE|PRIVATE KEY|RSA PRIVATE KEY)" meine-datei.pem
Yalnızca CERTIFICATE görünüyorsa içinde anahtar yok demektir. PRIVATE KEY de çıkıyorsa ikisi de mevcut.
Gerektiği gibi dönüştürüp Keycloak'ı başlattım. Gelen yanıt:
ERROR: Failed to initialize truststore: /certificates/root.p12, type: PKCS12 ERROR: toDerInputStream rejects tag type 45
tag type 45 kulağa şifreli geliyor; ama nasıl okunacağını öğrenince harika bir ipucu: 45, - karakterinin ASCII kodu. Java ikili bir PKCS#12 dosyası okumaya çalışıyordu ve bunun yerine metin buldu — -----BEGIN CERTIFICATE-----'nin başlangıcı.
Yani root.p12'm aslında sadece adı .p12 olan bir metin dosyasıydı (PEM). Kontrol yöntemi:
KANIT: 45, - karakterinin ASCII kodu — -----BEGIN CERTIFICATE-----'i başlatan tire.
file root.p12 # says "ASCII text" instead of "data"? -> not real PKCS#12 head -c 60 root.p12 # shows -----BEGIN...? -> it's PEM
HÜKÜM: dosya uzantısı içerik hakkında hiçbir şey söylemez. file ve head en iyi iki dostunuzdur.
İş daha da tuhaflaştı. Başka bir sertifika dosyası şöyle görünüyordu:
-----BEGIN CERTIFICATE----- LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlH...
İlk bakışta normal görünüyor — BEGIN CERTIFICATE, ardından Base64. Ama openssl x509 reddetti:
Could not find certificate ... wrong tag ... nested asn1 error
İpucu ikinci satırda. LS0tLS1CRUdJTiBD... başlı başına yine Base64 — yani -----BEGIN CERTIFICATE-----'nin Base64 kodlanmış hali. Dosya çift kodlanmıştı: biri hazır bir PEM'i alıp başka bir Base64 kodlamasından geçirmiş, ardından etrafına yeni BEGIN/END satırları koymuş.
Bu, düşündüğünüzden daha kolay oluyor; mesela zaten Base64 olan bir şeye kazara certutil -encode uygulandığında böyle bir sonuç çıkıyor.
Onarım — başlık ve alt bilgiyi kaldır, Windows satır sonlarını sil, bir kez Base64 çöz:
grep -v "CERTIFICATE-----" kaputt.crt | tr -d '\r' | base64 -d > sauber.crt openssl x509 -in sauber.crt -noout -subject -issuer
HÜKÜM: son komut düzgün biçimde subject= ve issuer= yazdırıyorsa dosya kurtarılmıştır. İkinci satırdaki LS0tLS1 deseni çift Base64'ün belirgin işaretidir.
Tüm hikâye boyunca tekrar eden bir baş belası: CRLF satır sonları (\r\n). Windows'ta bir dosya düzenlendiğinde ya da kopyalandığında içeri sızıyorlar. Görünür hale getirmek için:
cat -A datei.pem | head -5
KANIT: bir satırın sonunda ^M$ görünüyorsa CRLF var demektir.
Silmek için:
sed -i 's/\r$//' datei.pem
HÜKÜM: bu göze çarpmayan \r baytları bana birçok yerde saatlerce mal oldu — komutları ve parolaları hiç görünmeden sessiz sedasız sabote ediyorlar.
Bölüm 1'den Öğrendiklerim
- Dosya uzantısı ≠ format.
fileveheadile gerçekte ne olduğunu kontrol edin. .pfxile.p12özdeş — çoğu zaman yeniden adlandırmak yeterli.- "tag type" ya da "asn1" içeren Java hataları neredeyse her zaman bir format sorununa işaret eder, içerik sorununa değil.
tag type 45= ikili beklenirken metin geldi (BEGIN'in-'si).- Çift Base64 kodlaması ikinci satırdaki
LS0tLS1deseniyle anlaşılır. - CRLF satır sonları görünmezdir ama ölümcüldür —
cat -Aonları deşifre eder.
Sonraki bölümde bu temiz yapı taşlarından Java'nın gerçekten kabul ettiği bir keystore nasıl oluşturulur, onu inceleyeceğiz — ve "OpenSSL dosyayı açabiliyor" ile "Java da açabiliyor" arasındaki farkın neden bu kadar büyük olduğunu göreceğiz.