Skip to content

Commit 2f79730

Browse files
authored
Add bridge between Certificate and Security.SecCertificate (#182)
* Add bridge between Certificate and Security.SecCertificate * Add documentation for explaining the bridge between `Certificate` and `Security.SecCertificate`. Add `make` prefix to `SecCertificate` factory method.
1 parent 6bd8bee commit 2f79730

File tree

3 files changed

+55
-0
lines changed

3 files changed

+55
-0
lines changed

Sources/X509/Certificate.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ import SwiftASN1
5858
/// across the rest of the data. Allowing users to change this data makes it easy to accidentally modify
5959
/// a ``Certificate`` in one part of your code and not realise that the signature has inevitably
6060
/// been invalidated.
61+
///
62+
/// ### Creating Certificates from SecCertificate and vice versa
63+
///
64+
/// An instance of ``Certificate`` can be created from ``Security/SecCertificate`` (from the ``Security`` framework) with ``Certificate/init(_:)``.
65+
/// The opposite, that is, creating an instance of ``Security/SecCertificate`` from ``Certificate``, can be achieved with ``Security/SecCertificate/makeWithCertificate(_:)``.
6166
public struct Certificate {
6267
/// The X.509 version of this certificate.
6368
///
@@ -295,3 +300,30 @@ extension Certificate: PEMRepresentable {
295300
@inlinable
296301
public static var defaultPEMDiscriminator: String { "CERTIFICATE" }
297302
}
303+
304+
#if canImport(Security)
305+
import Security
306+
extension Certificate {
307+
/// Creates an instance of ``Certificate`` from ``Security/SecCertificate``.
308+
/// To create an instance of ``Security/SecCertificate``, use ``Security/SecCertificate/makeWithCertificate(_:)`` instead.
309+
/// - Parameter certificate: The `SecCertificate` instance used to initialize this new `Certificate` instance
310+
public init(_ certificate: SecCertificate) throws {
311+
try self.init(derEncoded: Array(SecCertificateCopyData(certificate) as Data))
312+
}
313+
}
314+
315+
extension SecCertificate {
316+
/// Creates an instance of ``Security/SecCertificate`` from ``Certificate``.
317+
/// To create an instance of ``Certificate``, use ``Certificate/init(_:)`` instead.
318+
/// - Parameter certificate: The `Certificate` instance used to initialize this new `SecCertificate` instance
319+
/// - Returns: A new `SecCertificate` instance based on the provided `Certificate` instance
320+
public static func makeWithCertificate(_ certificate: Certificate) throws -> SecCertificate {
321+
var coder = DER.Serializer()
322+
try certificate.serialize(into: &coder)
323+
324+
let derData = Data(coder.serializedBytes)
325+
326+
return SecCertificateCreateWithData(nil, derData as CFData)!
327+
}
328+
}
329+
#endif

Sources/X509/Docs.docc/Creating Certificates.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,8 @@ try serializer.serialize(certificate)
178178
let derEncodedCertificate = serializer.serializedBytes
179179
let derEncodedPrivateKey = swiftCryptoKey.derRepresentation
180180
```
181+
182+
### Creating Certificates from SecCertificate and vice versa
183+
184+
An instance of ``Certificate`` can be created from ``Security/SecCertificate`` (from the ``Security`` framework) with ``Certificate/init(_:)``.
185+
The opposite, that is, creating an instance of ``Security/SecCertificate`` from ``Certificate``, can be achieved with ``Security/SecCertificate/makeWithCertificate(_:)``.

Tests/X509Tests/CertificateDERTests.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ import SwiftASN1
1919
import Crypto
2020
import _CryptoExtras
2121

22+
#if canImport(Security)
23+
import Security
24+
#endif
25+
2226
final class CertificateDERTests: XCTestCase {
2327
static let base64EncodedSampleCert = """
2428
MIIDsjCCAzigAwIBAgIQDKuq0c7E6XzCZliB0CE49zAKBggqhkjOPQQDAzBhMQsw
@@ -164,6 +168,20 @@ final class CertificateDERTests: XCTestCase {
164168
""",
165169
]
166170

171+
#if canImport(Security)
172+
func testSecCertificateBridge() throws {
173+
let certificateData = Data(base64Encoded: Self.base64EncodedSampleCert, options: .ignoreUnknownCharacters)!
174+
let binary = Array(certificateData)
175+
176+
let cert = try Certificate(derEncoded: binary)
177+
178+
let certConvertedToSecCert = try SecCertificate.makeWithCertificate(cert)
179+
let secCertConvertedToCert = try Certificate(certConvertedToSecCert)
180+
181+
XCTAssertEqual(cert, secCertConvertedToCert)
182+
}
183+
#endif
184+
167185
func testSimpleDecode() throws {
168186
let binary = Array(Data(base64Encoded: Self.base64EncodedSampleCert, options: .ignoreUnknownCharacters)!)
169187
let cert = try Certificate(derEncoded: binary)

0 commit comments

Comments
 (0)