From 17a05856ef35530fa8bb31b5a7de8c3e08c6e33e Mon Sep 17 00:00:00 2001 From: Kevin Kieffer Date: Wed, 18 Dec 2019 10:51:42 -0500 Subject: [PATCH] Relaxing the validation of server certificates against embedded self-signed (pinned) certificates using the x509 policy. The SSL policy adds hostname validation, and on ios13 additional restrictions in valid dates which aren't applicable when the client already has the server certificate. This modification allows for IP hostnames and self-signed certificates with longer validity before expiration. Also update the README to include using embedded self-signed certs. --- README.md | 4 ++-- Sources/SSLService/SSLService.swift | 11 ++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8987dcb..cdd3fd4 100644 --- a/README.md +++ b/README.md @@ -113,9 +113,9 @@ Both clients and server require at a minimum the following configuration items: * No certificate at all. -**BlueSSLService** provides five ways to create a `Configuration` supporting the scenarios above. Only the **last version is supported on Apple platforms.** On Linux, **ALL** versions are supported. This is due to the limits imposed on the current implementation of *Apple Secure Transport*. +**BlueSSLService** provides five ways to create a `Configuration` supporting the scenarios above. Only the **first and last version is supported on Apple platforms.** On Linux, **ALL** versions are supported. This is due to the limits imposed on the current implementation of *Apple Secure Transport*. - `init()` - This API allows for the creation of *default* configuration. This is equivalent to calling the next initializer without changing any parameters. -- `init(withCipherSuite cipherSuite: String? = nil, clientAllowsSelfSignedCertificates: Bool = true)` - This API allows for the creation of configuration that does not contain a backing certificate or certificate chain. You can optionally provide a *cipherSuite* and decide whether to allow, when in client mode, use of *self-signed certificates* by the server. +- `init(withCipherSuite cipherSuite: String? = nil, clientAllowsSelfSignedCertificates: Bool = true, embeddedServerCertPaths: [URL]? = nil)` - This API allows for the creation of configuration that does not contain a backing certificate or certificate chain. You can optionally provide a *cipherSuite* and decide whether to allow, when in client mode, use of *self-signed certificates* by the server. If *clientAllowsSelfSignedCertificates* is true, you can optionally provide an array of paths to .der encoded x509 certificates to validate against (certificate pinning). **NOTE: At present, validating against pinned certificates is only available on Apple platforms: MacOS versions >= 10.14, iOS >= 12.0. For other platforms, the *embeddedServerCertPaths* parameter is ignored.** - `init(withCACertificatePath caCertificateFilePath: String?, usingCertificateFile certificateFilePath: String?, withKeyFile keyFilePath: String? = nil, usingSelfSignedCerts selfSigned: Bool = true, cipherSuite: String? = nil)` - This API allows you to create a configuration using a self contained `Certificate Authority (CA)` file. The second parameter is the path to the `Certificate` file to be used by application to establish the connection. The next parameter is the path to the `Private Key` file used by application corresponding to the `Public Key` in the `Certificate`. If you're using `self-signed certificates`, set the last parameter to true. - `init(withCACertificateDirectory caCertificateDirPath: String?, usingCertificateFile certificateFilePath: String?, withKeyFile keyFilePath: String? = nil, usingSelfSignedCerts selfSigned: Bool = true, cipherSuite: String? = nil)` - This API allows you to create a configuration using a directory of `Certificate Authority (CA)` files. These `CA` certificates **must** be hashed using the `Certificate Tool` provided by `OpenSSL`. The following parameters are identical to the previous API. - `init(withPEMCertificateString certificateString: String, usingSelfSignedCerts selfSigned: Bool = true, cipherSuite: String? = nil)` - This API used when supplying a PEM formatted certificate presented as a *String*. **NOTE: At present, this API is only available on Linux.** diff --git a/Sources/SSLService/SSLService.swift b/Sources/SSLService/SSLService.swift index de40806..71a575e 100644 --- a/Sources/SSLService/SSLService.swift +++ b/Sources/SSLService/SSLService.swift @@ -191,7 +191,7 @@ public class SSLService: SSLServiceDelegate { /// `true` to accept self-signed certificates from a server. `false` otherwise. /// **Note:** This parameter is only used when `SSLService` is used with a client socket. /// - embeddedServerCertPath: when client allows self-signed certificates from a server, verify to server certificate - /// against one of the locally embedded certificates. Pass `nil` to skip the check. + /// against one of the locally .der encoded x509 embedded certificates. Pass `nil` to skip the check. /// **Note:** This parameter is only used when `SSLService` is used with a client socket. /// **Note:** This parameter is only available on Apple platforms /// **Note:** This feature unavailable (parameter ignored) on MacOS versions less than 10.14, iOS < 12.0 @@ -1247,9 +1247,18 @@ public class SSLService: SSLServiceDelegate { #if swift(>=4.2) if #available(macOS 10.14, iOS 12.0, tvOS 12.0, *) { + + //create a basic X509 policy to validate against instead of the more stringent + //SSL policy. The SSL policy validates the hostname and may have other restrictions + //not needed when using pinned self-signed certificates + let policy = SecPolicyCreateBasicX509() + // Verify against a local embedded certificate... if let trust = self.trust, let paths = configuration.embeddedServerCertPaths { + SecTrustSetPolicies(trust, policy) + + // Load all the certs from files var certs : [SecCertificate] = [] for path in paths {