Skip to content

Commit 84f27fa

Browse files
authored
Merge pull request #80 from outfoxx/feature/check_trust
Add `checkTrust` to `SecCertificate` to allow checking trust without returning a public key
2 parents 4d6b38a + ee6d2a6 commit 84f27fa

File tree

2 files changed

+121
-0
lines changed

2 files changed

+121
-0
lines changed

Sources/ShieldSecurity/SecCertificate.swift

+16
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,22 @@ public extension SecCertificate {
100100
}
101101
#endif
102102

103+
func checkTrust(trustedCertificates: [SecCertificate]) throws {
104+
105+
let trust = try createCertificateValidationTrust(anchorCertificates: trustedCertificates)
106+
107+
try evaluateTrust(trust)
108+
}
109+
110+
#if swift(>=5.5)
111+
func checkTrust(trustedCertificates: [SecCertificate]) async throws {
112+
113+
let trust = try createCertificateValidationTrust(anchorCertificates: trustedCertificates)
114+
115+
try await evaluateTrust(trust)
116+
}
117+
#endif
118+
103119
private func createCertificateValidationTrust(anchorCertificates: [SecCertificate]) throws -> SecTrust {
104120

105121
let policy = SecPolicyCreateBasicX509()

Tests/SecCertificateTests.swift

+105
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,56 @@ class SecCertificateTests: XCTestCase {
194194
XCTAssertEqual(certSec.derEncoded, try SecCertificate.load(der: certDer).derEncoded)
195195
}
196196

197+
func testCheckTrust() throws {
198+
199+
let rootName = try NameBuilder().add("Unit Testing Root", forTypeName: "CN").name
200+
let rootID = try Random.generate(count: 10)
201+
let rootSerialNumber = try Certificate.Builder.randomSerialNumber()
202+
let rootKeyHash = try Digester.digest(keyPair.encodedPublicKey(), using: .sha1)
203+
let rootCertData =
204+
try Certificate.Builder()
205+
.serialNumber(rootSerialNumber)
206+
.subject(name: rootName, uniqueID: rootID)
207+
.subjectAlternativeNames(names: .dnsName("io.outfoxx.shield.tests.ca"))
208+
.publicKey(keyPair: keyPair, usage: [.keyCertSign, .cRLSign])
209+
.subjectKeyIdentifier(rootKeyHash)
210+
.issuer(name: rootName)
211+
.issuerAlternativeNames(names: .dnsName("io.outfoxx.shield.tests.ca"))
212+
.basicConstraints(ca: true)
213+
.valid(for: 86400 * 5)
214+
.build(signingKey: keyPair.privateKey, digestAlgorithm: .sha256)
215+
.encoded()
216+
output(rootCertData)
217+
218+
let rootCert = try SecCertificate.from(data: rootCertData)
219+
220+
let certKeyPair = try generateTestKeyPairChecked(type: .ec, keySize: 256, flags: [])
221+
defer { try? certKeyPair.delete() }
222+
223+
let certName = try NameBuilder().add("Unit Testing", forTypeName: "CN").name
224+
let certID = try Random.generate(count: 10)
225+
226+
let certData =
227+
try Certificate.Builder()
228+
.serialNumber(Certificate.Builder.randomSerialNumber())
229+
.subject(name: certName, uniqueID: certID)
230+
.subjectAlternativeNames(names: .dnsName("io.outfoxx.shield.tests.cert"))
231+
.publicKey(keyPair: certKeyPair, usage: [.keyEncipherment, .digitalSignature])
232+
.computeSubjectKeyIdentifier()
233+
.issuer(name: rootName)
234+
.issuerAlternativeNames(names: .dnsName("io.outfoxx.shield.tests.ca"))
235+
.authorityKeyIdentifier(rootKeyHash, certIssuer: [.directoryName(rootName)], certSerialNumber: rootSerialNumber)
236+
.valid(for: 86400 * 5)
237+
.build(signingKey: keyPair.privateKey, digestAlgorithm: .sha256)
238+
.encoded()
239+
output(certData)
240+
241+
242+
let cert = try SecCertificate.from(data: certData)
243+
244+
XCTAssertNoThrow(try cert.checkTrust(trustedCertificates: [rootCert]))
245+
}
246+
197247
func testValidatedPublicKey() throws {
198248

199249
let rootName = try NameBuilder().add("Unit Testing Root", forTypeName: "CN").name
@@ -260,6 +310,61 @@ class SecCertificateTests: XCTestCase {
260310
}
261311

262312
#if swift(>=5.5)
313+
func testCheckTrustAsync() async throws {
314+
315+
let rootName = try NameBuilder().add("Unit Testing Root", forTypeName: "CN").name
316+
let rootID = try Random.generate(count: 10)
317+
let rootSerialNumber = try Certificate.Builder.randomSerialNumber()
318+
let rootKeyHash = try Digester.digest(keyPair.encodedPublicKey(), using: .sha1)
319+
let rootCertData =
320+
try Certificate.Builder()
321+
.serialNumber(rootSerialNumber)
322+
.subject(name: rootName, uniqueID: rootID)
323+
.subjectAlternativeNames(names: .dnsName("io.outfoxx.shield.tests.ca"))
324+
.publicKey(keyPair: keyPair, usage: [.keyCertSign, .cRLSign])
325+
.subjectKeyIdentifier(rootKeyHash)
326+
.issuer(name: rootName)
327+
.issuerAlternativeNames(names: .dnsName("io.outfoxx.shield.tests.ca"))
328+
.basicConstraints(ca: true)
329+
.valid(for: 86400 * 5)
330+
.build(signingKey: keyPair.privateKey, digestAlgorithm: .sha256)
331+
.encoded()
332+
output(rootCertData)
333+
334+
let rootCert = try SecCertificate.from(data: rootCertData)
335+
336+
let certKeyPair = try generateTestKeyPairChecked(type: .ec, keySize: 256, flags: [])
337+
defer { try? certKeyPair.delete() }
338+
339+
let certName = try NameBuilder().add("Unit Testing", forTypeName: "CN").name
340+
let certID = try Random.generate(count: 10)
341+
342+
let certData =
343+
try Certificate.Builder()
344+
.serialNumber(Certificate.Builder.randomSerialNumber())
345+
.subject(name: certName, uniqueID: certID)
346+
.subjectAlternativeNames(names: .dnsName("io.outfoxx.shield.tests.cert"))
347+
.publicKey(keyPair: certKeyPair, usage: [.keyEncipherment, .digitalSignature])
348+
.computeSubjectKeyIdentifier()
349+
.issuer(name: rootName)
350+
.issuerAlternativeNames(names: .dnsName("io.outfoxx.shield.tests.ca"))
351+
.authorityKeyIdentifier(rootKeyHash, certIssuer: [.directoryName(rootName)], certSerialNumber: rootSerialNumber)
352+
.valid(for: 86400 * 5)
353+
.build(signingKey: keyPair.privateKey, digestAlgorithm: .sha256)
354+
.encoded()
355+
output(certData)
356+
357+
358+
let cert = try SecCertificate.from(data: certData)
359+
360+
do {
361+
try await cert.checkTrust(trustedCertificates: [rootCert])
362+
}
363+
catch {
364+
XCTFail("Unexpected error: \(error)")
365+
}
366+
}
367+
263368
func testValidatedPublicKeyAsync() async throws {
264369

265370
let rootName = try NameBuilder().add("Unit Testing Root", forTypeName: "CN").name

0 commit comments

Comments
 (0)