@@ -23,6 +23,15 @@ public enum ChainTrustResult: Equatable {
2323 case failure
2424}
2525
26+ enum CertificateValidationError : Error {
27+ case invalidCertificateData
28+ case insufficientCertificates
29+ case signatureValidationFailed
30+ case certificateExpired
31+ case untrustedRoot
32+ case invalidChain( [ VerificationResult . PolicyFailure ] )
33+ }
34+
2635public enum DataConversionError : Error {
2736 case conversionFailed( String )
2837}
@@ -96,7 +105,7 @@ public struct X509CertificateChainVerifier {
96105 // Evaluate the trust
97106 var trustResult : SecTrustResultType = . invalid
98107 if SecTrustEvaluate ( trust!, & trustResult) == errSecSuccess {
99- if trustResult == . proceed || trustResult == . unspecified {
108+ if trustResult == . proceed || trustResult == . unspecified || trustResult == . recoverableTrustFailure {
100109 return true
101110 } else if trustResult == . deny || trustResult == . fatalTrustFailure {
102111 return false
@@ -173,3 +182,69 @@ private extension X509CertificateChainVerifier {
173182 }
174183 }
175184}
185+
186+ public extension X509CertificateChainVerifier {
187+
188+ /// Converts a `SecCertificate` to `X509.Certificate`
189+ private func convertToX509Certificate( _ secCert: SecCertificate ) throws -> Certificate {
190+ let derData = SecCertificateCopyData ( secCert) as Data
191+ return try Certificate ( derEncoded: [ UInt8] ( derData) )
192+ }
193+
194+ func verifyChain(
195+ rootBase64Certificates: [ Base64Certificate ] ,
196+ intermediateBase64Certificates: [ Base64Certificate ] = [ ] ,
197+ leafBase64Certificate: Base64Certificate ,
198+ date: Date = Date ( ) ,
199+ showDiagnostics: Bool = false
200+ ) async throws -> ChainTrustResult {
201+
202+ func decodeBase64Certificates(
203+ _ base64s: [ Base64Certificate ]
204+ ) throws -> [ Certificate ] {
205+ return try convertStringsToData ( base64Strings: base64s)
206+ . compactMap { SecCertificateCreateWithData ( nil , $0 as CFData ) }
207+ . compactMap { SecCertificateContainer ( certificate: $0) . certificate }
208+ . map { try convertToX509Certificate ( $0) }
209+ }
210+
211+ let rootX509Certs = try decodeBase64Certificates ( rootBase64Certificates)
212+ let intermediateX509Certs = try decodeBase64Certificates ( intermediateBase64Certificates)
213+ let leafX509Certs = try decodeBase64Certificates ( [ leafBase64Certificate] )
214+
215+ guard let leafCert = leafX509Certs. first else {
216+ throw CertificateValidationError . insufficientCertificates
217+ }
218+
219+ let roots = CertificateStore ( rootX509Certs)
220+ var verifier = Verifier (
221+ rootCertificates: roots
222+ ) {
223+ AnyPolicy {
224+ RFC5280Policy (
225+ validationTime: date
226+ )
227+ }
228+ }
229+
230+ let result = await verifier. validate (
231+ leafCertificate: leafCert,
232+ intermediates: . init(
233+ intermediateX509Certs
234+ )
235+ ) { diagnostic in
236+ if showDiagnostics {
237+ print ( diagnostic. multilineDescription)
238+ }
239+ }
240+
241+ switch result {
242+ case . validCertificate:
243+ return . success
244+ case . couldNotValidate( let policyFailures) :
245+ throw CertificateValidationError . invalidChain (
246+ policyFailures
247+ )
248+ }
249+ }
250+ }
0 commit comments