Skip to content
ecdsa.com

Generate a key pair · Swift

How to generate an ECDSA key pair in Swift

To generate an ECDSA key pair in Swift, create a P256.Signing.PrivateKey — CryptoKit's API, also on Linux through swift-crypto — and write its pemRepresentation and publicKey.pemRepresentation. Those are the standard PKCS#8 and SPKI PEM files OpenSSL and every other stack read; the recipe also creates the private key file owner-only.

Tested with swift-crypto 5.0.3 (Swift 6.4.1 on Ubuntu 24.04 (BoringSSL backend)) and OpenSSL 3.0.13 (openssl pkey cross-check).

Package.swift — swift-crypto for Linux (Apple platforms ship CryptoKit)
// swift-tools-version:6.2
import PackageDescription
let package = Package(
name: "howto",
platforms: [.macOS(.v15)],
dependencies: [
.package(url: "https://github.com/apple/swift-crypto.git", exact: "5.0.3"),
],
targets: [
.executableTarget(name: "generate", dependencies: [.product(name: "Crypto", package: "swift-crypto")]),
]
)
Sources/generate/main.swift — a P-256 key pair written as PEM files
import Crypto
import Foundation
let privateKey = P256.Signing.PrivateKey() // random, from the system CSPRNG
// owner-only file: create it with mode 0600 before any key material is written
guard FileManager.default.createFile(atPath: "key.pem", contents: nil, attributes: [.posixPermissions: 0o600]) else {
fatalError("cannot create key.pem")
}
try Data((privateKey.pemRepresentation + "\n").utf8).write(to: URL(fileURLWithPath: "key.pem")) // "BEGIN PRIVATE KEY"
let publicPEM = privateKey.publicKey.pemRepresentation + "\n" // "BEGIN PUBLIC KEY" (SPKI)
try Data(publicPEM.utf8).write(to: URL(fileURLWithPath: "pub.pem"))
print(publicPEM, terminator: "")

How it works

  1. P256.Signing.PrivateKey() draws a new key from the system's secure random source; P384 and P521 have the same API.
  2. pemRepresentation writes PKCS#8 (BEGIN PRIVATE KEY) and publicKey.pemRepresentation writes SPKI (BEGIN PUBLIC KEY); the run checks with openssl pkey that they belong together and name P-256.
  3. FileManager.createFile(..., attributes: [.posixPermissions: 0o600]) creates the empty key file owner-only, and the non-atomic write(to:) that follows keeps that mode.
  4. derRepresentation and x963Representation give the same keys as DER or as raw SEC1 bytes, for APIs that do not take PEM.

Gotchas

  • write(to:options: .atomic) writes a temporary file and renames it over the target — the result has the default mode (0644 in the run's probe), not the 0600 set by createFile. Write private keys non-atomically into a file created with the right permissions.
  • pemRepresentation has no trailing newline. Append one, or concatenating PEM files and comparing them with other tools' output fails.
  • secp256k1 is not part of CryptoKit or swift-crypto; Bitcoin and Ethereum keys need a dedicated library (for example one built on libsecp256k1).
  • On Apple devices, consider SecureEnclave.P256.Signing.PrivateKey(): the key never leaves the hardware, so there is no PEM to write — only an opaque dataRepresentation for the same device.

Related recipes