Created
February 16, 2018 06:48
-
-
Save bagpack/1d7f8d1c2c25661c3a128b09efbcb5e8 to your computer and use it in GitHub Desktop.
X.509/P12
This file contains hidden or 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