-
-
Save VincentSit/67f234e6b512b114bc7d94dbf47e909b to your computer and use it in GitHub Desktop.
X.509/P12
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
enum X509Error: Error { | |
case certificateError(message: String) | |
case publicKeyError(message: String) | |
} | |
class X509 { | |
// A DER (Distinguished Encoding Rules) representation of an X.509 certificate. | |
let publicKey: SecKey | |
init(fileUrl: URL) throws { | |
guard let certificate = X509.certificate(fileUrl: fileUrl) else { | |
throw X509Error.certificateError(message: "unable to load certificate file \(fileUrl)") | |
} | |
guard let publicKey = X509.publicKey(for: certificate) else { | |
throw X509Error.publicKeyError(message: "unable to load publicKey") | |
} | |
self.publicKey = publicKey | |
} | |
private class func certificate(fileUrl: URL) -> SecCertificate? { | |
guard let certificateData = try? Data(contentsOf: fileUrl) as CFData else { | |
return nil | |
} | |
return SecCertificateCreateWithData(nil, certificateData) | |
} | |
private class func publicKey(for certificate: SecCertificate) -> SecKey? { | |
var publicKey: SecKey? | |
// SecCertificate -> SecTrust | |
let policy = SecPolicyCreateBasicX509() | |
var trust: SecTrust? | |
let trustCreationStatus = SecTrustCreateWithCertificates(certificate, policy, &trust) | |
if let trust = trust, trustCreationStatus == errSecSuccess { | |
publicKey = SecTrustCopyPublicKey(trust) | |
} | |
return publicKey | |
} | |
} | |
enum PKCS12Error: Error { | |
case loadError(message: String) | |
} | |
class PKCS12 { | |
let privateKey: SecKey | |
let publicKey: SecKey? | |
init(p12Url: URL, password: String) throws { | |
let identityAndTrust = try PKCS12.identityAndTrust(p12Url: p12Url, password: password) | |
self.privateKey = try PKCS12.privateKey(for: identityAndTrust.identity) | |
self.publicKey = PKCS12.publicKey(for: identityAndTrust.trust) | |
} | |
class func identityAndTrust(p12Url: URL, password: String) throws -> (identity: SecIdentity, trust: SecTrust) { | |
let data = try Data(contentsOf: p12Url) | |
var importResult: CFArray? = nil | |
let status = SecPKCS12Import( | |
data as NSData, | |
[kSecImportExportPassphrase as String: password] as NSDictionary, | |
&importResult | |
) | |
guard status == errSecSuccess else { | |
throw NSError(domain: NSOSStatusErrorDomain, code: Int(status), userInfo: nil) | |
} | |
guard let identityDictionaries = importResult as? [[String: Any]] else { | |
throw PKCS12Error.loadError(message: "unable to load pkcs12 \(p12Url)") | |
} | |
let identity = identityDictionaries[0][kSecImportItemIdentity as String] | |
as! SecIdentity // swiftlint:disable:this force_cast CoreFoundationのオブジェクトなので as? にしても常に成功する | |
let trust = identityDictionaries[0][kSecImportItemTrust as String] | |
as! SecTrust // swiftlint:disable:this force_cast CoreFoundationのオブジェクトなので as? にしても常に成功する | |
return (identity: identity, trust: trust) | |
} | |
class func privateKey(for identity: SecIdentity) throws -> SecKey { | |
var privateKey: SecKey? | |
let status = SecIdentityCopyPrivateKey(identity, &privateKey) | |
guard status == errSecSuccess else { | |
throw NSError(domain: NSOSStatusErrorDomain, code: Int(status), userInfo: nil) | |
} | |
return privateKey! | |
} | |
private class func publicKey(for trust: SecTrust) -> SecKey? { | |
return SecTrustCopyPublicKey(trust) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment