Elektronik kimlik kartındaki bazı alanları kart erişim cihazı (KEC) ile okuyabilmek için rol doğrulama işleminin yapılması gerekli. Rol doğrulamadan önce ise KEC'in GEM donanımı ile Rol Sahibi (RS) arasında bir güvenli iletişim kanalı kurulmalı. Bahsedilenler, TS 13681 numaralı standartın bir özeti.

Burada 3'lü bir yapı söz konusu:

RS <----> Uygulama <----> GEM

Akış, uygulama tarafından yönetilmekte. RS ve KEC'in işlemleri gerçekleştirebilmesi için bazı metotları sağlaması gerekmekte.

1. Uygulama, getCvc ve getCaCvc metotlarını çağırarak, RS ve KEC'den Güvenli İletişim (Gİ) CV (Card Verifiable) sertifikalarını ve bu sertifikaların alt kök (caCVC) CV sertifikasını ister. Uygulama, VerifyCvcChain metodunu RS üzerinde GEM’den aldığı sertifikalar ile, KEC üzerinde ise RS’den aldığı sertifikalar ile çağırır. Bu metotlar CVC’lerin geçerli bir kökten türediklerini ve tarihlerinin eskimemiş olduğunu denetlemelidir.

2. Uygulama, KEC vasıtasıyla GEM’den, GEM seri numarasını ve 8 byte’lık rastgele sayı (GEM.RND) alır. GEM Seri No ile GEM.RND birleştirilir. RS’deki SignAndEncrypt metoduna gönderilir. ISO9796-2 Scheme 1 imzalama, Sha256 digest ve RSA kullanılarak, implicit trailer ile uygulanmalı.Bu imzalama metodu kurtarılabilir ve imza içine eklenen verinin geri elde edilebilir olduğu bir yöntem. RS, SignAndEncrypt metoduna gelen verinin içinden son 8 byte’ı GEM.RND olarak o güvenli iletişim oturumu için saklamalı. Daha sonra 32 byte uzunluğunda bir kROL rastgele değeri üretmeli ve aynı oturum değerinde saklamalı. kROL değeri imzaya kurtarılabilir veri olarak dahil edilmeli. RSA-2048 sertifikalar 256 byte’lık bir blok genişliğine sahip.  Trailer(2 byte), kROL (32 byte), imzalanacak veri (16 byte) boyutundan geri kalanlar rastgele sayıyla (pRND1) doldurulmalıdır. İmzalama gerçekleştikten sonra Minimum bulma fonksiyonu olarak RS.Gİ sertifikası modulus değeri - imza değeri, imza değerinden daha küçükse, imza değeri olarak o seçilmelidir. Aksi halde imza değerinin kendisi kullanılmalıdır. Daha sonra elde edilen imza değerinin GEM.Gİ sertifikası ile şifrelenmesi gerekir.

3. Uygulama, RS’in SignAndEncrypt metodundan elde ettiği değeri KEC vasıtasıyla GEM’de doğrulamalıdır.

Daha sonra, uygulama aynı işlemlerin GEM üzerinde yapılması için RS’in GetSerialNumber ve GetRandom metotları ile değerleri alır, birleştirir ve GEM üzerinde SignAndEncrpyt metodunu çalıştırır. RS üzerindeki imzalama yöntemini kullanarak veriyi GEM.Gİ sertifikası ile imzalar ve RS.Gİ sertifikası ile şifreler. Standart üzerinde, üretilen imzanın hep implicit trailer (‘6A’…’BC’) sahip olacağı anlaşılmasına rağmen kullandığım test GEM’inin her zaman explicit trailer ile imza ürettiğini farkettim. Bu da, RS tarafında imzayı doğrularken öncelikle imzayı validate edip, içinden trailer’ın ne olduğunu öğrenip daha sonra BouncyCastle’a geçmemi gerektirdi. İki kez imza doğrulamaya ait RSA işlemini yapmamak için önbellekleyen bir cipher yazılması düşünülebilir.

 

