Перейти к содержанию

Хранилище ключей с приватными ключами, защищёнными слабым паролем

Критичность: ВЫСОКИЙ
Способ обнаружения: DAST, КЛЮЧЕВАЯ ИНФОРМАЦИЯ

Описание

Приложение использует доступное хранилище ключей с приватными ключами, защищёнными слабым паролем. Это может привести к подмене ключевой информации. Ключ шифрования не должен храниться в общедоступном месте.

При использовании криптографических операций на устройстве необходимо обеспечить максимальную безопасность основного секрета в таких операциях — ключа шифрования. При использовании ассиметричного шифрования — необходимо сохранить в секрете приватный ключ, в то время как в случае использования симметричных алгоритмов следует защищать ключ, который используется и для шифрования, и для расшифрования sensitive-информации.

Наиболее безопасным вариантом, безусловно является хранение ключей в Keychain.

Компрометация ключевой информации, которая используется в приложении, может привести к катастрофическим последствиям, в зависимости от использования данной информации в приложении, начиная от расшифровки файлов, трафика, заканчивая компрометацией закрытого ключа, использующегося для подписи приложения.

Рекомендации

Создание нового ключа в Keychain

import Foundation
import Security
func computeSymmetricKey() -> String? {
    var keyData = Data(count: 32) // 32 bytes === 256 bits
    let result = keyData.withUnsafeMutableBytes {
        (mutableBytes: UnsafeMutablePointer) -> Int32 in
        SecRandomCopyBytes(kSecRandomDefault, keyData.count, mutableBytes)
    }
    if result == errSecSuccess {
        return keyData.base64EncodedString()
    } else {
        return nil
    }
}
let secretKey = computeSymmetricKey()

Сохранение нового ключа в Keychain

enum KeychainErrors:Error {
    case COULDNOTINSERT
    case COULDNOTREAD
}
func store (key: String, withTag: String) throws {
    let fromKey = key.data(using: .utf8)!

    let query: [String: Any] = [
        kSecClass as String: kSecClassKey,
        kSecAttrApplicationTag as String: withTag,
        kSecValueRef as String: fromKey
    ]

    let status = SecItemAdd(query as CFDictionary, nil)
    guard status == errSecSuccess else { throw KeychainErrors.COULDNOTINSERT }
}
do {
    try store(key: secretKey!, withTag: "com.myapp.keys.localStore")
} catch {
    print(error)
}

Чтение ключа из Keychain

func read (tag: String) throws -> CFTypeRef {
    let readQuery: [String: Any] = [
        kSecClass as String: kSecClassKey,
        kSecAttrApplicationTag as String: tag,
        kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
        kSecReturnRef as String: true
    ]

    var item: CFTypeRef?
    let readStatus = SecItemCopyMatching(readQuery as CFDictionary, &item)
    guard readStatus == errSecSuccess else { throw KeychainErrors.COULDNOTREAD }

    return item!
}
do {
    let keyReadFromKeychain = try read(tag: "com.myapp.keys.localStore")
    print(keyReadFromKeychain)
} catch {
    print(error)
}

Применение ключа для шифрования и расшифровки

let algorithm: SecKeyAlgorithm = .rsaEncryptionOAEPSHA256
let plainText = "this is our golden secret. Encrypt it!"
var error: Unmanaged?
guard let cipherText = SecKeyCreateEncryptedData(secretKey as! SecKey, algorithm,
    plainText as! CFData, &error) as Data? else {
        throw error!.takeRetainedValue() as Error
    }

Отображение сертификатов

func showCertificateInfo() -> String {
    var resultString = "--- Certificates in Keychain ---\n"
    var outputACert = false
    let query = [kSecMatchLimit: kSecMatchLimitAll,
                kSecReturnRef: true,
                kSecClass: kSecClassCertificate] as CFDictionary
    var result: CFTypeRef?
    let resultCode = SecItemCopyMatching(query, &result)
    if resultCode == errSecSuccess {
        if CFArrayGetTypeID() == CFGetTypeID(result) {
            let array = (result as? NSArray) as? [SecCertificate]
            array?.forEach { (item) in
                resultString += self.displayCertificate(item)
                outputACert = true
            }
        } else {
            // swiftlint:disable force_cast
            resultString += self.displayCertificate(result as! SecCertificate)
            // swiftlint:enable force_cast
            outputACert = true
        }
    }
    if !outputACert {
        resultString += "None\n"
    }
    resultString += "-------------------------------"
    return resultString
}

Ссылки

  1. https://developer.apple.com/
  2. CertificateSDK
  3. Certificates
  4. Keychain Services
  5. Basic iOS Security: Keychain and Hashing
К началу