Make Install CA work

This commit is contained in:
Simon Kågedal Reimer 2019-05-30 18:35:17 +02:00
parent eb5032ad6f
commit cbf356c1e0
5 changed files with 69 additions and 19 deletions

View File

@ -4,8 +4,8 @@ A description of this package.
## TODO
* `install-ca` command
* `remove-ca` command
* Makefile
* Brew formula
* README
* Filter for os types
* `--dry-run` option
* `remove` command that removes simulators

View File

@ -8,6 +8,7 @@ import SPMUtility
class InstallCACommand: Command {
private struct Options {
var path: String?
var dryRun: Bool = false
}
let name = "install-ca"
let overview = "Install a Certificate Authority"
@ -19,6 +20,14 @@ class InstallCACommand: Command {
private var filteringOptions = FilteringOptions()
func addOptions(to parser: ArgumentParser) {
binder.bind(option: parser.add(
option: "--dry-run",
kind: Bool.self,
usage: "Don't actually install any CA"
), to: { options, dryRun in
options.dryRun = dryRun
})
binder.bind(positional: parser.add(
positional: "path",
kind: String.self,
@ -38,11 +47,16 @@ class InstallCACommand: Command {
func run() throws {
let url = URL(fileURLWithPath: options.path!)
let certificate = try Certificate.load(from: url)
print(certificate)
let certificateName = certificate.subjectSummary ?? "<unknown certificate>"
print(filteringOptions)
for device in try Simctl.flatListDevices().filter(using: filteringOptions) {
print(device.name)
if options.dryRun {
print("Would install \(certificateName) into \(device.name).")
} else {
print("Installing \(certificateName) into \(device.name).")
let trustStore = TrustStore(uuid: device.udid)
try trustStore.open().addCertificate(certificate)
}
}
}
}

View File

@ -26,12 +26,14 @@ struct Certificate {
}
}
let data: Data
private let certificate: SecCertificate
init(_ data: Data) throws {
guard let certificate = SecCertificateCreateWithData(nil, data as CFData) else {
throw Error.invalidDERX509
}
self.data = data
self.certificate = certificate
}
@ -69,7 +71,7 @@ struct Certificate {
}
}
static func load(from url: URL) throws -> SecCertificate {
static func load(from url: URL) throws -> Certificate {
let data = try Data(contentsOf: url)
var cfitems: CFArray?
@ -83,7 +85,8 @@ struct Certificate {
guard type == .itemTypeCertificate, let items = cfitems as? [SecCertificate], let item = items.first else {
throw Error.notACertficate
}
return item
let certData = SecCertificateCopyData(item) as Data
return try Certificate(certData)
}
}

View File

@ -71,8 +71,19 @@ struct TrustStore {
}
func removeCertificate(with sha1: Data) throws {
let query: Delete = tsettings.where(sha1Column == sha1.datatypeValue).delete()
let query = tsettings.where(sha1Column == sha1.datatypeValue).delete()
try connection.run(query)
}
func addCertificate(_ certificate: Certificate) throws {
let row = try TrustStoreRow(certificate)
let insert = tsettings.insert(
subjColumn <- row.subj.datatypeValue,
sha1Column <- row.sha1.datatypeValue,
tsetColumn <- row.tset?.datatypeValue,
dataColumn <- row.data?.datatypeValue
)
try connection.run(insert)
}
}
}

View File

@ -35,6 +35,15 @@ extension TrustStoreRow {
}
}
init(_ certificate: Certificate) throws {
let data = certificate.data
self.data = data
self.sha1 = Digest.sha1(data)
self.tset = createTset()
self.subj = try subjectContent(from: certificate)
assert(isValidTset(self.tset!))
}
func validatedCertificate() throws -> Certificate {
guard let tset = tset else {
throw Error.noTsetValue
@ -43,10 +52,9 @@ extension TrustStoreRow {
throw Error.noData
}
let certificate = try Certificate(data)
let subjectContent = try certificate.normalizedSubjectContent()
let tlv = try DERParser().parse(data: subjectContent)
let subject = try subjectContent(from: certificate)
guard tlv.data == subj else {
guard subject == subj else {
throw Error.subjectContentDoesNotMatch
}
@ -60,13 +68,27 @@ extension TrustStoreRow {
return certificate
}
}
private func isValidTset(_ data: Data) -> Bool {
do {
let plist = try PropertyListSerialization.propertyList(from: data, options: [], format: nil)
return plist is [Any]
} catch {
return false
}
private func subjectContent(from certificate: Certificate) throws -> Data {
let subjectContent = try certificate.normalizedSubjectContent()
let tlv = try DERParser().parse(data: subjectContent)
return tlv.data
}
private func isValidTset(_ data: Data) -> Bool {
do {
let plist = try PropertyListSerialization.propertyList(from: data, options: [], format: nil)
return plist is [Any]
} catch {
return false
}
}
private func createTset() -> Data {
do {
return try PropertyListSerialization.data(fromPropertyList: [], format: .xml, options: 0)
} catch {
fatalError("Should be able to create propertylist")
}
}