// swift-tools-version:6.2import PackageDescription let package = Package( name: "howto", platforms: [.macOS(.v15)], dependencies: [ .package(url: "https://github.com/apple/swift-certificates.git", exact: "1.21.0"), .package(url: "https://github.com/apple/swift-crypto.git", exact: "5.0.3"), ], targets: [ .executableTarget( name: "convert", dependencies: [ .product(name: "X509", package: "swift-certificates"), .product(name: "Crypto", package: "swift-crypto"), ] ), ]) import Cryptoimport Foundationimport SwiftASN1import X509 // Any PEM block: PEMDocument holds the label (discriminator) and the DER byteslet certPEM = try String(contentsOfFile: "cert.pem", encoding: .utf8)let document = try PEMDocument(pemString: certPEM)try Data(document.derBytes).write(to: URL(fileURLWithPath: "cert.der"))let certAgain = PEMDocument(type: "CERTIFICATE", derBytes: document.derBytes).pemString // Parsing the DER proves it is a certificatelet certificate = try Certificate(derEncoded: document.derBytes) // Private key: CryptoKit types read and write PKCS#8 in both encodingslet keyPEM = try String(contentsOfFile: "key.pem", encoding: .utf8)let key = try P256.Signing.PrivateKey(pemRepresentation: keyPEM)let keyDER = key.derRepresentationlet keyAgain = try P256.Signing.PrivateKey(derRepresentation: keyDER).pemRepresentation print("\(document.discriminator) DER: \(document.derBytes.count) bytes, sha256 \(SHA256.hash(data: document.derBytes).map { String(format: "%02x", $0) }.joined())")print("cert PEM again identical: \(certAgain + "\n" == certPEM), subject \(certificate.subject)")print("key DER: \(keyDER.count) bytes, sha256 \(SHA256.hash(data: keyDER).map { String(format: "%02x", $0) }.joined())")print("key PEM again identical: \(keyAgain + "\n" == keyPEM)") How it works
PEMDocument(pemString:)parses one block;discriminatoris the label (CERTIFICATE) andderBytesthe DER (563 bytes here).PEMDocument(type:derBytes:).pemStringbuilds the block again;Certificate(derEncoded:)proves the bytes are a certificate.P256.Signing.PrivateKey(pemRepresentation:)reads a PKCS#8 PEM key;derRepresentationis its PKCS#8 DER (138 bytes) andPrivateKey(derRepresentation:)reads it back.- The run checks the certificate's DER with OpenSSL (
openssl x509 -inform DER).
Gotchas
pemStringandpemRepresentationend without a trailing newline; the run appends\nto compare with files written by OpenSSL.- The CryptoKit key types are per curve (
P256,P384,Curve25519…): you need to know the algorithm before you can read a key's DER, because nothing in the call says it. PEMDocument(pemString:)takes one block; usePEMDocument.parseMultiple(pemString:)for a bundle.
Related recipes
- How to convert a certificate and a private key between PEM and DER in Kotlin
- How to convert a certificate and a private key between PEM and DER in Rust
- How to convert a certificate and a private key between PEM and DER with OpenSSL
- How to export a public key as PEM in Swift
- How to decode an X.509 certificate in Swift