一、SecCertificate
A digital certificate is a collection of data used to securely distribute the public half of a public/private key pair.
数字证书
1. 结构
2. 读取和存储
2.1 Identity
var certificate: SecCertificate?
let status = SecIdentityCopyCertificate(identity, &certificate)
guard status == errSecSuccess else { return }
2.2 Der-Encode Data from File
// get data for certificate
let certificate = a certificate
let certData = SecCertificateCopyData(certificate) as Data
// create certificate with data
let certificate = SecCertificateCreateWithData(nil, certData as CFData)
2.3 KeyChain
- add to KeyChain
let addquery: [String: Any] = [
kSecClass as String: kSecClassCertificate,
kSecValueRef as String: certificate,
kSecAttrLabel as String: "My Certificate",
]
var status = SecItemAdd(addquery as CFDictionary, nil)
guard status == errSecSuccess else {
return
}
- read from keyChain
let getquery: [String: Any] = [
kSecClass as String: kSecClassCertificate,
kSecAttrLabel as String: "My Certificate",
kSecReturnRef as String: kCFBooleanTrue,
]
var item: CFTypeRef?
status = SecItemCopyMatching(getquery as CFDictionary, &item)
guard status == errSecSuccess else {
return
}
let certificate = item as! SecCertificate
3. 属性解析
3.1 官方API: SecCertificateCopy系列函数
SecCertificateCopyKey: 获取公钥
SecCertificateCopyData: DER表示
SecCertificateCopyCommonName: 证书主题的公共名称
SecCertificateCopySubjectSummary: 一个简单的字符串,希望代表一个人可以理解的总结。
注: 官方iOS 平台API 只提供了少量的属性访问
3.2 三方参考解析工程: https://github.com/filom/ASN1Decoder
二、SecKey
SecKey即可以是public key 也可以是 private key.存在以下三种编码方式:
For an RSA key, the function returns data in the PKCS #1 format.
For an elliptic curve public key, the format follows the ANSI X9.63 standard using a byte string of 04 || X || Y.
For an elliptic curve private key, the output is formatted as the public key concatenated with the big endian encoding of the secret scalar, or 04 || X || Y || K.
1. 读取和存储
1.1 An identity
- load p12 file
func loadP12File() -> Dictionary<String, Any> {
var url = Bundle(for: P12FileStorageAndParseTests.self).resourceURL!
url.appendPathComponent("fitchAppleAccount.p12")
let data = try! Data(contentsOf: url)
let password = "******"
let options = [kSecImportExportPassphrase as String: password]
var rawItems: CFArray?
// suport DER 和 PEM Encode
let status = SecPKCS12Import(data as CFData,
options as CFDictionary,
&rawItems)
guard status == errSecSuccess else {
fatalError("parse p12 file failure")
}
// p12 文件可以包含多个证书和私钥
let items = rawItems! as! Array<Dictionary<String, Any>>
let firstItem = items[0]
return firstItem
}
- get SecIdentity from p12Attrs
func testParseP12File() {
let p12Attrs = loadP12File()
// parse p12 file
let identity = p12Attrs[kSecImportItemIdentity as String] as! SecIdentity
}
- add to key chain
let persistentRef = addToKeyChain() as! NSData
// 一旦存储成功, 需要将这个唯一标识符进行永久性存储
UserDefaults.standard.set(persistentRef, forKey: "kSecReturnPersistentRef")
func addToKeyChain() -> CFTypeRef? {
let p12Attrs = loadP12File()
// parse p12 file
let identity = p12Attrs[kSecImportItemIdentity as String] as! SecIdentity
var addResult: CFTypeRef?
let addquery: [String: Any] = [
kSecValueRef as String: identity,
kSecReturnPersistentRef as String: true,
]
let status = SecItemAdd(addquery as CFDictionary, &addResult)
guard status == errSecSuccess else {
print("addToKeyChain failure: \(status)")
return nil
}
return addResult
}
- load from key chain
UserDefaults.standard.set(persistentRef, forKey: "kSecReturnPersistentRef")
let identity = queryItem(persistentRef: persistentRef)!
// get private key
var privateKey: SecKey?
var status = SecIdentityCopyPrivateKey(identity, &privateKey)
XCTAssert(status == noErr)
XCTAssert(privateKey != nil)
// get public key
var certificate: SecCertificate?
status = SecIdentityCopyCertificate(identity, &certificate)
XCTAssert(status == noErr)
XCTAssert(certificate != nil)
let publicKey = SecCertificateCopyKey(certificate!)
XCTAssert(publicKey != nil)
func queryItem(persistentRef: NSData) -> SecIdentity? {
let getquery: [String: Any] = [
kSecReturnRef as String: kCFBooleanTrue,
kSecValuePersistentRef as String: persistentRef,
]
var item: CFTypeRef?
let status = SecItemCopyMatching(getquery as CFDictionary, &item)
guard status == errSecSuccess else {
print("queryItem failure \(status)")
return nil
}
return item as! SecIdentity
}
1.2 A trust
// 获取SecTrust 的 leaf cert public key
let publicKey = SecTrustCopyPublicKey(trust)
1.3 Another key.
//通过 privateKey 计算 publicKey
let publicKey = SecKeyCopyPublicKey(privateKey)
1.4 SecCertificate
private static func publicKey(_ certificate: SecCertificate) -> SecKey? {
var publicKey: SecKey?
let policy = SecPolicyCreateBasicX509()
var trust: SecTrust?
let trustCreationStatus = SecTrustCreateWithCertificates(certificate, policy, &trust)
if let trust = trust, trustCreationStatus == errSecSuccess {
publicKey = SecTrustCopyPublicKey(trust)
}
return publicKey
}
1.5 generate Data
- generate Data from SecKey
let key = <# a key #>
var error: Unmanaged<CFError>?
guard let data = SecKeyCopyExternalRepresentation(key, &error) as Data else {
throw error!.takeRetainedValue() as Error
}
- generate SecKey from Data
let options: [String: Any] = [kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
kSecAttrKeySizeInBits as String : 2048]
var error: Unmanaged<CFError>?
guard let key = SecKeyCreateWithData(data as CFData,
options as CFDictionary,
&error) else {
throw error!.takeRetainedValue() as Error
}
注意
使用苹果官方API生成的PublicData和使用OpenSSL 生成的PublicData 存在一些差异,苹果的缺少头部信息, OpenSSL拥有头部信息
解决方案:
对苹果API生成的PublicKeyData拼接额外的头部信息
对OpenSSL生成的PublicKeyData,去除头部信息
三、SecPolicy
证书评估策略
func SecPolicyCreateBasicX509() -> SecPolicy
,通常用于创建 SecTrustfunc SecPolicyCreateRevocation(_ revocationFlags: CFOptionFlags) -> SecPolicy?
func SecPolicyCreateSSL(_ server: Bool, _ hostname: CFString?) -> SecPolicy
, 用于SSL 证书认证
四、SecTrust
用于证书评估
1. 创建
let policy = SecPolicyCreateBasicX509()
var optionalTrust: SecTrust?
var status = SecTrustCreateWithCertificates(certArray as AnyObject,
policy,
&optionalTrust)
2. 证书评估以及解析结果
if status == errSecSuccess {
let trust = optionalTrust! // Safe to force unwrap now
SecTrustEvaluateAsync(trust, DispatchQueue.global()) {
_, trustResult in
switch trustResult {
case .proceed, .unspecified:
let publicKey = SecTrustCopyPublicKey(trust)
// Use key . . .
case .recoverableTrustFailure:
print("Trust failed recoverably")
default:
print("Trust failed absolutely")
}
}
}