// swift-tools-version:6.0import 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"), ]), ])import Cryptoimport Foundationimport 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 CSRlet 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#8How it works
P256.Signing.PrivateKey()creates the key;Certificate.PrivateKey(key)wraps it for swift-certificates.DistinguishedName { CountryName("US"); OrganizationName(…); CommonName(…) }builds the subject in DER order.Certificate.Extensions { SubjectAlternativeNames([.dnsName(…), …]) }goes intoExtensionRequest, which becomes a CSR attribute.serializeAsPEM().pemStringis the CSR;key.pemRepresentationthe PKCS#8 private key.
Gotchas
- The extensions are not a parameter of
CertificateSigningRequestitself: without theExtensionRequestattribute, the CSR has no SANs. signatureAlgorithmcan be left out — swift-certificates then picks one that fits the key — but naming.ecdsaWithSHA256keeps the CSR's algorithm explicit in the code.- On Apple platforms,
Certificate.PrivateKeyalso wraps aSecureEnclave.P256.Signing.PrivateKey, so the CSR can be signed by a key that can never be exported. pemRepresentationwrites the private key unencrypted; store it with file permissions or in the Keychain, never next to the CSR you send out.