Skip to content
ecdsa.com

Generate a CSR · Swift

How to generate a CSR for an EC P-256 key with SANs in Swift

Swift generates a CSR with Apple's swift-certificates package: CertificateSigningRequest takes the subject built with DistinguishedName, a P256.Signing.PrivateKey, and an ExtensionRequest attribute with SubjectAlternativeNames, and serializeAsPEM writes the request. It runs on Apple platforms and Linux; the run decodes the CSR with OpenSSL.

Tested with swift-certificates 1.21.0 (Swift 6.4.1, Linux, swift-crypto 4.5.2).

Package.swift — swift-certificates and swift-crypto
// swift-tools-version:6.0
import PackageDescription
let package = Package(
name: "csr-demo",
platforms: [.macOS(.v13)],
dependencies: [
.package(url: "https://github.com/apple/swift-certificates.git", exact: "1.21.0"),
.package(url: "https://github.com/apple/swift-crypto.git", exact: "4.5.2"),
],
targets: [
.executableTarget(name: "csr-demo", dependencies: [
.product(name: "X509", package: "swift-certificates"),
.product(name: "Crypto", package: "swift-crypto"),
]),
]
)
main.swift — P-256 key and CSR with two SANs
import Crypto
import Foundation
import X509
let key = P256.Signing.PrivateKey()
let subject = try DistinguishedName {
CountryName("US")
OrganizationName("Example Corp")
CommonName("api.example.com")
}
let extensions = try Certificate.Extensions {
SubjectAlternativeNames([.dnsName("api.example.com"), .dnsName("www.example.com")])
}
// Extensions go into the extensionRequest attribute of the CSR
let attributes = try CertificateSigningRequest.Attributes([.init(ExtensionRequest(extensions: extensions))])
let csr = try CertificateSigningRequest(
version: .v1,
subject: subject,
privateKey: Certificate.PrivateKey(key),
attributes: attributes,
signatureAlgorithm: .ecdsaWithSHA256
)
try csr.serializeAsPEM().pemString.write(toFile: "csr.pem", atomically: true, encoding: .utf8)
try key.pemRepresentation.write(toFile: "key.pem", atomically: true, encoding: .utf8) // PKCS#8

How it works

  1. P256.Signing.PrivateKey() creates the key; Certificate.PrivateKey(key) wraps it for swift-certificates.
  2. DistinguishedName { CountryName("US"); OrganizationName(…); CommonName(…) } builds the subject in DER order.
  3. Certificate.Extensions { SubjectAlternativeNames([.dnsName(…), …]) } goes into ExtensionRequest, which becomes a CSR attribute.
  4. serializeAsPEM().pemString is the CSR; key.pemRepresentation the PKCS#8 private key.

Gotchas

  • The extensions are not a parameter of CertificateSigningRequest itself: without the ExtensionRequest attribute, the CSR has no SANs.
  • signatureAlgorithm can be left out — swift-certificates then picks one that fits the key — but naming .ecdsaWithSHA256 keeps the CSR's algorithm explicit in the code.
  • On Apple platforms, Certificate.PrivateKey also wraps a SecureEnclave.P256.Signing.PrivateKey, so the CSR can be signed by a key that can never be exported.
  • pemRepresentation writes the private key unencrypted; store it with file permissions or in the Keychain, never next to the CSR you send out.

Related recipes