I've retrieved the set of certificates in my keychain using this code:
let query: [String: Any] = [
kSecClass as String: kSecClassCertificate,
kSecMatchLimit as String: kSecMatchLimitAll,
kSecReturnAttributes as String: false,
kSecReturnData as String: true
]
var result: CFTypeRef?
var results : Set<CertsResult> = []
let status = SecItemCopyMatching(query as CFDictionary, &result)
//[Check status]
guard let certificateData = result as? [CFData] else {
//[Handle]
}
From here, I loop through certificateData
and gather information about the certificates, but I need to get the SHA1 hash of the certificates as well. I've gathered from researching that I need to use import CommonCrypto
and CC_SHA1
, but what I've read doesn't use a CFData
.
Is there a good way to get from this point to its SHA1?
You can achieve it by performing the hash yourself. The fingerprints are not part of the certificate itself. More info about that over here.
import CryptoKit
let certificate = ...
let der = SecCertificateCopyData(certificate) as Data
let sha1 = Insecure.SHA1.hash(data: der)
let sha256 = SHA256.hash(data: der)
This can be created in an extension too. I've used CommonCrypto in the extension.
import CommonCrypto
extension SecCertificate {
var sha1: Data {
var digest = [UInt8](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH))
let der = SecCertificateCopyData(self) as Data
_ = CC_SHA1(Array(der), CC_LONG(der.count), &digest)
return Data(digest)
}
var sha256: Data {
var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
let der = SecCertificateCopyData(self) as Data
_ = CC_SHA256(Array(der), CC_LONG(der.count), &digest)
return Data(digest)
}
}
I'd like to mention that SHA-1 hashes of certificates are deprecated since like 2017 and websites and tech giants are starting to drop support for them.
Playground example
import CryptoKit
import Foundation
class CertificateStuff: NSObject, URLSessionDelegate {
func urlSession(
_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
) {
guard let serverTrust = challenge.protectionSpace.serverTrust else {
completionHandler(.rejectProtectionSpace, nil)
return
}
for index in 0 ..< SecTrustGetCertificateCount(serverTrust) {
let certificate = SecTrustGetCertificateAtIndex(serverTrust, index)!
let der = SecCertificateCopyData(certificate)
let sha1 = Insecure.SHA1.hash(data: der as Data)
let sha256 = SHA256.hash(data: der as Data)
print(certificate)
print(sha1)
print(sha256)
print()
}
completionHandler(.performDefaultHandling, nil)
}
func request(_ done: @escaping (Result<Data, Error>) -> Void) {
let url = URL(string: "https://security.stackexchange.com/questions/14330/what-is-the-actual-value-of-a-certificate-fingerprint")!
let request = URLRequest(url: url)
URLSession(configuration: .default, delegate: self, delegateQueue: nil).dataTask(with: request) { (d, r, e) in
if let e = e {
print(e)
return
}
print(d!)
}.resume()
}
}
CertificateStuff().request { result in print(result) }