Hardware Security Module (HSM) ile çalışırken bazı önemli konular mevcut. Bunlara dikkat edilmediği zaman normal işleyiş hata gibi algılanabiliyor.

PKCS 1.1 library bir kez yüklenmeli. Yani singleton olacak.

Multithread işlem yapılabilir, ancak her thread kendi Session'ını oluşturmalı ve işlem bitince kapatmalı.

Session'a login olunacaksa bir kez olunması yeterli, o session açık olduğu sürece diğer oluşturulan session'lar da logged in kabul edilecektir. Ancak bütün session'lar kapanırsa yeniden login ihtiyacı başlar.

Session'a zaten login yapılmışsa Ckr_User_Already_Logged_In hatası alırsınız. Bu hatayı basitçe yutarak işleme devam edilebilir.

Her seferinde private key handle'ı arayıp bulmaya gerek yok. İlk seferde aldığınız handle'ı saklarsanız diğer thread'lere ait session içinde de kullanabilirsiniz.

Bazen firewall'lar belirli bir süreden sonra HSM ile aranızdaki bağlantıyı kesebiliyor. Bu durumda Token_Not_Present benzeri bir hata alırsanız library'yi yeniden yüklemek zorunda kalabilirsiniz. En iyisi HSM PKCS library'de KeepAlive destekleniyorsa onu kullanmak veya firewall'un hattı kesmesini engellemek.

Secure Enclave Yongası

Apple'ın ürettiği cihazlar epeydir Secure Enclave yongası ile geliyor. Bu yonga kendi OS'ine sahip mini bir bilgisayar neredeyse. Sistemden bağımsız bir yapısı var. Rastgele sayı üretebiliyor ve kripto işlemleri yapabiliyor. Ayrıca, Elliptic Curve özel anahtarları üretip içerisinde güvenli olarak saklıyor.

Bu anahtarları yonga dışına çıkarmak için herhangi bir komut barındırmadığından teorik olarak çok güvenli. Yonga içinde imzalama ve şifre çözme işlemlerini gerçekleştirebiliyorsunuz. Keychain'den daha güvenli bir anahtar barındırma hizmeti sağlıyor. Apple'ın konu ile ilgili şöyle bir makalesi mevcut.

Güvenli bir şekilde EC anahtar üretmek ile başlıyoruz.

        let authContext = LAContext();
        
        guard let accessControl = SecAccessControlCreateWithFlags(
            nil,
            kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
            [.privateKeyUsage,.userPresence],
            nil
            ) else {
                os_log(.error, "Unable to initialize access control flags")
                throw RuntimeError.invalidOperation(message: "Unable to initialize access control flags")
        }
        
        let privateKey = try! SecureEnclave.P256.Signing.PrivateKey(
            compactRepresentable: true,
            accessControl: accessControl,
            authenticationContext: authContext)

 

Anahtarı üretmeden önce, bu anahtarı kullanmak için nasıl bir erişim yöntemi olmasını gerektiğini belirliyoruz. Örnekte, sadece anahtarın üretildiği cihazda ve cihaz kilidi açıkken ayrıca kullanıcı kendi varlığını biyometrisi ile kanıtlamışken erişime izin verdik.

Burada tabii ki özel anahtarın kendisini değil referansını elde ediyoruz. Daha önce de söylediğim gibi özel anahtara ulaşmak mümkün değil.

Bu referansı bir yerde saklamamız lazım ki ihtiyacımız olduğunda kullanabilelim. Örneklerde CryptoKit kullandığımdan IOS 13 ve sonrasında çalışabilir.

        let query = [kSecClass: kSecClassGenericPassword,
                     kSecAttrAccount: "Daha sonra bulmak için buraya bir key yazılmalı",
                     kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
                     kSecUseDataProtectionKeychain: true,
                     kSecValueData: privateKey.dataRepresentation] as [String: Any]
        
        let status = SecItemAdd(query as CFDictionary, nil)
        guard status == errSecSuccess else {
            throw RuntimeError.invalidOperation(message: "Unable to store item: \(status)")
        }

