Skip to content

Commit 5108dbd

Browse files
authored
Deprecate NIOSSLCertificate(file:format:) (#550)
Motivation: NIOSSLCertificate has an 'init' which loads a single cert from PEM or DER encoded file. This, obviously, only loads the first PEM which is very rarely what users actually want to do. This is a common source of error when loading cert chains and is easy to miss in review. Modifications: - Deprecate the init and point users to the `fromPEMBytes` method for PEM which returns an array of certs, and to a new `fromDERBytes` which returns a single cert. Result: Harder to misuse API
1 parent f64263a commit 5108dbd

File tree

4 files changed

+46
-17
lines changed

4 files changed

+46
-17
lines changed

Sources/NIOSSL/SSLCertificate.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,33 @@ public final class NIOSSLCertificate {
8080
///
8181
/// Note that this method will only ever load the first certificate from a given file.
8282
///
83+
/// If you want to load certificates from a PEM file use ``fromPEMFile(_:)``. To load
84+
/// a certificate from a DER file use ``fromDERFile(_:)``.
85+
///
8386
/// - parameters:
8487
/// - file: The path to the file to load the certificate from.
8588
/// - format: The format to use to parse the file.
89+
@available(
90+
*,
91+
deprecated,
92+
message: """
93+
Use 'fromPEMFile(_:)' to load all certificates from a PEM file or 'fromDERFile(_:)' \
94+
to load a single certificate from a DER file.
95+
"""
96+
)
8697
public convenience init(file: String, format: NIOSSLSerializationFormats) throws {
98+
try self.init(_file: file, format: format)
99+
}
100+
101+
/// Create a ``NIOSSLCertificate`` from a file at a given path in either PEM or
102+
/// DER format.
103+
///
104+
/// Note that this method will only ever load the first certificate from a given file.
105+
///
106+
/// - parameters:
107+
/// - file: The path to the file to load the certificate from.
108+
/// - format: The format to use to parse the file.
109+
private convenience init(_file file: String, format: NIOSSLSerializationFormats) throws {
87110
let fileObject = try Posix.fopen(file: file, mode: "rb")
88111
defer {
89112
fclose(fileObject)
@@ -335,6 +358,14 @@ extension NIOSSLCertificate {
335358
return try readCertificatesFromBIO(bio)
336359
}
337360

361+
/// Create a ``NIOSSLCertificate`` from a DER file at a given path.
362+
///
363+
/// - parameters:
364+
/// - path: The path to the file to load the certificate from.
365+
public static func fromDERFile(_ path: String) throws -> NIOSSLCertificate {
366+
try NIOSSLCertificate(_file: path, format: .der)
367+
}
368+
338369
/// Returns the timestamp before which this certificate is not valid.
339370
///
340371
/// The value is in seconds since the UNIX epoch.

Sources/NIOSSL/SSLContext.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -688,8 +688,9 @@ extension NIOSSLContext {
688688
// Load only the certificates that resolve to an existing certificate in the directory.
689689
for symPath in certificateFilePaths {
690690
// c_rehash only support pem files.
691-
let cert = try NIOSSLCertificate(file: symPath, format: .pem)
692-
try addCACertificateNameToList(context: context, certificate: cert)
691+
if let cert = try NIOSSLCertificate.fromPEMFile(symPath).first {
692+
try addCACertificateNameToList(context: context, certificate: cert)
693+
}
693694
}
694695
}
695696
}

Tests/NIOSSLTests/SSLCertificateTest.swift

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -220,10 +220,8 @@ class SSLCertificateTest: XCTestCase {
220220

221221
func testLoadingPemCertFromFile() throws {
222222
let (cert1, cert2) = try Self.withPemCertPath {
223-
(
224-
try NIOSSLCertificate(file: $0, format: .pem),
225-
try NIOSSLCertificate(file: $0, format: .pem)
226-
)
223+
let cert = try NIOSSLCertificate.fromPEMFile($0).first!
224+
return (cert, cert)
227225
}
228226

229227
XCTAssertEqual(cert1, cert2)
@@ -234,10 +232,8 @@ class SSLCertificateTest: XCTestCase {
234232

235233
func testLoadingDerCertFromFile() throws {
236234
let (cert1, cert2) = try Self.withDerCertPath {
237-
(
238-
try NIOSSLCertificate(file: $0, format: .der),
239-
try NIOSSLCertificate(file: $0, format: .der)
240-
)
235+
let cert = try NIOSSLCertificate.fromDERFile($0)
236+
return (cert, cert)
241237
}
242238

243239
XCTAssertEqual(cert1, cert2)
@@ -248,10 +244,10 @@ class SSLCertificateTest: XCTestCase {
248244

249245
func testDerAndPemAreIdentical() throws {
250246
let cert1 = try Self.withPemCertPath {
251-
try NIOSSLCertificate(file: $0, format: .pem)
247+
try NIOSSLCertificate.fromPEMFile($0).first!
252248
}
253249
let cert2 = try Self.withDerCertPath {
254-
try NIOSSLCertificate(file: $0, format: .der)
250+
try NIOSSLCertificate.fromDERFile($0)
255251
}
256252

257253
XCTAssertEqual(cert1, cert2)
@@ -334,7 +330,7 @@ class SSLCertificateTest: XCTestCase {
334330
_ = tempFile.withCString { unlink($0) }
335331
}
336332

337-
XCTAssertThrowsError(try NIOSSLCertificate(file: tempFile, format: .pem)) { error in
333+
XCTAssertThrowsError(try NIOSSLCertificate.fromPEMFile(tempFile)) { error in
338334
XCTAssertEqual(.failedToLoadCertificate, error as? NIOSSLError)
339335
}
340336
}
@@ -356,11 +352,12 @@ class SSLCertificateTest: XCTestCase {
356352
_ = tempFile.withCString { unlink($0) }
357353
}
358354

359-
XCTAssertThrowsError(try NIOSSLCertificate(file: tempFile, format: .der)) { error in
355+
XCTAssertThrowsError(try NIOSSLCertificate.fromDERFile(tempFile)) { error in
360356
XCTAssertEqual(.failedToLoadCertificate, error as? NIOSSLError)
361357
}
362358
}
363359

360+
@available(*, deprecated, message: "Deprecated to test deprecated functionality")
364361
func testLoadingNonexistentFileAsPem() throws {
365362
XCTAssertThrowsError(try NIOSSLCertificate(file: "/nonexistent/path", format: .pem)) { error in
366363
guard let error = error as? IOError else {
@@ -382,7 +379,7 @@ class SSLCertificateTest: XCTestCase {
382379
}
383380

384381
func testLoadingNonexistentFileAsDer() throws {
385-
XCTAssertThrowsError(try NIOSSLCertificate(file: "/nonexistent/path", format: .der)) { error in
382+
XCTAssertThrowsError(try NIOSSLCertificate.fromDERFile("/nonexistent/path")) { error in
386383
guard let error = error as? IOError else {
387384
return XCTFail("unexpected error \(error)")
388385
}

Tests/NIOSSLTests/TLSConfigurationTest.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -439,9 +439,9 @@ class TLSConfigurationTest: XCTestCase {
439439
func getRehashFilename(path: String, testName: String, numericExtension: Int) -> String {
440440
var cert: NIOSSLCertificate!
441441
if path.suffix(4) == ".pem" {
442-
XCTAssertNoThrow(cert = try NIOSSLCertificate(file: path, format: .pem))
442+
XCTAssertNoThrow(cert = try NIOSSLCertificate.fromPEMFile(path).first)
443443
} else {
444-
XCTAssertNoThrow(cert = try NIOSSLCertificate(file: path, format: .der))
444+
XCTAssertNoThrow(cert = try NIOSSLCertificate.fromDERFile(path))
445445
}
446446
// Create a rehash format filename to symlink the hard file above to.
447447
let originalSubjectName = cert.getSubjectNameHash()

0 commit comments

Comments
 (0)