Skip to content

Instantly share code, notes, and snippets.

@VincentSit
Forked from bagpack/X509.swift
Created June 8, 2019 14:15
Show Gist options
  • Save VincentSit/67f234e6b512b114bc7d94dbf47e909b to your computer and use it in GitHub Desktop.
Save VincentSit/67f234e6b512b114bc7d94dbf47e909b to your computer and use it in GitHub Desktop.
X.509/P12
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