Crpytokit ile keychain üzerinde GenericPassword gibi saklıyoruz. Çünkü sakladığımız referans String tipinde.

Bu referansı sonradan kSecAttrAccount'a yazdığımız değer ile geri çağırabiliriz.

        let query = [kSecClass: kSecClassGenericPassword,
                     kSecAttrAccount: "Daha önce verdiğimiz key değeri",
                     kSecUseDataProtectionKeychain: true,
                     kSecReturnData: true] as [String: Any]
        
        var item: CFTypeRef?
        let status = SecItemCopyMatching(query as CFDictionary, &item)
        switch status {
        case errSecSuccess:
            return item as! Data
            
        case errSecItemNotFound:
            throw RuntimeError.invalidOperation(message: "Key not found in keychain")
            
        default:
            throw RuntimeError.invalidOperation(message: "Unknown Error")
        }

veya kullanmıyorsak silebiliriz.

        let query = [kSecClass: kSecClassGenericPassword,
                     kSecAttrAccount: keyLabel,
                     kSecUseDataProtectionKeychain: true] as CFDictionary
        
        let status = SecItemDelete(query)
        guard status == errSecSuccess else {
            os_log(.error, "Unable to delete key: %d", status)
            return
        }

Data tipinde aldığımız özel anahtar referansı ile Secure Enclave yonga içinde imzalama yaptırabiliriz. Bu çağrı daha önce biyometri ayarladığımız için, parmak izi olan sistemlerde parmak izi doğrulama, Face id olanlarda yüz doğrulama, hiçbirine sahip olmayanlarda da pin ile doğrulama isteyecek.

        let signer = try SecureEnclave.P256.Signing.PrivateKey.init(dataRepresentation: key)
       
        do {
            let signedData = try signer.signature(for: data)
            return signedData.derRepresentation
        } catch {
            throw RuntimeError.invalidOperation(message: "Signing failed: \(error)")
        }

 

 

Elektronik kimlik kartında bulunan Kimlik Doğrulama Uygulaması (0x3D00), PİN ile kimlik doğrulama yapılabilmesini sağlar. Kimlik doğrulama için 32 byte'lık rastgele oluşturulmuş bir veri kullanılmalı. Bu veri kart üzerinde PkcsPss-Sha256 yöntemi ile imzalanır. İmzalanan veri Kimlik Doğrulama X.509 Sertifikası (0x2F10) ile doğrulanır.

Kimlik doğrulama sertifikası, kök ve alt kök makam ile imzalanan bir sertifikadır. http://depo.tckk.gov.tr adresinden bu sertifikalar indirilir ve sertifika doğrulamalarında kullanılır.

Doğrulamada önce kartın kök klasörü seçilir. Sonra Verify (0x20) komutu P1=0x00 ve P2=0x01 olarak hazırlanacak. Data parametresi PİN'in ASCII encoded hali olacak şekilde hazırlanacak ve karta gönderilecek.

Geçersiz PİN gönderilmesi halinde kart'a PİN+PEN şeklinde gönderim yapılmalı.

Daha sonra Kimlik Doğrulama Uygulaması seçilmeli.

PkcsPss-Sha256 imzalama işlemi için kart hazırlanır.

0x22 komutunun P1=0x41, P2=0xB6 olacak ve Data parametresi:

{0x80, 0x01, algorithm, 0x84, 0x01, key}

olacak şekilde hazırlanır. "algorithm" değişkeni PkcsPss-Sha256 için:

0x81 | 16

olmalıdır. "key" değişkeni 0x81 olmalıdır.

Kart üzerinde imzalayı başlatmak için 0x22 komutunun P1=0x9E P2=0x9A olacak ve Data parametresi imzalanacak veri olarak ayarlanacak. Komut başarılı olursa (StatusWord=0x9000) geriye imzalı veri dönecek.

Önceki makale: Elektronik Kimlik Kartı - Bölüm 1