|
class KeychainDumper { |
|
class func key(_ key: CFString, class: CFString? = nil) -> String { |
|
if key == kSecAttrType { |
|
if `class` == kSecClassKey || `class` == kSecClassIdentity { |
|
return "KeyType" |
|
} else { |
|
return "Type" |
|
} |
|
} |
|
|
|
if let name = [ |
|
// Item Class Keys and Values |
|
// https://developer.apple.com/documentation/security/keychain_services/keychain_items/item_class_keys_and_values |
|
kSecClass: "Class", |
|
|
|
// Item Attribute Keys and Values |
|
// https://developer.apple.com/documentation/security/keychain_services/keychain_items/item_attribute_keys_and_values |
|
|
|
// General Item Attribute Keys |
|
kSecAttrAccessControl: "AccessControl", |
|
kSecAttrAccessible: "Accessible", |
|
kSecAttrAccessGroup: "AccessGroup", |
|
kSecAttrSynchronizable: "Synchronizable", |
|
kSecAttrCreationDate: "CreationDate", |
|
kSecAttrModificationDate: "ModificationDate", |
|
kSecAttrDescription: "Description", |
|
kSecAttrComment: "Comment", |
|
kSecAttrCreator: "Creator", |
|
kSecAttrLabel: "Label", |
|
kSecAttrIsInvisible: "IsInvisible", |
|
kSecAttrIsNegative: "IsNegative", |
|
kSecAttrSyncViewHint: "SyncViewHint", |
|
kSecAttrPersistentReference: "PersistentReference", |
|
|
|
// Password Attribute Keys |
|
kSecAttrAccount: "Account", |
|
kSecAttrService: "Service", |
|
kSecAttrGeneric: "Generic", |
|
kSecAttrSecurityDomain: "SecurityDomain", |
|
kSecAttrServer: "Server", |
|
kSecAttrProtocol: "Protocol", |
|
kSecAttrAuthenticationType: "AuthenticationType", |
|
kSecAttrPort: "Port", |
|
kSecAttrPath: "Path", |
|
|
|
// Certificate Attribute Keys |
|
kSecAttrSubject: "Subject", |
|
kSecAttrIssuer: "Issuer", |
|
kSecAttrSerialNumber: "SerialNumber", |
|
kSecAttrSubjectKeyID: "SubjectKeyID", |
|
kSecAttrPublicKeyHash: "PublicKeyHash", |
|
kSecAttrCertificateType: "CertificateType", |
|
kSecAttrCertificateEncoding: "CertificateEncoding", |
|
|
|
// Cryptographic Key Attribute Keys |
|
kSecAttrKeyClass: "KeyClass", |
|
kSecAttrApplicationLabel: "ApplicationLabel", |
|
kSecAttrApplicationTag: "ApplicationTag", |
|
kSecAttrKeySizeInBits: "KeySizeInBits", |
|
kSecAttrEffectiveKeySize: "EffectiveKeySize", |
|
kSecAttrTokenID: "TokenID", |
|
kSecAttrIsPermanent: "IsPermanent", |
|
kSecAttrIsSensitive: "IsSensitive", |
|
kSecAttrIsExtractable: "IsExtractable", |
|
kSecAttrCanEncrypt: "CanEncrypt", |
|
kSecAttrCanDecrypt: "CanDecrypt", |
|
kSecAttrCanDerive: "CanDerive", |
|
kSecAttrCanSign: "CanSign", |
|
kSecAttrCanVerify: "CanVerify", |
|
kSecAttrCanWrap: "CanWrap", |
|
kSecAttrCanUnwrap: "CanUnwrap", |
|
|
|
// Search Attribute Keys and Values |
|
// https://developer.apple.com/documentation/security/keychain_services/keychain_items/search_attribute_keys_and_values |
|
|
|
// Item Search Matching Keys |
|
kSecMatchPolicy: "MatchPolicy", |
|
kSecMatchItemList: "MatchItemList", |
|
kSecMatchSearchList: "MatchSearchList", |
|
kSecMatchIssuers: "MatchIssuers", |
|
kSecMatchEmailAddressIfPresent: "MatchEmailAddressIfPresent", |
|
kSecMatchSubjectContains: "MatchSubjectContains", |
|
kSecMatchCaseInsensitive: "MatchCaseInsensitive", |
|
kSecMatchTrustedOnly: "MatchTrustedOnly", |
|
kSecMatchValidOnDate: "MatchValidOnDate", |
|
kSecMatchLimit: "MatchLimit", |
|
|
|
// Additional Item Search Keys |
|
"u_ItemList": "UseItemList", // kSecUseItemList |
|
kSecUseOperationPrompt: "UseOperationPrompt", |
|
"u_NoAuthUI": "UseNoAuthenticationUI", // kSecUseNoAuthenticationUI |
|
kSecUseAuthenticationUI: "UseAuthenticationUI", |
|
kSecUseAuthenticationContext: "UseAuthenticationContext", |
|
kSecUseDataProtectionKeychain: "UseDataProtectionKeychain", |
|
|
|
// Item Return Result Keys |
|
// https://developer.apple.com/documentation/security/keychain_services/keychain_items/item_return_result_keys |
|
|
|
// Item Result Keys |
|
kSecReturnData: "ReturnData", |
|
kSecReturnAttributes: "ReturnAttributes", |
|
kSecReturnRef: "ReturnRef", |
|
kSecReturnPersistentRef: "ReturnPersistentRef", |
|
|
|
// Item Value Type Keys |
|
kSecValueData: "ValueData", |
|
kSecValueRef: "ValueRef", |
|
kSecValuePersistentRef: "ValuePersistentRef" |
|
][key] { |
|
return name |
|
} |
|
|
|
return key as String |
|
} |
|
|
|
class func value(_ value: Any, for key: CFString, class: CFString? = nil) -> Any { |
|
let value = value as CFTypeRef |
|
|
|
if key == kSecClass { |
|
if let value = [ |
|
kSecClassKey: "Key", |
|
kSecClassIdentity: "Identity", |
|
kSecClassCertificate: "Certificate", |
|
kSecClassGenericPassword: "GenericPassword", |
|
kSecClassInternetPassword: "InternetPassword" |
|
][value as! CFString] { |
|
return value |
|
} |
|
} |
|
if key == kSecAttrAccessible { |
|
if let value = [ |
|
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly: "WhenPasscodeSetThisDeviceOnly", |
|
kSecAttrAccessibleWhenUnlockedThisDeviceOnly: "WhenUnlockedThisDeviceOnly", |
|
kSecAttrAccessibleWhenUnlocked: "WhenUnlocked", |
|
kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly: "AfterFirstUnlockThisDeviceOnly", |
|
kSecAttrAccessibleAfterFirstUnlock: "AfterFirstUnlock", |
|
"dku" as CFString: "AlwaysThisDeviceOnly", // kSecAttrAccessibleAlwaysThisDeviceOnly |
|
"dk" as CFString: "Always" // kSecAttrAccessibleAlways |
|
][value as! CFString] { |
|
return value |
|
} |
|
} |
|
if key == kSecAttrKeyClass { |
|
if let value = [ |
|
kSecAttrKeyClassPublic: "Public", |
|
kSecAttrKeyClassPrivate: "Private", |
|
kSecAttrKeyClassSymmetric: "Symmetric" |
|
]["\(value)"] { |
|
return value |
|
} |
|
} |
|
if key == kSecAttrKeyType && `class` == kSecClassKey || `class` == kSecClassIdentity { |
|
if let value = [ |
|
kSecAttrKeyTypeRSA: "RSA", |
|
kSecAttrKeyTypeEC: "EC" |
|
]["\(value)"] { |
|
return value |
|
} |
|
} |
|
|
|
return value |
|
} |
|
|
|
class func attributes(for dict: [CFString: Any]) -> [(key: String, value: Any)] { |
|
var order: [String] = [ |
|
kSecClass, |
|
|
|
// Item Search Matching Keys |
|
kSecMatchPolicy, |
|
kSecMatchItemList, |
|
kSecMatchSearchList, |
|
kSecMatchIssuers, |
|
kSecMatchEmailAddressIfPresent, |
|
kSecMatchSubjectContains, |
|
kSecMatchCaseInsensitive, |
|
kSecMatchTrustedOnly, |
|
kSecMatchValidOnDate, |
|
kSecMatchLimit, |
|
|
|
// Additional Item Search Keys |
|
"u_ItemList" as CFString, // kSecUseItemList |
|
kSecUseOperationPrompt, |
|
"u_NoAuthUI" as CFString, // kSecUseNoAuthenticationUI |
|
kSecUseAuthenticationUI, |
|
kSecUseAuthenticationContext, |
|
kSecUseDataProtectionKeychain, |
|
|
|
// Item Result Keys |
|
kSecReturnData, |
|
kSecReturnAttributes, |
|
kSecReturnRef, |
|
kSecReturnPersistentRef, |
|
|
|
// General Item Attribute Keys |
|
kSecAttrAccessControl, |
|
kSecAttrAccessible, |
|
kSecAttrAccessGroup, |
|
kSecAttrSynchronizable, |
|
kSecAttrCreationDate, |
|
kSecAttrModificationDate, |
|
kSecAttrDescription, |
|
kSecAttrComment, |
|
kSecAttrCreator, |
|
kSecAttrLabel, |
|
kSecAttrIsInvisible, |
|
kSecAttrIsNegative, |
|
kSecAttrSyncViewHint, |
|
kSecAttrPersistentReference, |
|
|
|
// Password Attribute Keys |
|
kSecAttrAccount, |
|
kSecAttrService, |
|
kSecAttrGeneric, |
|
kSecAttrSecurityDomain, |
|
kSecAttrServer, |
|
kSecAttrProtocol, |
|
kSecAttrAuthenticationType, |
|
kSecAttrPort, |
|
kSecAttrPath, |
|
|
|
// Certificate Attribute Keys |
|
kSecAttrSubject, |
|
kSecAttrIssuer, |
|
kSecAttrSerialNumber, |
|
kSecAttrSubjectKeyID, |
|
kSecAttrPublicKeyHash, |
|
kSecAttrCertificateType, |
|
kSecAttrCertificateEncoding, |
|
|
|
// Cryptographic Key Attribute Keys |
|
kSecAttrKeyClass, |
|
kSecAttrApplicationLabel, |
|
kSecAttrApplicationTag, |
|
kSecAttrKeySizeInBits, |
|
kSecAttrEffectiveKeySize, |
|
kSecAttrTokenID, |
|
kSecAttrIsPermanent, |
|
kSecAttrIsSensitive, |
|
kSecAttrIsExtractable, |
|
kSecAttrCanEncrypt, |
|
kSecAttrCanDecrypt, |
|
kSecAttrCanDerive, |
|
kSecAttrCanSign, |
|
kSecAttrCanVerify, |
|
kSecAttrCanWrap, |
|
kSecAttrCanUnwrap, |
|
|
|
// Item Value Type Keys |
|
kSecValueData, |
|
kSecValueRef, |
|
kSecValuePersistentRef |
|
] as [String] |
|
|
|
var `class`: CFString? |
|
if let value = dict[kSecClass] as? String { |
|
`class` = value as CFString |
|
} |
|
|
|
if `class` == kSecClassKey || `class` == kSecClassIdentity { |
|
order.insert(kSecAttrKeyType as String, at: order.firstIndex(of: kSecAttrApplicationTag as String)! + 1) |
|
} else { |
|
order.insert(kSecAttrType as String, at: order.firstIndex(of: kSecAttrCreator as String)! + 1) |
|
} |
|
|
|
var knownPairs: [(String, Any)] = [] |
|
var unknownPairs: [(String, Any)] = [] |
|
for (key, value) in dict { |
|
let key = key as String |
|
if order.contains(key) { |
|
knownPairs.append((key, value)) |
|
} else { |
|
unknownPairs.append((key, value)) |
|
} |
|
} |
|
|
|
knownPairs.sort { order.firstIndex(of: $0.0)! < order.firstIndex(of: $1.0)! } |
|
unknownPairs.sort { $0.0 < $1.0 } |
|
let pairs = (knownPairs + unknownPairs).map { |
|
(key($0.0 as CFString, class: `class`), value($0.1, for: $0.0 as CFString, class: `class`)) |
|
} |
|
|
|
return pairs |
|
} |
|
|
|
class func item(_ item: Any) -> Any { |
|
if let item = item as? [CFString: Any] { |
|
return attributes(for: item) |
|
} |
|
if let items = item as? [Any] { |
|
return items.map { self.item($0) } |
|
} |
|
return item |
|
} |
|
|
|
class func dump(_ item: Any) { |
|
func dump(_ item: Any) { |
|
if let attrs = item as? [(String, Any)] { |
|
for attr in attrs { |
|
print("\(attr.0):", attr.1) |
|
} |
|
print() |
|
} else if let items = item as? [Any] { |
|
for item in items { |
|
dump(item) |
|
} |
|
} else { |
|
print(item) |
|
} |
|
} |
|
|
|
dump(self.item(item)) |
|
} |
|
} |
|
|
|
func dump() { |
|
let query: [CFString: Any] = [ |
|
kSecMatchLimit: kSecMatchLimitAll, |
|
kSecAttrSynchronizable: kSecAttrSynchronizableAny, |
|
kSecReturnAttributes: true, |
|
kSecReturnRef: true, |
|
kSecReturnData: true, |
|
kSecReturnPersistentRef: true |
|
] |
|
|
|
print("KEYCHAIN") |
|
print("========") |
|
print() |
|
|
|
for `class` in [ |
|
kSecClassKey, |
|
kSecClassIdentity, |
|
kSecClassCertificate, |
|
kSecClassGenericPassword, |
|
kSecClassInternetPassword |
|
] { |
|
var query = query |
|
query[kSecClass] = `class` |
|
|
|
let `class` = KeychainDumper.value(`class`, for: kSecClass) as! String |
|
|
|
print(`class`) |
|
print(String(repeating: "-", count: `class`.count)) |
|
|
|
var result: CFTypeRef? |
|
let status = SecItemCopyMatching(query as CFDictionary, &result) |
|
if status != errSecSuccess && status != errSecItemNotFound { |
|
if let message = SecCopyErrorMessageString(status, nil) { |
|
print("Error(\(status)): \(message)") |
|
} else { |
|
print("Error: \(status)") |
|
} |
|
return |
|
} |
|
|
|
if let item = result { |
|
KeychainDumper.dump(item) |
|
} else { |
|
print() |
|
} |
|
} |
|
} |
https://gist.githubusercontent.com/ed3243c920915ecdf7a2bccfb247c8c1